@glomex/integration-analytics 1.1480.0 → 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 -11
  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 +19 -8
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @glomex/integration-analytics
2
2
 
3
- A unified analytics integration package for the [turbo player](https://www.npmjs.com/package/@glomex/integration-web-component). This package provides adapters for various analytics providers including NPAW/Youbora, with planned support for Nielsen, Comscore, and more.
3
+ A unified analytics integration package for the [turbo player](https://www.npmjs.com/package/@glomex/integration-web-component). This package provides adapters for various analytics providers including NPAW/Youbora, Comscore, Nielsen, and Sensic.
4
4
 
5
5
  ## Installation
6
6
 
@@ -21,7 +21,9 @@ import { connectToNpaw } from '@glomex/integration-analytics/npaw';
21
21
  const integration = document.querySelector('glomex-integration');
22
22
 
23
23
  // Simple setup for NPAW analytics (that also handles consent)
24
- const { npawPlugin, destroy } = connectToNpaw(integration, 'your-npaw-account-code');
24
+ const { npawPlugin, destroy } = connectToNpaw(integration, {
25
+ accountCode: 'your-npaw-account-code'
26
+ });
25
27
  ```
26
28
 
27
29
  #### Advanced NPAW Configuration
@@ -29,7 +31,8 @@ const { npawPlugin, destroy } = connectToNpaw(integration, 'your-npaw-account-co
29
31
  ```typescript
30
32
  import { connectToNpaw } from '@glomex/integration-analytics/npaw';
31
33
 
32
- const { npawPlugin, destroy } = connectToNpaw(integration, 'your-npaw-account-code', {
34
+ const { npawPlugin, destroy } = connectToNpaw(integration, {
35
+ accountCode: 'your-npaw-account-code',
33
36
  appName: 'my-app',
34
37
  appVersion: '1.0.0'
35
38
  });
@@ -55,22 +58,249 @@ const npawPlugin = new NpawPlugin('your-npaw-account-code');
55
58
  npawPlugin.registerAdapterFromClass(integration, NpawTurboPlayerAdapter);
56
59
  ```
57
60
 
58
- ## Planned Adapters
61
+ ### Comscore
62
+
63
+ [Comscore Streaming Analytics](https://www.comscore.com/) integration for measuring streaming video consumption.
64
+
65
+ ```typescript
66
+ import { connectToComscore } from '@glomex/integration-analytics/comscore';
67
+
68
+ // Get your integration element
69
+ const integration = document.querySelector('glomex-integration');
70
+
71
+ // Connect Comscore analytics
72
+ const disconnect = connectToComscore(integration, {
73
+ publisherId: 'your-publisher-id',
74
+ publisherName: 'Your Publisher Name'
75
+ });
76
+
77
+ // Clean up when done
78
+ disconnect();
79
+ ```
80
+
81
+ #### Comscore Configuration Options
82
+
83
+ | Option | Type | Required | Description |
84
+ |--------|------|----------|-------------|
85
+ | `publisherId` | `string` | Yes | Your Comscore publisher ID |
86
+ | `publisherName` | `string` | Yes | Your publisher name (e.g., "Joyn") |
87
+ | `debug` | `boolean` | No | Enable debug mode for implementation validation |
88
+ | `warnCallback` | `(error: Error) => void` | No | Custom callback for warning messages |
89
+ | `configureContentMetadata` | `(metadata) => metadata \| null` | No | Customize content metadata before tracking |
90
+
91
+ #### Advanced Comscore Configuration
92
+
93
+ ```typescript
94
+ import { connectToComscore } from '@glomex/integration-analytics/comscore';
95
+
96
+ const disconnect = connectToComscore(integration, {
97
+ publisherId: 'your-publisher-id',
98
+ publisherName: 'Your Publisher Name',
99
+ debug: true,
100
+ warnCallback: (error) => {
101
+ console.warn('Comscore warning:', error.message);
102
+ },
103
+ configureContentMetadata: (metadata) => {
104
+ // Customize metadata or return null to skip tracking
105
+ metadata.setGenreName('Sports');
106
+ return metadata;
107
+ }
108
+ });
109
+ ```
110
+
111
+ #### Comscore Consent Requirements
112
+
113
+ Comscore tracking only occurs when the user has given consent according to IAB TCF:
114
+ - **Purpose 1** consent (Store and/or access information on a device)
115
+ - **Vendor 77** consent (Comscore)
116
+
117
+ ### Nielsen
118
+
119
+ [Nielsen Digital Content Ratings](https://www.nielsen.com/solutions/audience-measurement/streaming-measurement/) integration for streaming measurement.
120
+
121
+ ```typescript
122
+ import { connectToNielsen } from '@glomex/integration-analytics/nielsen';
123
+
124
+ // Get your integration element
125
+ const integration = document.querySelector('glomex-integration');
126
+
127
+ // Connect Nielsen analytics
128
+ const disconnect = connectToNielsen(integration, {
129
+ appId: 'your-app-id',
130
+ country: 'de'
131
+ });
132
+
133
+ // Clean up when done
134
+ disconnect();
135
+ ```
136
+
137
+ #### Nielsen Configuration Options
138
+
139
+ | Option | Type | Required | Description |
140
+ |--------|------|----------|-------------|
141
+ | `appId` | `string` | Yes | Nielsen App ID (starts with 'P' for production, 'T' for test) |
142
+ | `country` | `string` | Yes | Two-letter country code for country-specific metadata mapping (e.g., 'de' for Germany) |
143
+ | `debug` | `boolean` | No | Enable Nielsen SDK debug mode |
144
+ | `warnCallback` | `(error: Error) => void` | No | Custom callback for warning messages |
145
+ | `configureContentMetadata` | `(metadata) => metadata \| null` | No | Extend/modify content metadata for tracking |
146
+ | `configureAdMetadata` | `(metadata) => metadata` | No | Extend/modify ad metadata for tracking |
147
+
148
+ #### Supported Countries
149
+
150
+ Nielsen uses country-specific metadata mappings:
151
+ - `'de'` - Germany (uses AGF-specific custom variables)
152
+ - Other country codes - Base metadata only (`assetid`, `type`, `program`, `title`, `length` for content; `assetid`, `type` for ads)
153
+
154
+ For Germany (`country: 'de'`), the following metadata is automatically built:
155
+ - **Content metadata**: `assetid`, `program`, `title`, `length`, `nol_c0` (part number), `nol_c2` (web only flag), `nol_c5` (page URL), `nol_c7` (video ID), `nol_c9` (video title), `nol_c10` (publisher), `nol_c12` (video type), `nol_c15` (format ID), `nol_c18` (livestream flag)
156
+ - **Ad metadata**: `assetid`, `type`, `length`, `title`, `nol_c1` (universal ad ID), `nol_c2`, `nol_c4` (ad form), `nol_c10`, `nol_c11` (ad ID), `nol_c12`, `nol_c17` (placement type), `nol_c18`
157
+
158
+ For unsupported country codes, only base metadata is provided. Use `configureContentMetadata` and `configureAdMetadata` to add country-specific fields manually:
159
+
160
+ ```typescript
161
+ const disconnect = connectToNielsen(integration, {
162
+ appId: 'your-app-id',
163
+ country: 'fr', // No built-in mapping for France
164
+ configureContentMetadata: (baseMetadata) => {
165
+ // baseMetadata contains: assetid, type, program, title, length
166
+ return {
167
+ ...baseMetadata,
168
+ program: integration.content?.show?.name ?? '',
169
+ // Add France-specific fields as needed
170
+ };
171
+ },
172
+ configureAdMetadata: (baseMetadata) => {
173
+ // baseMetadata contains: assetid, type
174
+ return {
175
+ ...baseMetadata,
176
+ title: integration.currentAd?.title ?? ''
177
+ };
178
+ }
179
+ });
180
+ ```
181
+
182
+ #### Advanced Nielsen Configuration
183
+
184
+ The `configureContentMetadata` and `configureAdMetadata` callbacks receive country-specific metadata that is automatically built from integration data. You can extend or modify this metadata:
185
+
186
+ ```typescript
187
+ import { connectToNielsen } from '@glomex/integration-analytics/nielsen';
188
+
189
+ const disconnect = connectToNielsen(integration, {
190
+ appId: 'your-app-id',
191
+ country: 'de',
192
+ debug: true,
193
+ warnCallback: (error) => {
194
+ console.warn('Nielsen warning:', error.message);
195
+ },
196
+ configureContentMetadata: (metadata) => {
197
+ // metadata contains country-specific fields (e.g., AGF fields for 'de')
198
+ // Return null to skip tracking this content
199
+ if (!integration.content) return null;
200
+
201
+ return {
202
+ ...metadata,
203
+ // Add custom fields or override existing ones
204
+ subbrand: 'abc' // VCID provided by Nielsen
205
+ };
206
+ },
207
+ configureAdMetadata: (metadata) => {
208
+ // metadata contains country-specific ad fields
209
+ return {
210
+ ...metadata,
211
+ subbrand: 'abc'
212
+ };
213
+ }
214
+ });
215
+ ```
216
+
217
+ #### Nielsen Consent
218
+
219
+ Nielsen does not require consent checks and uses a synchronous stub pattern. Events are queued until the SDK loads from CDN.
220
+
221
+ ### Sensic (GfK)
222
+
223
+ [Sensic](https://sensic.net/) (formerly GfK) integration for streaming measurement in Germany and Austria.
224
+
225
+ ```typescript
226
+ import { connectToSensic } from '@glomex/integration-analytics/sensic';
227
+
228
+ // Get your integration element
229
+ const integration = document.querySelector('glomex-integration');
230
+
231
+ // Connect Sensic analytics
232
+ const disconnect = connectToSensic(integration, {
233
+ media: 'your-media-id',
234
+ platform: 'web',
235
+ country: 'de'
236
+ });
237
+
238
+ // Clean up when done
239
+ disconnect();
240
+ ```
241
+
242
+ #### Sensic Configuration Options
243
+
244
+ | Option | Type | Required | Description |
245
+ |--------|------|----------|-------------|
246
+ | `media` | `string` | Yes | Sensic media identifier |
247
+ | `platform` | `'web' \| 'tv'` | Yes | Platform type |
248
+ | `country` | `string` | Yes | Two-letter country code (e.g., `'de'`, `'at'`) |
249
+ | `warnCallback` | `(error: Error) => void` | No | Custom callback for warning messages |
250
+ | `buildCustomParams` | `(customParams) => Record<string, string> \| null` | No | Extend/modify custom parameters for tracking |
251
+ | `buildStreamId` | `(streamId) => string` | No | Extend/modify stream ID for tracking |
252
+
253
+ #### Advanced Sensic Configuration
254
+
255
+ The `buildCustomParams` and `buildStreamId` callbacks receive country-specific values as input and are called on each stream start (VoD content, live content, and linear ads). Use `integration.content` for content data or `integration.currentAd` for ad data:
256
+
257
+ ```typescript
258
+ import { connectToSensic } from '@glomex/integration-analytics/sensic';
259
+
260
+ const disconnect = connectToSensic(integration, {
261
+ media: 'your-media-id',
262
+ platform: 'web',
263
+ country: 'at',
264
+ warnCallback: (error) => {
265
+ console.warn('Sensic warning:', error.message);
266
+ },
267
+ buildCustomParams: (customParams) => {
268
+ // customParams contains country-specific params (e.g., AT params for Austria)
269
+ // Return null to skip tracking this stream
270
+ return {
271
+ ...customParams,
272
+ genre: integration.content?.genre ?? '',
273
+ showId: integration.content?.show?.id ?? ''
274
+ };
275
+ },
276
+ buildStreamId: (streamId) => {
277
+ // streamId contains country-specific stream ID
278
+ return streamId || integration.content?.id ?? '';
279
+ }
280
+ });
281
+ ```
282
+
283
+ #### Sensic Consent Requirements
284
+
285
+ Sensic tracking only occurs when the user has given consent according to IAB TCF:
286
+ - **Vendor 758** consent (Sensic)
287
+
288
+ #### Supported Countries
59
289
 
60
- The following adapters are planned for future releases:
290
+ Sensic loads country-specific measurement scripts:
291
+ - `'de'` - Germany (`https://de-config.sensic.net/s2s-web.js`)
292
+ - `'at'` - Austria (`https://at-config.sensic.net/s2s-web.js`)
61
293
 
62
- - [Nielsen](https://www.nielsen.com/solutions/audience-measurement/streaming-measurement/) - `@glomex/integration-analytics/nielsen`
63
- - [Comscore](https://www.comscore.com/) - `@glomex/integration-analytics/comscore`
64
- - [GfK Sensic](https://sensic.net/) - `@glomex/integration-analytics/sensic`
65
- - [Datazoom](https://www.datazoom.io/) - `@glomex/integration-analytics/datazoom`
294
+ For TV platforms, the CTV variant is loaded automatically (e.g., `https://de-config.sensic.net/ctv/s2s-web.js`).
66
295
 
67
296
  ## Features
68
297
 
69
298
  - 🎯 **Easy Integration**: Simple setup with turbo player
70
- - 📊 **Multiple Providers**: Support for various analytics providers
299
+ - 📊 **Multiple Providers**: Support for NPAW, Comscore, Nielsen, and Sensic
71
300
  - 🔧 **Smart Metadata Mapping**: Automatically maps player content metadata to each analytics provider's format, with full support for custom overrides
72
301
  - 📱 **Cross-Platform**: Works with web, mobile web, and embedded players
73
- - 🔒 **Consent Handling**: Built-in consent management support
302
+ - 🔒 **Consent Handling**: Built-in consent management support with IAB TCF compliance
303
+ - 🎬 **Comprehensive Tracking**: Tracks content and linear ad playback (preroll, midroll, postroll)
74
304
 
75
305
  ## License
76
306
 
@@ -0,0 +1,56 @@
1
+ import { type IntegrationElement } from '@glomex/integration-web-component';
2
+ /**
3
+ * Base class for analytics event mappers that handles:
4
+ * - Event subscription/unsubscription
5
+ * - Event queuing until SDK is ready
6
+ * - Common state management (stopped, inAd, contentStarted, hasContentImpression)
7
+ */
8
+ export declare abstract class BaseEventMapper {
9
+ #private;
10
+ protected readonly integration: IntegrationElement;
11
+ protected readonly onWarn: (error: Error) => void;
12
+ constructor(integration: IntegrationElement, onWarn: (error: Error) => void);
13
+ /** Whether the event mapper has been stopped/destroyed */
14
+ protected get isStopped(): boolean;
15
+ /** Whether we are currently in an ad */
16
+ protected get isInAd(): boolean;
17
+ /** Whether onContentStart has been called */
18
+ protected get contentStarted(): boolean;
19
+ /**
20
+ * Mark SDK as ready and flush any queued events.
21
+ */
22
+ protected markSdkReady(): void;
23
+ protected onContentStart(): void;
24
+ protected onContentImpression(): void;
25
+ protected onContentPlay(): void;
26
+ protected onContentPause(): void;
27
+ protected onContentSeeking(): void;
28
+ protected onContentSeeked(): void;
29
+ protected onContentBufferingStart(): void;
30
+ protected onContentBufferingEnd(): void;
31
+ protected onContentEnded(): void;
32
+ protected onContentTimeUpdate(): void;
33
+ protected onVolumeChange(): void;
34
+ protected onPresentationModeChange(): void;
35
+ protected onAdImpression(): void;
36
+ protected onAdPaused(): void;
37
+ protected onAdResumed(): void;
38
+ protected onAdBufferingStart(): void;
39
+ protected onAdBufferingEnd(): void;
40
+ protected onAdEnd(): void;
41
+ protected onAdTimeUpdate(): void;
42
+ /**
43
+ * Override to perform SDK-specific cleanup (e.g., send end/stop signal).
44
+ * Called once when destroy() is invoked.
45
+ */
46
+ protected onDestroy(): void;
47
+ /**
48
+ * Tears down the event mapper:
49
+ * 1. Marks as stopped (prevents further event processing)
50
+ * 2. Calls onDestroy() for SDK-specific cleanup
51
+ * 3. Removes all event listeners
52
+ *
53
+ * Safe to call multiple times - subsequent calls are no-ops.
54
+ */
55
+ destroy(): void;
56
+ }
@@ -0,0 +1,313 @@
1
+ import { IntegrationEvent } from '@glomex/integration-web-component';
2
+ /** Ad break types that are tracked (linear ads) */
3
+ const LINEAR_AD_BREAKS = ['preroll', 'midroll', 'postroll'];
4
+ /**
5
+ * All playback events that analytics adapters may track
6
+ */
7
+ const PLAYBACK_EVENTS = [
8
+ IntegrationEvent.CONTENT_START,
9
+ IntegrationEvent.CONTENT_IMPRESSION,
10
+ IntegrationEvent.CONTENT_PLAY,
11
+ IntegrationEvent.CONTENT_PAUSE,
12
+ IntegrationEvent.CONTENT_SEEKING,
13
+ IntegrationEvent.CONTENT_SEEKED,
14
+ IntegrationEvent.CONTENT_BUFFERING_START,
15
+ IntegrationEvent.CONTENT_BUFFERING_END,
16
+ IntegrationEvent.CONTENT_ENDED,
17
+ IntegrationEvent.CONTENT_STOP,
18
+ IntegrationEvent.CONTENT_TIME_UPDATE,
19
+ IntegrationEvent.CONTENT_VOLUME_CHANGE,
20
+ IntegrationEvent.PLAYER_SET_PRESENTATION_MODE,
21
+ IntegrationEvent.AD_IMPRESSION,
22
+ IntegrationEvent.AD_PAUSED,
23
+ IntegrationEvent.AD_RESUMED,
24
+ IntegrationEvent.AD_BUFFERING_START,
25
+ IntegrationEvent.AD_BUFFERING_END,
26
+ IntegrationEvent.AD_COMPLETE,
27
+ IntegrationEvent.AD_SKIPPED,
28
+ IntegrationEvent.AD_TIME_UPDATE,
29
+ IntegrationEvent.AD_ERROR
30
+ ];
31
+ /**
32
+ * Base class for analytics event mappers that handles:
33
+ * - Event subscription/unsubscription
34
+ * - Event queuing until SDK is ready
35
+ * - Common state management (stopped, inAd, contentStarted, hasContentImpression)
36
+ */
37
+ export class BaseEventMapper {
38
+ integration;
39
+ onWarn;
40
+ #stopped = false;
41
+ #inAd = false;
42
+ #contentStarted = false;
43
+ #hasContentImpression = false;
44
+ #eventHandler;
45
+ #eventQueue = [];
46
+ #sdkReady = false;
47
+ constructor(integration, onWarn) {
48
+ this.integration = integration;
49
+ this.onWarn = onWarn;
50
+ this.#eventHandler = this.#handleEvent.bind(this);
51
+ for (const eventType of PLAYBACK_EVENTS) {
52
+ this.integration.addEventListener(eventType, this.#eventHandler);
53
+ }
54
+ }
55
+ /** Whether the event mapper has been stopped/destroyed */
56
+ get isStopped() {
57
+ return this.#stopped;
58
+ }
59
+ /** Whether we are currently in an ad */
60
+ get isInAd() {
61
+ return this.#inAd;
62
+ }
63
+ /** Whether onContentStart has been called */
64
+ get contentStarted() {
65
+ return this.#contentStarted;
66
+ }
67
+ /**
68
+ * Mark SDK as ready and flush any queued events.
69
+ */
70
+ markSdkReady() {
71
+ this.#sdkReady = true;
72
+ for (const fn of this.#eventQueue) {
73
+ fn();
74
+ }
75
+ this.#eventQueue = [];
76
+ }
77
+ #enqueue(fn) {
78
+ if (this.#sdkReady) {
79
+ fn();
80
+ }
81
+ else {
82
+ this.#eventQueue.push(fn);
83
+ }
84
+ }
85
+ #handleEvent(event) {
86
+ if (this.#stopped)
87
+ return;
88
+ const handler = this.#getHandler(event.type);
89
+ if (!handler)
90
+ return;
91
+ this.#enqueue(() => {
92
+ try {
93
+ handler();
94
+ }
95
+ catch (error) {
96
+ this.onWarn(error instanceof Error ? error : new Error(String(error)));
97
+ }
98
+ });
99
+ }
100
+ #getHandler(type) {
101
+ switch (type) {
102
+ case IntegrationEvent.CONTENT_START:
103
+ return () => {
104
+ this.#contentStarted = true;
105
+ this.onContentStart();
106
+ };
107
+ case IntegrationEvent.CONTENT_IMPRESSION:
108
+ return () => {
109
+ this.#hasContentImpression = true;
110
+ this.onContentImpression();
111
+ };
112
+ case IntegrationEvent.CONTENT_PLAY:
113
+ return () => {
114
+ // plays before impression are not interesting
115
+ if (!this.#hasContentImpression)
116
+ return;
117
+ // If ad didn't end properly, end it now
118
+ if (this.#inAd) {
119
+ this.onAdEnd();
120
+ this.#inAd = false;
121
+ }
122
+ this.onContentPlay();
123
+ };
124
+ case IntegrationEvent.CONTENT_PAUSE:
125
+ return () => {
126
+ if (!this.#hasContentImpression)
127
+ return;
128
+ this.onContentPause();
129
+ };
130
+ case IntegrationEvent.CONTENT_SEEKING:
131
+ return () => {
132
+ if (!this.#hasContentImpression)
133
+ return;
134
+ this.onContentSeeking();
135
+ };
136
+ case IntegrationEvent.CONTENT_SEEKED:
137
+ return () => {
138
+ if (!this.#hasContentImpression)
139
+ return;
140
+ this.onContentSeeked();
141
+ };
142
+ case IntegrationEvent.CONTENT_BUFFERING_START:
143
+ return () => {
144
+ if (!this.#hasContentImpression)
145
+ return;
146
+ this.onContentBufferingStart();
147
+ };
148
+ case IntegrationEvent.CONTENT_BUFFERING_END:
149
+ return () => {
150
+ if (!this.#hasContentImpression)
151
+ return;
152
+ this.onContentBufferingEnd();
153
+ };
154
+ case IntegrationEvent.CONTENT_ENDED:
155
+ return () => {
156
+ if (!this.#hasContentImpression)
157
+ return;
158
+ this.onContentEnded();
159
+ };
160
+ case IntegrationEvent.CONTENT_STOP:
161
+ return () => this.destroy();
162
+ case IntegrationEvent.CONTENT_TIME_UPDATE:
163
+ return () => {
164
+ if (!this.#hasContentImpression)
165
+ return;
166
+ this.onContentTimeUpdate();
167
+ };
168
+ case IntegrationEvent.CONTENT_VOLUME_CHANGE:
169
+ return () => this.onVolumeChange();
170
+ case IntegrationEvent.PLAYER_SET_PRESENTATION_MODE:
171
+ return () => this.onPresentationModeChange();
172
+ case IntegrationEvent.AD_IMPRESSION:
173
+ return () => {
174
+ // only allow linear ads into #inAd mode
175
+ if (!this.#isLinearAd(this.integration.currentAd))
176
+ return;
177
+ this.#inAd = true;
178
+ this.onAdImpression();
179
+ };
180
+ case IntegrationEvent.AD_PAUSED:
181
+ return () => {
182
+ if (!this.#inAd)
183
+ return;
184
+ this.onAdPaused();
185
+ };
186
+ case IntegrationEvent.AD_RESUMED:
187
+ return () => {
188
+ if (!this.#inAd)
189
+ return;
190
+ this.onAdResumed();
191
+ };
192
+ case IntegrationEvent.AD_BUFFERING_START:
193
+ return () => {
194
+ if (!this.#inAd)
195
+ return;
196
+ this.onAdBufferingStart();
197
+ };
198
+ case IntegrationEvent.AD_BUFFERING_END:
199
+ return () => {
200
+ if (!this.#inAd)
201
+ return;
202
+ this.onAdBufferingEnd();
203
+ };
204
+ case IntegrationEvent.AD_COMPLETE:
205
+ case IntegrationEvent.AD_SKIPPED:
206
+ case IntegrationEvent.AD_ERROR:
207
+ return () => {
208
+ if (!this.#inAd)
209
+ return;
210
+ this.onAdEnd();
211
+ this.#inAd = false;
212
+ };
213
+ case IntegrationEvent.AD_TIME_UPDATE:
214
+ return () => {
215
+ if (!this.#inAd)
216
+ return;
217
+ this.onAdTimeUpdate();
218
+ };
219
+ default:
220
+ return undefined;
221
+ }
222
+ }
223
+ // Default no-op implementations - subclasses override what they need
224
+ onContentStart() {
225
+ // no-op
226
+ }
227
+ onContentImpression() {
228
+ // no-op
229
+ }
230
+ onContentPlay() {
231
+ // no-op
232
+ }
233
+ onContentPause() {
234
+ // no-op
235
+ }
236
+ onContentSeeking() {
237
+ // no-op
238
+ }
239
+ onContentSeeked() {
240
+ // no-op
241
+ }
242
+ onContentBufferingStart() {
243
+ // no-op
244
+ }
245
+ onContentBufferingEnd() {
246
+ // no-op
247
+ }
248
+ onContentEnded() {
249
+ // no-op
250
+ }
251
+ onContentTimeUpdate() {
252
+ // no-op
253
+ }
254
+ onVolumeChange() {
255
+ // no-op
256
+ }
257
+ onPresentationModeChange() {
258
+ // no-op
259
+ }
260
+ onAdImpression() {
261
+ // no-op
262
+ }
263
+ onAdPaused() {
264
+ // no-op
265
+ }
266
+ onAdResumed() {
267
+ // no-op
268
+ }
269
+ onAdBufferingStart() {
270
+ // no-op
271
+ }
272
+ onAdBufferingEnd() {
273
+ // no-op
274
+ }
275
+ onAdEnd() {
276
+ // no-op
277
+ }
278
+ onAdTimeUpdate() {
279
+ // no-op
280
+ }
281
+ /**
282
+ * Override to perform SDK-specific cleanup (e.g., send end/stop signal).
283
+ * Called once when destroy() is invoked.
284
+ */
285
+ onDestroy() {
286
+ // no-op
287
+ }
288
+ /**
289
+ * Check if the ad is a linear ad (preroll, midroll, postroll).
290
+ */
291
+ #isLinearAd(ad) {
292
+ if (!ad)
293
+ return false;
294
+ return LINEAR_AD_BREAKS.includes(ad.breakName);
295
+ }
296
+ /**
297
+ * Tears down the event mapper:
298
+ * 1. Marks as stopped (prevents further event processing)
299
+ * 2. Calls onDestroy() for SDK-specific cleanup
300
+ * 3. Removes all event listeners
301
+ *
302
+ * Safe to call multiple times - subsequent calls are no-ops.
303
+ */
304
+ destroy() {
305
+ if (this.#stopped)
306
+ return;
307
+ this.#stopped = true;
308
+ this.onDestroy();
309
+ for (const eventType of PLAYBACK_EVENTS) {
310
+ this.integration.removeEventListener(eventType, this.#eventHandler);
311
+ }
312
+ }
313
+ }
@@ -0,0 +1,26 @@
1
+ import { type IntegrationElement } from '@glomex/integration-web-component';
2
+ import { BaseEventMapper } from '../base-event-mapper.js';
3
+ import type { Analytics, ContentMetadataInstance } from './comscore-types.js';
4
+ export declare class ComscoreEventMapper extends BaseEventMapper {
5
+ #private;
6
+ constructor(integration: IntegrationElement, sdkReadyPromise: Promise<{
7
+ analytics: Analytics;
8
+ contentMetadata: ContentMetadataInstance;
9
+ } | undefined>, onWarn: (error: Error) => void);
10
+ protected onContentStart(): void;
11
+ protected onContentImpression(): void;
12
+ protected onContentPlay(): void;
13
+ protected onContentPause(): void;
14
+ protected onContentSeeking(): void;
15
+ protected onContentSeeked(): void;
16
+ protected onContentBufferingStart(): void;
17
+ protected onContentBufferingEnd(): void;
18
+ protected onContentEnded(): void;
19
+ protected onAdImpression(): void;
20
+ protected onAdPaused(): void;
21
+ protected onAdResumed(): void;
22
+ protected onAdBufferingStart(): void;
23
+ protected onAdBufferingEnd(): void;
24
+ protected onAdEnd(): void;
25
+ protected onDestroy(): void;
26
+ }