@maestro_io/maestro-web-sdk 2.2.2 → 2.2.4

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,4 +1,3 @@
1
- import { BaseMetadata } from '../../../services/AnalyticsService/types';
2
1
  /**
3
2
  * Valid bet types as specified in the analytics spec
4
3
  */
@@ -7,19 +6,3 @@ export type BetType = 'straight' | 'parlay' | 'parlay_plus' | 'teaser' | 'boost'
7
6
  * Valid card types for bets module
8
7
  */
9
8
  export type BetCardType = '6-pack' | 'my bets > live' | 'my bets > settled' | 'my bets > upcoming' | 'hot-props' | 'promo' | 'disclaimer';
10
- /**
11
- * Metadata for bet panel view events
12
- */
13
- export interface BetPanelViewMetadata extends BaseMetadata {
14
- in_game_bets: number;
15
- total_bets: number;
16
- }
17
- /**
18
- * Metadata for bet card click events
19
- */
20
- export interface BetCardClickMetadata extends BaseMetadata {
21
- card: BetCardType;
22
- in_game_bets: number;
23
- total_bets: number;
24
- bet_type: BetType | null;
25
- }
@@ -138,10 +138,6 @@ export declare class BetsViewModel extends ViewModel {
138
138
  * Track card click event
139
139
  */
140
140
  trackCardClick(cardType: BetCardType, betType?: BetType | null): Promise<void>;
141
- /**
142
- * Get base metadata for analytics events
143
- */
144
- private getBaseMetadata;
145
141
  /**
146
142
  * Get user bet counts for analytics
147
143
  */
@@ -4,6 +4,7 @@ import { DrivePlay, Team } from '../interfaces/IStats';
4
4
  import { AthletesStatisticsItem } from '../view/components/AthletesStatistics/AthletesStatistics';
5
5
  import StatsService from '@/services/StatsService';
6
6
  import IMaestroEventDelegate from '@/interfaces/IMaestroEventDelegate';
7
+ import { TestUseCase } from '@/interfaces/IMaestroEvent';
7
8
  import AnalyticsService from '@/services/AnalyticsService/AnalyticsService';
8
9
  declare const STATS_ERROR_STATE: {
9
10
  readonly NONE: "none";
@@ -114,12 +115,13 @@ export declare class StatsViewModel extends ViewModel {
114
115
  currentGameState: Observable<string>;
115
116
  statsService: StatsService;
116
117
  analyticsService: AnalyticsService;
117
- private pollingInterval;
118
118
  private useMockData;
119
- private pollingTask;
120
119
  private lastResponseHash;
120
+ private currentPlayerTimeCode;
121
+ private timecodePollingTask;
122
+ private smartPollingTimeout;
123
+ private timecodePollingState;
121
124
  constructor(eventId: string, delegate: IMaestroEventDelegate, options?: {
122
- pollingInterval?: number;
123
125
  useMockData?: boolean;
124
126
  });
125
127
  /**
@@ -132,27 +134,66 @@ export declare class StatsViewModel extends ViewModel {
132
134
  */
133
135
  cleanup(): void;
134
136
  /**
135
- * Fetch new stats data immediately (one-time)
137
+ * Handle player timecode updates with enhanced logging and validation
138
+ *
139
+ * @param playerTimeCode - The timecode in milliseconds, or null for live
136
140
  */
137
- fetchNewData(): Promise<void>;
141
+ onUpdatePlayerTimeCode(playerTimeCode: number | null): void;
138
142
  /**
139
- * Fetch mock data for testing
143
+ * Implement smart polling strategy: immediate API call + delayed polling resumption
144
+ *
145
+ * @param syncUrl - The sync URL to use for the API call
140
146
  */
141
- private fetchMockData;
147
+ private performSmartPolling;
148
+ /**
149
+ * Enhanced error handling for timecode-related operations
150
+ *
151
+ * @param context - Description of where the error occurred
152
+ * @param error - The error that occurred
153
+ */
154
+ private handleTimecodeError;
155
+ /**
156
+ * Clear any pending smart polling timeout
157
+ */
158
+ private clearSmartPollingTimeout;
159
+ /**
160
+ * Reset timecode polling state
161
+ */
162
+ private resetTimecodePollingState;
163
+ /**
164
+ * Build sync URL for timecode-based stats fetching with enhanced logging
165
+ * Enhanced with comprehensive validation and fallback strategies
166
+ *
167
+ * @param timeCode - The timecode in milliseconds, or null for 'latest'
168
+ * @returns Complete sync URL for the fauxcast stats endpoint
169
+ * @throws Error if page config is not available or required fields are missing
170
+ */
171
+ private buildSyncUrl;
142
172
  /**
143
- * Refresh polling for bet data
173
+ * Format timecode for API consumption
174
+ * @param timeCode - Timecode in milliseconds or null
175
+ * @returns ISO date string, "pre.json" for pre-game, or "latest" for post-game
144
176
  */
145
- refreshPolling(): void;
177
+ private formatTimeCodeForAPI;
146
178
  /**
147
- * Poll with the correct method based on authentication status
179
+ * Start timecode-based polling with enhanced logging
180
+ *
181
+ * @param syncUrl - The sync URL to poll
148
182
  */
149
- private pollWithInterval;
183
+ private startTimecodePolling;
150
184
  /**
151
- * Handle errors that occur during polling
185
+ * Stop timecode-based polling with enhanced logging
152
186
  */
153
- private handlePollingError;
187
+ private stopTimecodePolling;
188
+ /**
189
+ * Fetch mock data for testing
190
+ */
191
+ private fetchMockData;
154
192
  /**
155
- * Process data received from polling
193
+ * Process polled data with hash comparison to avoid unnecessary re-renders
194
+ * Enhanced with comprehensive logging to show data changes
195
+ *
196
+ * @param data - The stats data received from the API
156
197
  */
157
198
  private processPolledData;
158
199
  private processData;
@@ -167,9 +208,9 @@ export declare class StatsViewModel extends ViewModel {
167
208
  private parseActiveAthletes;
168
209
  private getActiveAthletesTeamInfo;
169
210
  /**
170
- * Stop polling for bet data
211
+ * Simulate a use case using mock StatsApiResponse data
171
212
  */
172
- stopPolling(): void;
213
+ simulateUseCase<T = any>(_useCase: TestUseCase, testData?: T): void;
173
214
  }
174
215
  declare global {
175
216
  interface String {
@@ -1,31 +1,20 @@
1
1
  import InsightsPayloadBuilder from '../InsightsService/InsightsPayloadBuilder';
2
2
  import InsightsService from '../InsightsService/InsightsService';
3
- import { Metadata } from './types';
3
+ import { PageConfig } from '../PageService/types';
4
+ import { BaseMetadata } from './types';
4
5
  type KeyPlaysClickedAction = {
5
- panel_id: string;
6
- panel_name: string;
7
- panel_type: string;
6
+ type: 'espnKeyPlays';
8
7
  name: 'key_play_clicked';
9
8
  metadata: {
10
- league: string;
11
- event: string;
12
- airing: string;
13
- sport: string;
14
9
  action: string;
15
10
  clip: string;
16
11
  key_play: string;
17
12
  };
18
13
  };
19
14
  type BetsItemClickedAction = {
20
- panel_id: string;
21
- panel_name: string;
22
- panel_type: string;
15
+ type: 'espnBet';
23
16
  name: 'bets_item_clicked';
24
17
  metadata: {
25
- league: string;
26
- event: string;
27
- airing: string;
28
- sport: string;
29
18
  card: string;
30
19
  bet_type: string | null;
31
20
  in_game_bets: number;
@@ -33,25 +22,18 @@ type BetsItemClickedAction = {
33
22
  };
34
23
  };
35
24
  type StatsItemClickedAction = {
36
- panel_id: string;
37
- panel_name: string;
38
- panel_type: string;
25
+ type: 'espnStats';
39
26
  name: 'stats_item_clicked';
40
27
  metadata: {
41
- league: string;
42
- event: string;
43
- airing: string;
44
- sport: string;
45
28
  card: string[];
46
29
  game_state: string[];
47
30
  };
48
31
  };
49
32
  type PanelClickPayload = KeyPlaysClickedAction | BetsItemClickedAction | StatsItemClickedAction;
50
- type PanelViewPayload = {
33
+ type PanelData = {
51
34
  panel_type: string;
52
35
  panel_id: string;
53
36
  panel_name: string;
54
- metadata: Metadata;
55
37
  };
56
38
  declare class AnalyticsService {
57
39
  insightsService: InsightsService;
@@ -59,7 +41,9 @@ declare class AnalyticsService {
59
41
  constructor();
60
42
  private trackAction;
61
43
  private trackImpression;
62
- panelView({ panel_type, panel_id, panel_name, metadata, }: PanelViewPayload): Promise<void>;
63
- panelClick({ panel_type, panel_id, panel_name, metadata, name, }: PanelClickPayload): Promise<void>;
44
+ panelView(panelType: 'espnStats' | 'espnKeyPlays' | 'espnBet', metadata?: Record<string, any>): Promise<void>;
45
+ panelClick({ type, metadata, name }: PanelClickPayload): Promise<void>;
46
+ getBaseMetadataFromPageConfig(pageConfig: PageConfig | null): BaseMetadata;
47
+ getPanelDataFromPageConfig(type: 'espnBet' | 'espnStats' | 'espnKeyPlays', pageConfig: PageConfig | null): PanelData;
64
48
  }
65
49
  export default AnalyticsService;
@@ -1,5 +1,6 @@
1
1
  import { ICacheManager } from '@/external/ports/cacheManager';
2
2
  import InsightsService from './InsightsService';
3
+ import { PageConfig } from '../PageService/types';
3
4
  type EventData = {
4
5
  url?: string;
5
6
  metadata?: Record<string, unknown>;
@@ -18,6 +19,7 @@ export default class InsightsPayloadBuilder {
18
19
  serverOffsetTime: number;
19
20
  insightsService: InsightsService;
20
21
  cacheManager: ICacheManager;
22
+ pageConfig: PageConfig | null;
21
23
  constructor();
22
24
  getPayload(event: EventData): Promise<{
23
25
  /**
@@ -128,12 +130,12 @@ export default class InsightsPayloadBuilder {
128
130
  * @description Site domain.
129
131
  * @example site.settings.domain.url. 'maestro.tv/site_slug'
130
132
  */
131
- si_d: string;
133
+ si_d: string | undefined;
132
134
  /**
133
135
  * @description Site ID.
134
136
  * @example site._id
135
137
  */
136
- si_id: string;
138
+ si_id: string | undefined;
137
139
  /**
138
140
  * @description Site package ID.
139
141
  * @example `${site.package.id}-sapphire`
@@ -183,7 +185,7 @@ export default class InsightsPayloadBuilder {
183
185
  * @description Unique identifier for the insights ID. Stored in site's document.
184
186
  * @example site.settings.services.insights_id
185
187
  */
186
- i_id: string;
188
+ i_id: string | null | undefined;
187
189
  /**
188
190
  * @description Device created timestamp. You might wanna keep this fixed for the device you're in using a local storage or something related
189
191
  * @example new Date().getTime()
@@ -242,22 +244,22 @@ export default class InsightsPayloadBuilder {
242
244
  * @description MongoDB page document ID (e.g. 'pageId')
243
245
  * @example object._id
244
246
  */
245
- p_id: string;
247
+ p_id: string | undefined;
246
248
  /**
247
249
  * @description MongoDB page name (e.g. 'Home', 'Product Page')
248
250
  * @example object.data.name || object.seo.title
249
251
  */
250
- p_n: string;
252
+ p_n: string | undefined;
251
253
  /**
252
254
  * @description MongoDB page slug (e.g. 'home', 'product-page')
253
255
  * @example object.slug
254
256
  */
255
- p_s: string;
257
+ p_s: string | undefined;
256
258
  /**
257
259
  * @description MongoDB page type (e.g. 'landing', 'channel')
258
260
  * @example object.type
259
261
  */
260
- p_t: string;
262
+ p_t: string | undefined;
261
263
  }>;
262
264
  private getEventInsights;
263
265
  private getPageInsights;
@@ -0,0 +1,8 @@
1
+ import { Observable } from '../../helpers/Observable';
2
+ import { PageConfig } from './types';
3
+ export default class PageService {
4
+ configObservable: Observable<PageConfig | null>;
5
+ private client;
6
+ constructor();
7
+ fetchPageConfig(siteId: string, eventId: string): Promise<PageConfig | null>;
8
+ }
@@ -0,0 +1,2 @@
1
+ import PageService from './PageService';
2
+ export default PageService;
@@ -0,0 +1,2 @@
1
+ import { PageConfig } from './types';
2
+ export declare const MOCK_PAGE_CONFIG: PageConfig;
@@ -0,0 +1,53 @@
1
+ import ITheme from '@/models/ITheme';
2
+ interface PageConfigPanel {
3
+ _id: string;
4
+ slug: string;
5
+ panel_name?: string;
6
+ panel_type?: string;
7
+ icon_name?: string;
8
+ }
9
+ export type PageMetadata = {
10
+ /**
11
+ * Event ID or Airing ID
12
+ */
13
+ external_field_1?: string;
14
+ /**
15
+ * Sport guid
16
+ */
17
+ external_field_2?: string;
18
+ /**
19
+ * League guid
20
+ */
21
+ external_field_3?: string;
22
+ /**
23
+ * External field 1 identifier. Can be either 'airing' or 'event'.
24
+ */
25
+ external_field_4?: 'airing' | 'event';
26
+ /**
27
+ * A stringified JSON object containing additional metadata.
28
+ */
29
+ external_field_5?: string;
30
+ };
31
+ export interface PageConfig {
32
+ panels: PageConfigPanel[];
33
+ page: {
34
+ _id: string;
35
+ slug: string;
36
+ type: string;
37
+ name?: string;
38
+ metadata?: PageMetadata;
39
+ };
40
+ insights: {
41
+ _id: string | null;
42
+ };
43
+ site: {
44
+ _id: string;
45
+ domainUrl: string;
46
+ package: {
47
+ version: string;
48
+ id: string;
49
+ };
50
+ };
51
+ theme?: ITheme | null;
52
+ }
53
+ export {};
@@ -8,29 +8,30 @@ declare class StatsService {
8
8
  networkManager: IStatsNetworkManager;
9
9
  constructor(delegate: IMaestroEventDelegate);
10
10
  /**
11
- * Get stats for an event
11
+ * Get stats using a full sync URL (for timecode support)
12
12
  *
13
- * @param eventID - The ID of the event
14
- * @param gameState - Optional game state filter (e.g., 'pre', 'in', 'post')
13
+ * @param syncUrl - Complete sync URL (e.g., https://client.espn.com/fauxcast/stats/...)
15
14
  * @returns Promise with the stats data
16
15
  */
17
- getStats(eventID: string, gameState?: string): Promise<StatsApiResponse>;
16
+ getStatsByUrl(syncUrl: string): Promise<StatsApiResponse>;
18
17
  /**
19
- * Start polling for stats
18
+ * Start polling using a full sync URL (for timecode support)
20
19
  *
21
- * @param eventID - The ID of the event
22
- * @param gameState - Optional game state filter (e.g., 'pre', 'in', 'post')
20
+ * @param syncUrl - Complete sync URL (e.g., https://client.espn.com/fauxcast/stats/...)
23
21
  * @param interval - The polling interval in milliseconds
24
22
  * @param onData - Callback for successful data fetch
25
23
  * @param onError - Callback for errors
26
24
  * @returns Object with a stop function to stop polling
27
25
  */
28
- pollStats(eventID: string, gameState: string | undefined, interval: number, onData: (data: StatsApiResponse) => void, onError?: (error: Error) => void): {
26
+ pollStatsByUrl(syncUrl: string, interval: number, onData: (data: StatsApiResponse) => void, onError?: (error: Error) => void): {
29
27
  stop: () => void;
30
28
  };
31
29
  /**
32
- * Get the endpoint for stats
30
+ * Parse a sync URL into base URL and endpoint
31
+ *
32
+ * @param syncUrl - Complete sync URL
33
+ * @returns Object with baseUrl and endpoint
33
34
  */
34
- statsEndpoint(eventID: string): string;
35
+ private parseSyncUrl;
35
36
  }
36
37
  export default StatsService;
@@ -9,6 +9,7 @@ import StatsViewModel from '../modules/stats/view-model/StatsViewModel';
9
9
  import IMaestroKeyPlaysResponse from '../modules/key-plays/interfaces/IKeyPlaysResponse';
10
10
  import { MaestroPanelType } from '@/models/IPanel';
11
11
  import { IUserSettings } from '../interfaces/IUserSettings';
12
+ import { PanelManagerViewModel } from './PanelManagerViewModel';
12
13
  export declare class MaestroEventViewModel extends ViewModel implements IMaestroEvent {
13
14
  private _eventId;
14
15
  private _title;
@@ -19,8 +20,10 @@ export declare class MaestroEventViewModel extends ViewModel implements IMaestro
19
20
  keyPlaysViewModel: KeyPlaysViewModel;
20
21
  betsViewModel: BetsViewModel;
21
22
  statsViewModel: StatsViewModel;
23
+ panelManagerViewModel: PanelManagerViewModel;
22
24
  navigation: SpatialNavigation;
23
25
  constructor(eventId: string | undefined, delegate: IMaestroEventDelegate);
26
+ setActivePanel(panelType: MaestroPanelType): void;
24
27
  updatePlayerTimeCode(timestamp: number): void;
25
28
  getPlayerTimeCode(): number | null;
26
29
  deinit(): void;
@@ -1,12 +1,16 @@
1
1
  import { Observable } from '../helpers/Observable';
2
2
  import { MaestroPanelType } from '../models/IPanel';
3
+ export type PanelInfo = {
4
+ type: MaestroPanelType;
5
+ title: string;
6
+ };
3
7
  export declare class PanelManagerViewModel {
4
8
  private _panels;
5
9
  private _currentPanel;
6
- private _newlyFocusedPanel;
7
10
  private settingsManager;
8
11
  private settingsUnsubscribe;
9
12
  private readonly ALL_PANELS;
13
+ private getAvailablePanelsFromConfig;
10
14
  constructor();
11
15
  /**
12
16
  * Initialize panels based on current settings
@@ -17,9 +21,10 @@ export declare class PanelManagerViewModel {
17
21
  */
18
22
  private subscribeToSettingsChanges;
19
23
  /**
20
- * Filter panels based on user settings
24
+ * Filter panels based on user settings and page config
21
25
  */
22
- private filterPanelsBasedOnSettings;
26
+ private filterPanels;
27
+ private updateActivePanel;
23
28
  /**
24
29
  * Update panels when settings change
25
30
  */
@@ -28,13 +33,12 @@ export declare class PanelManagerViewModel {
28
33
  * Cleanup subscriptions
29
34
  */
30
35
  cleanup(): void;
31
- get panels(): MaestroPanelType[];
32
- get panelsObservable(): Observable<MaestroPanelType[]>;
33
- get currentPanel(): MaestroPanelType;
34
- set currentPanel(value: MaestroPanelType);
35
- get currentPanelObservable(): Observable<MaestroPanelType>;
36
- get newlyFocusedPanel(): MaestroPanelType | null;
37
- set newlyFocusedPanel(value: MaestroPanelType | null);
36
+ get panels(): PanelInfo[];
37
+ get panelsObservable(): Observable<PanelInfo[]>;
38
+ get currentPanel(): MaestroPanelType | null;
39
+ set currentPanel(value: MaestroPanelType | null);
40
+ get currentPanelObservable(): Observable<MaestroPanelType | null>;
41
+ setActivePanel(panelType: MaestroPanelType): void;
38
42
  getIndex(forPanelType: MaestroPanelType): number | null;
39
43
  getPanelType(atIndex: number): MaestroPanelType | null;
40
44
  get panelTypeToLeft(): MaestroPanelType | null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@maestro_io/maestro-web-sdk",
3
3
  "private": false,
4
- "version": "2.2.2",
4
+ "version": "2.2.4",
5
5
  "type": "commonjs",
6
6
  "license": "MIT",
7
7
  "main": "dist/maestro-web-sdk.umd.js",
@@ -1,23 +0,0 @@
1
- import { MaestroEventViewModel } from './MaestroEventViewModel';
2
- import { PanelManagerViewModel } from './PanelManagerViewModel';
3
- import { OverlayViewModel } from './OverlayViewModel';
4
- import IMaestroEventDelegate from '../interfaces/IMaestroEventDelegate';
5
- import KeyPlaysViewModel from '../modules/key-plays/view-model/KeyPlaysViewModel';
6
- /**
7
- * Aggregates all View Models
8
- */
9
- export declare class MaestroViewModels {
10
- readonly eventViewModel: MaestroEventViewModel;
11
- readonly panelManagerViewModel: PanelManagerViewModel;
12
- readonly keyPlaysViewModel: KeyPlaysViewModel;
13
- readonly overlayViewModel: OverlayViewModel;
14
- constructor(eventViewModel: MaestroEventViewModel);
15
- /**
16
- * Cleanup all view models
17
- */
18
- cleanup(): void;
19
- /**
20
- * Creates view models from an event ID
21
- */
22
- static fromEventId(eventId: string, delegate: IMaestroEventDelegate): Promise<MaestroViewModels | null>;
23
- }