@xhub-short/core 0.1.0-beta.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/dist/index.d.ts +1466 -0
- package/dist/index.js +2495 -0
- package/package.json +41 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1466 @@
|
|
|
1
|
+
import { VideoItem, IDataSource, IAnalytics, ILogger, ISessionStorage, SessionSnapshot, INetworkAdapter, IVideoLoader, IPosterLoader, VideoSource, IInteraction } from '@xhub-short/contracts';
|
|
2
|
+
export { SessionSnapshot } from '@xhub-short/contracts';
|
|
3
|
+
import { StoreApi } from 'zustand/vanilla';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Feed state for zustand store
|
|
7
|
+
*/
|
|
8
|
+
interface FeedState {
|
|
9
|
+
/** Normalized video data - Map for O(1) lookup */
|
|
10
|
+
itemsById: Map<string, VideoItem>;
|
|
11
|
+
/** Ordered list of video IDs for rendering */
|
|
12
|
+
displayOrder: string[];
|
|
13
|
+
/** Initial loading state */
|
|
14
|
+
loading: boolean;
|
|
15
|
+
/** Loading more (pagination) state */
|
|
16
|
+
loadingMore: boolean;
|
|
17
|
+
/** Error state */
|
|
18
|
+
error: FeedError | null;
|
|
19
|
+
/** Cursor for pagination */
|
|
20
|
+
cursor: string | null;
|
|
21
|
+
/** Whether more items can be loaded */
|
|
22
|
+
hasMore: boolean;
|
|
23
|
+
/** Whether data is stale and needs revalidation */
|
|
24
|
+
isStale: boolean;
|
|
25
|
+
/** Last successful fetch timestamp */
|
|
26
|
+
lastFetchTime: number | null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Feed error with additional context
|
|
30
|
+
*/
|
|
31
|
+
interface FeedError {
|
|
32
|
+
/** Error message */
|
|
33
|
+
message: string;
|
|
34
|
+
/** Error code for programmatic handling */
|
|
35
|
+
code: 'NETWORK_ERROR' | 'TIMEOUT' | 'SERVER_ERROR' | 'UNKNOWN';
|
|
36
|
+
/** Number of retry attempts made */
|
|
37
|
+
retryCount: number;
|
|
38
|
+
/** Whether error is recoverable */
|
|
39
|
+
recoverable: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* FeedManager configuration
|
|
43
|
+
*/
|
|
44
|
+
interface FeedConfig {
|
|
45
|
+
/** Page size for pagination (default: 10) */
|
|
46
|
+
pageSize?: number;
|
|
47
|
+
/** Maximum retry attempts (default: 3) */
|
|
48
|
+
maxRetries?: number;
|
|
49
|
+
/** Base delay for exponential backoff in ms (default: 1000) */
|
|
50
|
+
retryBaseDelay?: number;
|
|
51
|
+
/** Stale time in ms - data older than this will be revalidated (default: 5 minutes) */
|
|
52
|
+
staleTime?: number;
|
|
53
|
+
/** Whether to enable SWR pattern (default: true) */
|
|
54
|
+
enableSWR?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Maximum number of videos to keep in memory cache
|
|
57
|
+
* When exceeded, older videos will be evicted (LRU policy)
|
|
58
|
+
* @default 100
|
|
59
|
+
*/
|
|
60
|
+
maxCacheSize?: number;
|
|
61
|
+
/**
|
|
62
|
+
* Whether to enable automatic garbage collection
|
|
63
|
+
* @default true
|
|
64
|
+
*/
|
|
65
|
+
enableGC?: boolean;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Default feed configuration
|
|
69
|
+
*/
|
|
70
|
+
declare const DEFAULT_FEED_CONFIG: Required<FeedConfig>;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* FeedManager - Manages video feed data with zustand/vanilla store
|
|
74
|
+
*
|
|
75
|
+
* Features:
|
|
76
|
+
* - Data normalization (Map for O(1) lookup + ordered IDs)
|
|
77
|
+
* - Deduplication
|
|
78
|
+
* - Race condition protection
|
|
79
|
+
* - Request deduplication (prevents duplicate API calls)
|
|
80
|
+
* - Exponential backoff retry
|
|
81
|
+
* - SWR pattern support
|
|
82
|
+
* - Garbage Collection (LRU eviction when cache exceeds maxCacheSize)
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* const feedManager = new FeedManager(dataSource, { pageSize: 10 });
|
|
87
|
+
*
|
|
88
|
+
* // Subscribe to state changes
|
|
89
|
+
* feedManager.store.subscribe((state) => console.log(state));
|
|
90
|
+
*
|
|
91
|
+
* // Load initial data
|
|
92
|
+
* await feedManager.loadInitial();
|
|
93
|
+
*
|
|
94
|
+
* // Load more
|
|
95
|
+
* await feedManager.loadMore();
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
declare class FeedManager {
|
|
99
|
+
private readonly dataSource;
|
|
100
|
+
/** Zustand vanilla store - Single Source of Truth */
|
|
101
|
+
readonly store: StoreApi<FeedState>;
|
|
102
|
+
/** Resolved configuration */
|
|
103
|
+
private readonly config;
|
|
104
|
+
/** Abort controller for cancelling in-flight requests */
|
|
105
|
+
private abortController;
|
|
106
|
+
/**
|
|
107
|
+
* Request deduplication: Map of cursor → in-flight Promise
|
|
108
|
+
* Prevents duplicate API calls for the same cursor
|
|
109
|
+
*/
|
|
110
|
+
private inFlightRequests;
|
|
111
|
+
/**
|
|
112
|
+
* LRU tracking: Map of videoId → lastAccessTime
|
|
113
|
+
* Used for garbage collection
|
|
114
|
+
*/
|
|
115
|
+
private accessOrder;
|
|
116
|
+
constructor(dataSource: IDataSource, config?: FeedConfig);
|
|
117
|
+
/**
|
|
118
|
+
* Load initial feed data
|
|
119
|
+
*
|
|
120
|
+
* Implements SWR pattern:
|
|
121
|
+
* 1. If cached data exists, show it immediately
|
|
122
|
+
* 2. If data is stale (>staleTime), revalidate in background
|
|
123
|
+
* 3. If no cached data, fetch from network
|
|
124
|
+
*
|
|
125
|
+
* Request Deduplication:
|
|
126
|
+
* - If a request for the same cursor is already in-flight, returns the existing Promise
|
|
127
|
+
* - Prevents duplicate API calls from rapid UI interactions
|
|
128
|
+
*/
|
|
129
|
+
loadInitial(): Promise<void>;
|
|
130
|
+
/**
|
|
131
|
+
* Internal: Execute load initial logic
|
|
132
|
+
*/
|
|
133
|
+
private executeLoadInitial;
|
|
134
|
+
/**
|
|
135
|
+
* Load more videos (pagination)
|
|
136
|
+
*
|
|
137
|
+
* Features:
|
|
138
|
+
* - Race condition protection (prevents concurrent calls)
|
|
139
|
+
* - Request deduplication (prevents duplicate API calls for same cursor)
|
|
140
|
+
* - Exponential backoff retry on failure
|
|
141
|
+
*/
|
|
142
|
+
loadMore(): Promise<void>;
|
|
143
|
+
/**
|
|
144
|
+
* Internal: Execute load more logic
|
|
145
|
+
*/
|
|
146
|
+
private executeLoadMore;
|
|
147
|
+
/**
|
|
148
|
+
* Revalidate feed data in background (SWR pattern)
|
|
149
|
+
*
|
|
150
|
+
* Used when cached data is stale but still shown to user
|
|
151
|
+
*/
|
|
152
|
+
revalidate(): Promise<void>;
|
|
153
|
+
/**
|
|
154
|
+
* Get a video by ID
|
|
155
|
+
* Also updates LRU access time for garbage collection
|
|
156
|
+
*/
|
|
157
|
+
getVideo(id: string): VideoItem | undefined;
|
|
158
|
+
/**
|
|
159
|
+
* Get ordered list of videos
|
|
160
|
+
*/
|
|
161
|
+
getVideos(): VideoItem[];
|
|
162
|
+
/**
|
|
163
|
+
* Update a video in the feed (for optimistic updates)
|
|
164
|
+
*/
|
|
165
|
+
updateVideo(id: string, updates: Partial<VideoItem>): void;
|
|
166
|
+
/**
|
|
167
|
+
* Check if data is stale and needs revalidation
|
|
168
|
+
*/
|
|
169
|
+
isStale(): boolean;
|
|
170
|
+
/**
|
|
171
|
+
* Reset feed state
|
|
172
|
+
*/
|
|
173
|
+
reset(): void;
|
|
174
|
+
/**
|
|
175
|
+
* Cancel any pending requests
|
|
176
|
+
*/
|
|
177
|
+
cancelPendingRequests(): void;
|
|
178
|
+
/**
|
|
179
|
+
* Destroy the manager and cleanup
|
|
180
|
+
*/
|
|
181
|
+
destroy(): void;
|
|
182
|
+
/**
|
|
183
|
+
* Fetch with exponential backoff retry
|
|
184
|
+
*/
|
|
185
|
+
private fetchWithRetry;
|
|
186
|
+
/**
|
|
187
|
+
* Add videos with deduplication
|
|
188
|
+
* Triggers garbage collection if cache exceeds maxCacheSize
|
|
189
|
+
*/
|
|
190
|
+
private addVideos;
|
|
191
|
+
/**
|
|
192
|
+
* Merge videos (for SWR revalidation)
|
|
193
|
+
* Updates existing videos, adds new ones at the beginning
|
|
194
|
+
*/
|
|
195
|
+
private mergeVideos;
|
|
196
|
+
/**
|
|
197
|
+
* Handle and categorize errors
|
|
198
|
+
*/
|
|
199
|
+
private handleError;
|
|
200
|
+
/**
|
|
201
|
+
* Categorize error for better error handling
|
|
202
|
+
*/
|
|
203
|
+
private categorizeError;
|
|
204
|
+
/**
|
|
205
|
+
* Sleep utility for retry delays
|
|
206
|
+
*/
|
|
207
|
+
private sleep;
|
|
208
|
+
/**
|
|
209
|
+
* Run garbage collection using LRU (Least Recently Used) policy
|
|
210
|
+
*
|
|
211
|
+
* When cache size exceeds maxCacheSize:
|
|
212
|
+
* 1. Sort videos by last access time (oldest first)
|
|
213
|
+
* 2. Evict oldest videos until cache is within limit
|
|
214
|
+
* 3. Keep videos that are currently in viewport (most recent in displayOrder)
|
|
215
|
+
*
|
|
216
|
+
* @returns Number of evicted items
|
|
217
|
+
*/
|
|
218
|
+
private runGarbageCollection;
|
|
219
|
+
/**
|
|
220
|
+
* Manually trigger garbage collection
|
|
221
|
+
* Useful for debugging or when memory pressure is detected
|
|
222
|
+
*
|
|
223
|
+
* @returns Number of evicted items
|
|
224
|
+
*/
|
|
225
|
+
forceGarbageCollection(): number;
|
|
226
|
+
/**
|
|
227
|
+
* Get current cache statistics
|
|
228
|
+
* Useful for debugging and monitoring
|
|
229
|
+
*/
|
|
230
|
+
getCacheStats(): {
|
|
231
|
+
size: number;
|
|
232
|
+
maxSize: number;
|
|
233
|
+
utilizationPercent: number;
|
|
234
|
+
oldestAccessTime: number | null;
|
|
235
|
+
newestAccessTime: number | null;
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Player status enum - represents the current state of the player
|
|
241
|
+
*/
|
|
242
|
+
declare enum PlayerStatus {
|
|
243
|
+
/** Initial state - no video loaded */
|
|
244
|
+
IDLE = "idle",
|
|
245
|
+
/** Loading video resources */
|
|
246
|
+
LOADING = "loading",
|
|
247
|
+
/** Video is playing */
|
|
248
|
+
PLAYING = "playing",
|
|
249
|
+
/** Video is paused */
|
|
250
|
+
PAUSED = "paused",
|
|
251
|
+
/** Buffering due to network/resource constraints */
|
|
252
|
+
BUFFERING = "buffering",
|
|
253
|
+
/** Error occurred */
|
|
254
|
+
ERROR = "error"
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Player error with context
|
|
258
|
+
*/
|
|
259
|
+
interface PlayerError {
|
|
260
|
+
/** Error message */
|
|
261
|
+
message: string;
|
|
262
|
+
/** Error code for programmatic handling */
|
|
263
|
+
code: 'MEDIA_ERROR' | 'NETWORK_ERROR' | 'DECODE_ERROR' | 'NOT_SUPPORTED' | 'UNKNOWN';
|
|
264
|
+
/** Whether the error is recoverable */
|
|
265
|
+
recoverable: boolean;
|
|
266
|
+
/** Original error if available */
|
|
267
|
+
originalError?: Error;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Player state for zustand store
|
|
271
|
+
*/
|
|
272
|
+
interface PlayerState {
|
|
273
|
+
/** Current player status */
|
|
274
|
+
status: PlayerStatus;
|
|
275
|
+
/** Currently loaded video */
|
|
276
|
+
currentVideo: VideoItem | null;
|
|
277
|
+
/** Current playback time in seconds */
|
|
278
|
+
currentTime: number;
|
|
279
|
+
/** Video duration in seconds */
|
|
280
|
+
duration: number;
|
|
281
|
+
/** Buffered percentage (0-100) */
|
|
282
|
+
buffered: number;
|
|
283
|
+
/** Volume level (0-1) */
|
|
284
|
+
volume: number;
|
|
285
|
+
/** Whether video is muted */
|
|
286
|
+
muted: boolean;
|
|
287
|
+
/** Playback rate (1 = normal) */
|
|
288
|
+
playbackRate: number;
|
|
289
|
+
/** Loop count - how many times video has looped */
|
|
290
|
+
loopCount: number;
|
|
291
|
+
/** Total watch time in seconds for current video */
|
|
292
|
+
watchTime: number;
|
|
293
|
+
/** Error state */
|
|
294
|
+
error: PlayerError | null;
|
|
295
|
+
/** Whether video has ended */
|
|
296
|
+
ended: boolean;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* PlayerEngine configuration
|
|
300
|
+
*/
|
|
301
|
+
interface PlayerConfig {
|
|
302
|
+
/** Auto-play when video is loaded (default: true) */
|
|
303
|
+
autoplay?: boolean;
|
|
304
|
+
/** Default volume (default: 1) */
|
|
305
|
+
defaultVolume?: number;
|
|
306
|
+
/** Default muted state (default: true for mobile) */
|
|
307
|
+
defaultMuted?: boolean;
|
|
308
|
+
/** Enable looping (default: true for short videos) */
|
|
309
|
+
loop?: boolean;
|
|
310
|
+
/** Analytics adapter for tracking */
|
|
311
|
+
analytics?: IAnalytics;
|
|
312
|
+
/** Logger adapter */
|
|
313
|
+
logger?: ILogger;
|
|
314
|
+
/** Watch time threshold for analytics (seconds) (default: 3) */
|
|
315
|
+
watchTimeThreshold?: number;
|
|
316
|
+
/** Completion threshold percentage (default: 90) */
|
|
317
|
+
completionThreshold?: number;
|
|
318
|
+
/** Circuit Breaker configuration */
|
|
319
|
+
circuitBreaker?: CircuitBreakerConfig;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Default player configuration
|
|
323
|
+
*/
|
|
324
|
+
declare const DEFAULT_PLAYER_CONFIG: Required<Omit<PlayerConfig, 'analytics' | 'logger' | 'circuitBreaker'>>;
|
|
325
|
+
/**
|
|
326
|
+
* Player events for external listening
|
|
327
|
+
*/
|
|
328
|
+
type PlayerEvent = {
|
|
329
|
+
type: 'statusChange';
|
|
330
|
+
status: PlayerStatus;
|
|
331
|
+
previousStatus: PlayerStatus;
|
|
332
|
+
} | {
|
|
333
|
+
type: 'timeUpdate';
|
|
334
|
+
currentTime: number;
|
|
335
|
+
duration: number;
|
|
336
|
+
} | {
|
|
337
|
+
type: 'seek';
|
|
338
|
+
time: number;
|
|
339
|
+
} | {
|
|
340
|
+
type: 'ended';
|
|
341
|
+
loopCount: number;
|
|
342
|
+
watchTime: number;
|
|
343
|
+
} | {
|
|
344
|
+
type: 'error';
|
|
345
|
+
error: PlayerError;
|
|
346
|
+
} | {
|
|
347
|
+
type: 'videoChange';
|
|
348
|
+
video: VideoItem | null;
|
|
349
|
+
} | {
|
|
350
|
+
type: 'circuitOpened';
|
|
351
|
+
consecutiveErrors: number;
|
|
352
|
+
lastError: PlayerError;
|
|
353
|
+
} | {
|
|
354
|
+
type: 'circuitHalfOpen';
|
|
355
|
+
} | {
|
|
356
|
+
type: 'circuitClosed';
|
|
357
|
+
reason: 'success' | 'manual';
|
|
358
|
+
} | {
|
|
359
|
+
type: 'loadRejected';
|
|
360
|
+
reason: 'circuit_open';
|
|
361
|
+
};
|
|
362
|
+
/**
|
|
363
|
+
* Player event listener
|
|
364
|
+
*/
|
|
365
|
+
type PlayerEventListener = (event: PlayerEvent) => void;
|
|
366
|
+
/**
|
|
367
|
+
* Circuit Breaker state
|
|
368
|
+
*
|
|
369
|
+
* - CLOSED: Normal operation, allow load()
|
|
370
|
+
* - OPEN: Reject load(), return cached error (circuit tripped)
|
|
371
|
+
* - HALF_OPEN: Allow 1 try, success→CLOSED, fail→OPEN
|
|
372
|
+
*/
|
|
373
|
+
declare enum CircuitState {
|
|
374
|
+
/** Normal operation - allow video loading */
|
|
375
|
+
CLOSED = "closed",
|
|
376
|
+
/** Circuit tripped - reject video loading */
|
|
377
|
+
OPEN = "open",
|
|
378
|
+
/** Testing state - allow one request to test recovery */
|
|
379
|
+
HALF_OPEN = "half_open"
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Circuit Breaker configuration
|
|
383
|
+
*/
|
|
384
|
+
interface CircuitBreakerConfig {
|
|
385
|
+
/**
|
|
386
|
+
* Number of consecutive recoverable errors to trip the circuit
|
|
387
|
+
* @default 3
|
|
388
|
+
*/
|
|
389
|
+
consecutiveErrorThreshold?: number;
|
|
390
|
+
/**
|
|
391
|
+
* Time in ms before transitioning from OPEN to HALF_OPEN
|
|
392
|
+
* @default 30000 (30 seconds)
|
|
393
|
+
*/
|
|
394
|
+
resetTimeoutMs?: number;
|
|
395
|
+
/**
|
|
396
|
+
* Number of successful loads needed to close the circuit from HALF_OPEN
|
|
397
|
+
* @default 1
|
|
398
|
+
*/
|
|
399
|
+
successThreshold?: number;
|
|
400
|
+
/**
|
|
401
|
+
* Enable circuit breaker (default: true)
|
|
402
|
+
* Set to false to disable circuit breaker behavior
|
|
403
|
+
*/
|
|
404
|
+
enabled?: boolean;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Circuit Breaker internal state
|
|
408
|
+
*/
|
|
409
|
+
interface CircuitBreakerState {
|
|
410
|
+
/** Current circuit state */
|
|
411
|
+
state: CircuitState;
|
|
412
|
+
/** Number of consecutive recoverable errors */
|
|
413
|
+
consecutiveErrors: number;
|
|
414
|
+
/** Number of successful loads in HALF_OPEN state */
|
|
415
|
+
halfOpenSuccesses: number;
|
|
416
|
+
/** Timestamp when circuit was opened (for reset timeout) */
|
|
417
|
+
openedAt: number | null;
|
|
418
|
+
/** Last error that caused the circuit to open */
|
|
419
|
+
lastError: PlayerError | null;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* PlayerEngine - Video player state machine with zustand/vanilla store
|
|
424
|
+
*
|
|
425
|
+
* Features:
|
|
426
|
+
* - Guarded state transitions (only valid transitions allowed)
|
|
427
|
+
* - Watch time tracking
|
|
428
|
+
* - Loop count tracking
|
|
429
|
+
* - Analytics integration
|
|
430
|
+
* - Event system for external listeners
|
|
431
|
+
*
|
|
432
|
+
* @example
|
|
433
|
+
* ```typescript
|
|
434
|
+
* const player = new PlayerEngine({ analytics, logger });
|
|
435
|
+
*
|
|
436
|
+
* // Subscribe to state changes
|
|
437
|
+
* player.store.subscribe((state) => console.log(state.status));
|
|
438
|
+
*
|
|
439
|
+
* // Load and play a video
|
|
440
|
+
* player.load(videoItem);
|
|
441
|
+
*
|
|
442
|
+
* // Control playback
|
|
443
|
+
* player.play();
|
|
444
|
+
* player.pause();
|
|
445
|
+
* player.seek(10);
|
|
446
|
+
* ```
|
|
447
|
+
*/
|
|
448
|
+
declare class PlayerEngine {
|
|
449
|
+
/** Zustand vanilla store - Single Source of Truth */
|
|
450
|
+
readonly store: StoreApi<PlayerState>;
|
|
451
|
+
/** Resolved configuration */
|
|
452
|
+
private readonly config;
|
|
453
|
+
/** Circuit Breaker configuration */
|
|
454
|
+
private readonly circuitBreakerConfig;
|
|
455
|
+
/** Analytics adapter */
|
|
456
|
+
private readonly analytics?;
|
|
457
|
+
/** Logger adapter */
|
|
458
|
+
private readonly logger?;
|
|
459
|
+
/** Event listeners */
|
|
460
|
+
private readonly eventListeners;
|
|
461
|
+
/** Watch time tracking interval */
|
|
462
|
+
private watchTimeInterval;
|
|
463
|
+
/** Last status for tracking transitions */
|
|
464
|
+
private lastStatus;
|
|
465
|
+
/** Circuit Breaker state */
|
|
466
|
+
private circuitBreaker;
|
|
467
|
+
/** Circuit Breaker reset timer */
|
|
468
|
+
private circuitResetTimer;
|
|
469
|
+
constructor(config?: PlayerConfig);
|
|
470
|
+
/**
|
|
471
|
+
* Load a video and prepare for playback
|
|
472
|
+
*
|
|
473
|
+
* @returns true if load was initiated, false if rejected (circuit open or invalid state)
|
|
474
|
+
*/
|
|
475
|
+
load(video: VideoItem): boolean;
|
|
476
|
+
/**
|
|
477
|
+
* Called when video is ready to play (after loading)
|
|
478
|
+
*/
|
|
479
|
+
onReady(): boolean;
|
|
480
|
+
/**
|
|
481
|
+
* Start or resume playback
|
|
482
|
+
*/
|
|
483
|
+
play(): boolean;
|
|
484
|
+
/**
|
|
485
|
+
* Pause playback
|
|
486
|
+
*/
|
|
487
|
+
pause(): boolean;
|
|
488
|
+
/**
|
|
489
|
+
* Seek to a specific time
|
|
490
|
+
*
|
|
491
|
+
* Emits a 'seek' event that VideoPlayer listens to
|
|
492
|
+
* to apply the seek to the actual video element.
|
|
493
|
+
*/
|
|
494
|
+
seek(time: number): boolean;
|
|
495
|
+
/**
|
|
496
|
+
* Set volume
|
|
497
|
+
*/
|
|
498
|
+
setVolume(volume: number): void;
|
|
499
|
+
/**
|
|
500
|
+
* Toggle mute
|
|
501
|
+
*/
|
|
502
|
+
setMuted(muted: boolean): void;
|
|
503
|
+
/**
|
|
504
|
+
* Set playback rate
|
|
505
|
+
*/
|
|
506
|
+
setPlaybackRate(rate: number): void;
|
|
507
|
+
/**
|
|
508
|
+
* Handle time update from video element
|
|
509
|
+
*/
|
|
510
|
+
onTimeUpdate(currentTime: number): void;
|
|
511
|
+
/**
|
|
512
|
+
* Handle duration change from video element
|
|
513
|
+
*
|
|
514
|
+
* Called when video metadata is loaded and actual duration is available.
|
|
515
|
+
* This may differ from the API-provided duration in VideoItem.
|
|
516
|
+
*
|
|
517
|
+
* @param duration - Actual video duration in seconds from video element
|
|
518
|
+
*/
|
|
519
|
+
onDurationChange(duration: number): void;
|
|
520
|
+
/**
|
|
521
|
+
* Handle buffering start
|
|
522
|
+
*/
|
|
523
|
+
onBuffering(): boolean;
|
|
524
|
+
/**
|
|
525
|
+
* Handle buffering end
|
|
526
|
+
*/
|
|
527
|
+
onBufferingEnd(): boolean;
|
|
528
|
+
/**
|
|
529
|
+
* Handle buffer progress update
|
|
530
|
+
*/
|
|
531
|
+
onBufferProgress(bufferedPercent: number): void;
|
|
532
|
+
/**
|
|
533
|
+
* Handle video ended
|
|
534
|
+
*/
|
|
535
|
+
onEnded(): void;
|
|
536
|
+
/**
|
|
537
|
+
* Handle video error
|
|
538
|
+
*/
|
|
539
|
+
onError(error: Error, mediaError?: MediaError): void;
|
|
540
|
+
/**
|
|
541
|
+
* Reset player to initial state
|
|
542
|
+
*/
|
|
543
|
+
reset(): void;
|
|
544
|
+
/**
|
|
545
|
+
* Destroy player and cleanup
|
|
546
|
+
*/
|
|
547
|
+
destroy(): void;
|
|
548
|
+
/**
|
|
549
|
+
* Add event listener
|
|
550
|
+
*/
|
|
551
|
+
addEventListener(listener: PlayerEventListener): () => void;
|
|
552
|
+
/**
|
|
553
|
+
* Remove event listener
|
|
554
|
+
*/
|
|
555
|
+
removeEventListener(listener: PlayerEventListener): void;
|
|
556
|
+
/**
|
|
557
|
+
* Get current status
|
|
558
|
+
*/
|
|
559
|
+
getStatus(): PlayerStatus;
|
|
560
|
+
/**
|
|
561
|
+
* Get current video
|
|
562
|
+
*/
|
|
563
|
+
getCurrentVideo(): VideoItem | null;
|
|
564
|
+
/**
|
|
565
|
+
* Check if playing
|
|
566
|
+
*/
|
|
567
|
+
isPlaying(): boolean;
|
|
568
|
+
/**
|
|
569
|
+
* Check if paused
|
|
570
|
+
*/
|
|
571
|
+
isPaused(): boolean;
|
|
572
|
+
/**
|
|
573
|
+
* Attempt to transition to a new state
|
|
574
|
+
*/
|
|
575
|
+
private transitionTo;
|
|
576
|
+
/**
|
|
577
|
+
* Emit event to all listeners
|
|
578
|
+
*/
|
|
579
|
+
private emitEvent;
|
|
580
|
+
/**
|
|
581
|
+
* Start watch time tracking
|
|
582
|
+
*/
|
|
583
|
+
private startWatchTimeTracking;
|
|
584
|
+
/**
|
|
585
|
+
* Stop watch time tracking
|
|
586
|
+
*/
|
|
587
|
+
private stopWatchTimeTracking;
|
|
588
|
+
/**
|
|
589
|
+
* Track completion analytics
|
|
590
|
+
*/
|
|
591
|
+
private trackCompletion;
|
|
592
|
+
/**
|
|
593
|
+
* Categorize media error
|
|
594
|
+
*/
|
|
595
|
+
private categorizeError;
|
|
596
|
+
/**
|
|
597
|
+
* Get current circuit breaker state
|
|
598
|
+
*/
|
|
599
|
+
getCircuitBreakerState(): CircuitBreakerState;
|
|
600
|
+
/**
|
|
601
|
+
* Check if circuit breaker is open (blocking loads)
|
|
602
|
+
*/
|
|
603
|
+
isCircuitOpen(): boolean;
|
|
604
|
+
/**
|
|
605
|
+
* Manually reset circuit breaker to CLOSED state
|
|
606
|
+
* Useful for user-triggered retry after showing error UI
|
|
607
|
+
*/
|
|
608
|
+
resetCircuitBreaker(): void;
|
|
609
|
+
/**
|
|
610
|
+
* Check if circuit breaker allows load operation
|
|
611
|
+
* Also handles OPEN → HALF_OPEN transition based on timeout
|
|
612
|
+
*/
|
|
613
|
+
private checkCircuitBreaker;
|
|
614
|
+
/**
|
|
615
|
+
* Record a recoverable error for circuit breaker tracking
|
|
616
|
+
*/
|
|
617
|
+
private recordCircuitBreakerError;
|
|
618
|
+
/**
|
|
619
|
+
* Record a successful load for circuit breaker tracking
|
|
620
|
+
*/
|
|
621
|
+
private recordCircuitBreakerSuccess;
|
|
622
|
+
/**
|
|
623
|
+
* Trip the circuit breaker (CLOSED/HALF_OPEN → OPEN)
|
|
624
|
+
*/
|
|
625
|
+
private tripCircuit;
|
|
626
|
+
/**
|
|
627
|
+
* Transition from OPEN to HALF_OPEN
|
|
628
|
+
*/
|
|
629
|
+
private transitionToHalfOpen;
|
|
630
|
+
/**
|
|
631
|
+
* Close the circuit breaker (HALF_OPEN → CLOSED)
|
|
632
|
+
*/
|
|
633
|
+
private closeCircuit;
|
|
634
|
+
/**
|
|
635
|
+
* Schedule automatic transition from OPEN to HALF_OPEN
|
|
636
|
+
*/
|
|
637
|
+
private scheduleHalfOpenTransition;
|
|
638
|
+
/**
|
|
639
|
+
* Clear circuit reset timer
|
|
640
|
+
*/
|
|
641
|
+
private clearCircuitResetTimer;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* Check if a state transition is valid
|
|
646
|
+
*/
|
|
647
|
+
declare function isValidTransition(from: PlayerStatus, to: PlayerStatus): boolean;
|
|
648
|
+
/**
|
|
649
|
+
* Check if player is in a "active" state (can receive playback commands)
|
|
650
|
+
*/
|
|
651
|
+
declare function isActiveState(status: PlayerStatus): boolean;
|
|
652
|
+
/**
|
|
653
|
+
* Check if player can start playback
|
|
654
|
+
*/
|
|
655
|
+
declare function canPlay(status: PlayerStatus): boolean;
|
|
656
|
+
/**
|
|
657
|
+
* Check if player can pause
|
|
658
|
+
*/
|
|
659
|
+
declare function canPause(status: PlayerStatus): boolean;
|
|
660
|
+
/**
|
|
661
|
+
* Check if player can seek
|
|
662
|
+
*/
|
|
663
|
+
declare function canSeek(status: PlayerStatus): boolean;
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Lifecycle state for zustand store
|
|
667
|
+
*/
|
|
668
|
+
interface LifecycleState {
|
|
669
|
+
/** Whether manager is initialized */
|
|
670
|
+
isInitialized: boolean;
|
|
671
|
+
/** Whether there's a pending save operation */
|
|
672
|
+
isSaving: boolean;
|
|
673
|
+
/** Whether there's a pending restore operation */
|
|
674
|
+
isRestoring: boolean;
|
|
675
|
+
/** Last saved timestamp */
|
|
676
|
+
lastSavedAt: number | null;
|
|
677
|
+
/** Last restored timestamp */
|
|
678
|
+
lastRestoredAt: number | null;
|
|
679
|
+
/** Whether the restored snapshot needs revalidation */
|
|
680
|
+
needsRevalidation: boolean;
|
|
681
|
+
/** Current visibility state */
|
|
682
|
+
visibilityState: DocumentVisibilityState;
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Restore result
|
|
686
|
+
*/
|
|
687
|
+
interface RestoreResult {
|
|
688
|
+
/** Whether restore was successful */
|
|
689
|
+
success: boolean;
|
|
690
|
+
/** Restored snapshot data (null if no valid snapshot) */
|
|
691
|
+
snapshot: SessionSnapshot | null;
|
|
692
|
+
/** Whether the restored data is stale and needs revalidation */
|
|
693
|
+
needsRevalidation: boolean;
|
|
694
|
+
/** Reason for failure (if any) */
|
|
695
|
+
reason?: 'no_snapshot' | 'expired' | 'invalid' | 'error';
|
|
696
|
+
/** Playback time in seconds (only present if restorePlaybackPosition config is enabled) */
|
|
697
|
+
playbackTime?: number;
|
|
698
|
+
/** Video ID that was playing (only present if restorePlaybackPosition config is enabled) */
|
|
699
|
+
currentVideoId?: string;
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* LifecycleManager configuration
|
|
703
|
+
*/
|
|
704
|
+
interface LifecycleConfig {
|
|
705
|
+
/** Storage adapter for persistence */
|
|
706
|
+
storage?: ISessionStorage;
|
|
707
|
+
/** Logger adapter */
|
|
708
|
+
logger?: ILogger;
|
|
709
|
+
/** Snapshot expiry time in ms (default: 24 hours) */
|
|
710
|
+
snapshotExpiryMs?: number;
|
|
711
|
+
/** Revalidation threshold in ms (default: 5 minutes) */
|
|
712
|
+
revalidationThresholdMs?: number;
|
|
713
|
+
/** Auto-save on visibility change (default: true) */
|
|
714
|
+
autoSaveOnHidden?: boolean;
|
|
715
|
+
/**
|
|
716
|
+
* Enable restoring video playback position (default: false)
|
|
717
|
+
* When enabled, saves and restores the exact playback time (seconds)
|
|
718
|
+
* so video can seek() to the exact position user was watching
|
|
719
|
+
*/
|
|
720
|
+
restorePlaybackPosition?: boolean;
|
|
721
|
+
/** SDK version for compatibility */
|
|
722
|
+
version?: string;
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Default lifecycle configuration
|
|
726
|
+
*/
|
|
727
|
+
declare const DEFAULT_LIFECYCLE_CONFIG: Required<Omit<LifecycleConfig, 'storage' | 'logger'>>;
|
|
728
|
+
/**
|
|
729
|
+
* Lifecycle events for external listening
|
|
730
|
+
*/
|
|
731
|
+
type LifecycleEvent = {
|
|
732
|
+
type: 'saveStart';
|
|
733
|
+
} | {
|
|
734
|
+
type: 'saveComplete';
|
|
735
|
+
timestamp: number;
|
|
736
|
+
} | {
|
|
737
|
+
type: 'saveFailed';
|
|
738
|
+
error: Error;
|
|
739
|
+
} | {
|
|
740
|
+
type: 'restoreStart';
|
|
741
|
+
} | {
|
|
742
|
+
type: 'restoreComplete';
|
|
743
|
+
result: RestoreResult;
|
|
744
|
+
} | {
|
|
745
|
+
type: 'visibilityChange';
|
|
746
|
+
state: DocumentVisibilityState;
|
|
747
|
+
};
|
|
748
|
+
/**
|
|
749
|
+
* Lifecycle event listener
|
|
750
|
+
*/
|
|
751
|
+
type LifecycleEventListener = (event: LifecycleEvent) => void;
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* LifecycleManager - Handles session persistence and restoration
|
|
755
|
+
*
|
|
756
|
+
* Strategy: "State Persistence, DOM Destruction"
|
|
757
|
+
* - Save snapshot to storage when user leaves
|
|
758
|
+
* - Restore state from storage when user returns
|
|
759
|
+
* - Video DOM nodes are destroyed to free RAM
|
|
760
|
+
*
|
|
761
|
+
* Features:
|
|
762
|
+
* - Auto-save on visibility change (optional)
|
|
763
|
+
* - Snapshot expiry (24 hours default)
|
|
764
|
+
* - Stale data detection for revalidation
|
|
765
|
+
* - Event system for UI coordination
|
|
766
|
+
*
|
|
767
|
+
* @example
|
|
768
|
+
* ```typescript
|
|
769
|
+
* const lifecycle = new LifecycleManager({ storage, logger });
|
|
770
|
+
*
|
|
771
|
+
* // Initialize and attempt restore
|
|
772
|
+
* const result = await lifecycle.initialize();
|
|
773
|
+
* if (result.success) {
|
|
774
|
+
* feedManager.restoreFromSnapshot(result.snapshot);
|
|
775
|
+
* if (result.needsRevalidation) {
|
|
776
|
+
* feedManager.revalidate();
|
|
777
|
+
* }
|
|
778
|
+
* }
|
|
779
|
+
*
|
|
780
|
+
* // Save snapshot before leaving
|
|
781
|
+
* lifecycle.saveSnapshot({
|
|
782
|
+
* items: feedManager.getVideos(),
|
|
783
|
+
* cursor: feedManager.store.getState().cursor,
|
|
784
|
+
* focusedIndex: resourceGovernor.store.getState().focusedIndex,
|
|
785
|
+
* });
|
|
786
|
+
* ```
|
|
787
|
+
*/
|
|
788
|
+
declare class LifecycleManager {
|
|
789
|
+
/** Zustand vanilla store - Single Source of Truth */
|
|
790
|
+
readonly store: StoreApi<LifecycleState>;
|
|
791
|
+
/** Resolved configuration */
|
|
792
|
+
private readonly config;
|
|
793
|
+
/** Storage adapter */
|
|
794
|
+
private readonly storage?;
|
|
795
|
+
/** Logger adapter */
|
|
796
|
+
private readonly logger?;
|
|
797
|
+
/** Event listeners */
|
|
798
|
+
private readonly eventListeners;
|
|
799
|
+
/** Visibility change handler reference (for cleanup) */
|
|
800
|
+
private visibilityHandler?;
|
|
801
|
+
/** Pending save data (for debouncing) */
|
|
802
|
+
private pendingSaveData?;
|
|
803
|
+
constructor(config?: LifecycleConfig);
|
|
804
|
+
/**
|
|
805
|
+
* Initialize lifecycle manager and attempt to restore session
|
|
806
|
+
*/
|
|
807
|
+
initialize(): Promise<RestoreResult>;
|
|
808
|
+
/**
|
|
809
|
+
* Destroy lifecycle manager and cleanup
|
|
810
|
+
*/
|
|
811
|
+
destroy(): void;
|
|
812
|
+
/**
|
|
813
|
+
* Restore session from storage
|
|
814
|
+
*/
|
|
815
|
+
restoreSession(): Promise<RestoreResult>;
|
|
816
|
+
/**
|
|
817
|
+
* Save session snapshot to storage
|
|
818
|
+
*
|
|
819
|
+
* @param data - Snapshot data to save
|
|
820
|
+
* @param data.playbackTime - Current video playback position (only saved if restorePlaybackPosition config is enabled)
|
|
821
|
+
* @param data.currentVideoId - Current video ID (only saved if restorePlaybackPosition config is enabled)
|
|
822
|
+
*/
|
|
823
|
+
saveSnapshot(data: {
|
|
824
|
+
items: VideoItem[];
|
|
825
|
+
cursor: string | null;
|
|
826
|
+
focusedIndex: number;
|
|
827
|
+
scrollPosition?: number;
|
|
828
|
+
/** Current video playback time in seconds (only used when restorePlaybackPosition is enabled) */
|
|
829
|
+
playbackTime?: number;
|
|
830
|
+
/** Current video ID (only used when restorePlaybackPosition is enabled) */
|
|
831
|
+
currentVideoId?: string;
|
|
832
|
+
}): Promise<boolean>;
|
|
833
|
+
/**
|
|
834
|
+
* Clear saved snapshot
|
|
835
|
+
*/
|
|
836
|
+
clearSnapshot(): Promise<void>;
|
|
837
|
+
/**
|
|
838
|
+
* Mark that pending data should be saved (for use with debouncing)
|
|
839
|
+
*
|
|
840
|
+
* @param data - Data to save when flush is called
|
|
841
|
+
*/
|
|
842
|
+
setPendingSave(data: Omit<SessionSnapshot, 'savedAt' | 'version'>): void;
|
|
843
|
+
/**
|
|
844
|
+
* Check if restorePlaybackPosition config is enabled
|
|
845
|
+
* SDK can use this to decide whether to collect playbackTime
|
|
846
|
+
*/
|
|
847
|
+
isPlaybackPositionRestoreEnabled(): boolean;
|
|
848
|
+
/**
|
|
849
|
+
* Flush pending save (called on visibility hidden)
|
|
850
|
+
*/
|
|
851
|
+
flushPendingSave(): Promise<void>;
|
|
852
|
+
/**
|
|
853
|
+
* Handle visibility state change
|
|
854
|
+
*/
|
|
855
|
+
onVisibilityChange(state: DocumentVisibilityState): void;
|
|
856
|
+
/**
|
|
857
|
+
* Get current visibility state
|
|
858
|
+
*/
|
|
859
|
+
getVisibilityState(): DocumentVisibilityState;
|
|
860
|
+
/**
|
|
861
|
+
* Add event listener
|
|
862
|
+
*/
|
|
863
|
+
addEventListener(listener: LifecycleEventListener): () => void;
|
|
864
|
+
/**
|
|
865
|
+
* Remove event listener
|
|
866
|
+
*/
|
|
867
|
+
removeEventListener(listener: LifecycleEventListener): void;
|
|
868
|
+
/**
|
|
869
|
+
* Setup visibility change listener
|
|
870
|
+
*/
|
|
871
|
+
private setupVisibilityListener;
|
|
872
|
+
/**
|
|
873
|
+
* Validate snapshot data
|
|
874
|
+
*/
|
|
875
|
+
private validateSnapshot;
|
|
876
|
+
/**
|
|
877
|
+
* Check if snapshot is stale (needs revalidation)
|
|
878
|
+
*/
|
|
879
|
+
private isSnapshotStale;
|
|
880
|
+
/**
|
|
881
|
+
* Emit event to all listeners
|
|
882
|
+
*/
|
|
883
|
+
private emitEvent;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* Network type for prefetch strategy
|
|
888
|
+
* Simplified from contracts NetworkType for prefetch decisions
|
|
889
|
+
*/
|
|
890
|
+
type NetworkType = 'wifi' | 'cellular' | 'offline';
|
|
891
|
+
/**
|
|
892
|
+
* Map contracts NetworkType to simplified NetworkType
|
|
893
|
+
*/
|
|
894
|
+
declare function mapNetworkType(type: string): NetworkType;
|
|
895
|
+
/**
|
|
896
|
+
* Resource state for zustand store
|
|
897
|
+
*/
|
|
898
|
+
interface ResourceState {
|
|
899
|
+
/** Currently allocated video slot indices (max 3) */
|
|
900
|
+
activeAllocations: Set<number>;
|
|
901
|
+
/** Queue of indices to preload */
|
|
902
|
+
preloadQueue: number[];
|
|
903
|
+
/** Currently focused/playing video index */
|
|
904
|
+
focusedIndex: number;
|
|
905
|
+
/** Current network type */
|
|
906
|
+
networkType: NetworkType;
|
|
907
|
+
/** Total number of items in feed (for bounds checking) */
|
|
908
|
+
totalItems: number;
|
|
909
|
+
/** Whether resource governor is active */
|
|
910
|
+
isActive: boolean;
|
|
911
|
+
/** Whether preloading is throttled due to scroll thrashing */
|
|
912
|
+
isThrottled: boolean;
|
|
913
|
+
/** Indices currently being preloaded */
|
|
914
|
+
preloadingIndices: Set<number>;
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* Allocation result returned by requestAllocation
|
|
918
|
+
*/
|
|
919
|
+
interface AllocationResult {
|
|
920
|
+
/** Indices that should mount video elements */
|
|
921
|
+
toMount: number[];
|
|
922
|
+
/** Indices that should unmount video elements */
|
|
923
|
+
toUnmount: number[];
|
|
924
|
+
/** Currently active allocations after this operation */
|
|
925
|
+
activeAllocations: number[];
|
|
926
|
+
/** Whether focus change was successful */
|
|
927
|
+
success: boolean;
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* Prefetch configuration by network type
|
|
931
|
+
*/
|
|
932
|
+
interface PrefetchConfig {
|
|
933
|
+
/** Number of posters to prefetch ahead (default: wifi=5, cellular=3, offline=1) */
|
|
934
|
+
posterCount: number;
|
|
935
|
+
/** Number of video segments to prefetch (default: wifi=2, cellular=1, offline=0) */
|
|
936
|
+
videoSegmentCount: number;
|
|
937
|
+
/** Whether to prefetch video at all (default: true for wifi/cellular) */
|
|
938
|
+
prefetchVideo: boolean;
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Scroll thrashing configuration
|
|
942
|
+
* Prevents excessive preloading when user scrolls too fast
|
|
943
|
+
*/
|
|
944
|
+
interface ScrollThrashingConfig {
|
|
945
|
+
/** Time window to measure scroll rate (ms) - default: 1000 */
|
|
946
|
+
windowMs: number;
|
|
947
|
+
/** Max focus changes allowed in window before throttling - default: 3 */
|
|
948
|
+
maxChangesInWindow: number;
|
|
949
|
+
/** Cooldown time after throttle before resuming preload (ms) - default: 500 */
|
|
950
|
+
cooldownMs: number;
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* ResourceGovernor configuration
|
|
954
|
+
*/
|
|
955
|
+
interface ResourceConfig {
|
|
956
|
+
/** Maximum video DOM nodes (default: 3) */
|
|
957
|
+
maxAllocations?: number;
|
|
958
|
+
/** Focus debounce time in ms (default: 150) */
|
|
959
|
+
focusDebounceMs?: number;
|
|
960
|
+
/** Prefetch configuration overrides by network type */
|
|
961
|
+
prefetch?: Partial<Record<NetworkType, Partial<PrefetchConfig>>>;
|
|
962
|
+
/** Scroll thrashing configuration */
|
|
963
|
+
scrollThrashing?: Partial<ScrollThrashingConfig>;
|
|
964
|
+
/** Network adapter for detecting connection type */
|
|
965
|
+
networkAdapter?: INetworkAdapter;
|
|
966
|
+
/** Video loader adapter for preloading video data */
|
|
967
|
+
videoLoader?: IVideoLoader;
|
|
968
|
+
/** Poster loader adapter for preloading thumbnails */
|
|
969
|
+
posterLoader?: IPosterLoader;
|
|
970
|
+
/** Logger adapter */
|
|
971
|
+
logger?: ILogger;
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Default prefetch configuration by network type
|
|
975
|
+
*/
|
|
976
|
+
declare const DEFAULT_PREFETCH_CONFIG: Record<NetworkType, PrefetchConfig>;
|
|
977
|
+
/**
|
|
978
|
+
* Default resource configuration
|
|
979
|
+
*/
|
|
980
|
+
declare const DEFAULT_RESOURCE_CONFIG: Required<Omit<ResourceConfig, 'networkAdapter' | 'videoLoader' | 'posterLoader' | 'logger' | 'prefetch' | 'scrollThrashing'>> & {
|
|
981
|
+
prefetch: Record<NetworkType, PrefetchConfig>;
|
|
982
|
+
scrollThrashing: ScrollThrashingConfig;
|
|
983
|
+
};
|
|
984
|
+
/**
|
|
985
|
+
* Resource events for external listening
|
|
986
|
+
*/
|
|
987
|
+
type ResourceEvent = {
|
|
988
|
+
type: 'allocationChange';
|
|
989
|
+
toMount: number[];
|
|
990
|
+
toUnmount: number[];
|
|
991
|
+
} | {
|
|
992
|
+
type: 'focusChange';
|
|
993
|
+
index: number;
|
|
994
|
+
previousIndex: number;
|
|
995
|
+
} | {
|
|
996
|
+
type: 'networkChange';
|
|
997
|
+
networkType: NetworkType;
|
|
998
|
+
} | {
|
|
999
|
+
type: 'prefetchRequest';
|
|
1000
|
+
indices: number[];
|
|
1001
|
+
};
|
|
1002
|
+
/**
|
|
1003
|
+
* Resource event listener
|
|
1004
|
+
*/
|
|
1005
|
+
type ResourceEventListener = (event: ResourceEvent) => void;
|
|
1006
|
+
|
|
1007
|
+
/**
|
|
1008
|
+
* Video source getter function type
|
|
1009
|
+
* ResourceGovernor needs this to get video sources for preloading
|
|
1010
|
+
*/
|
|
1011
|
+
type VideoSourceGetter = (index: number) => {
|
|
1012
|
+
id: string;
|
|
1013
|
+
source: VideoSource;
|
|
1014
|
+
poster?: string;
|
|
1015
|
+
} | null;
|
|
1016
|
+
/**
|
|
1017
|
+
* ResourceGovernor - Manages video DOM allocation and prefetch strategy
|
|
1018
|
+
*
|
|
1019
|
+
* Features:
|
|
1020
|
+
* - Sliding window allocation (max 3 video DOM nodes)
|
|
1021
|
+
* - Focus debouncing for smooth swipe
|
|
1022
|
+
* - Network-aware prefetch strategy
|
|
1023
|
+
* - Event system for UI synchronization
|
|
1024
|
+
*
|
|
1025
|
+
* Memory Strategy:
|
|
1026
|
+
* - Only 3 video elements exist in DOM at any time
|
|
1027
|
+
* - Sliding window: [previous, current, next]
|
|
1028
|
+
* - When user scrolls, we unmount far elements and mount new ones
|
|
1029
|
+
*
|
|
1030
|
+
* @example
|
|
1031
|
+
* ```typescript
|
|
1032
|
+
* const governor = new ResourceGovernor({ maxAllocations: 3 });
|
|
1033
|
+
*
|
|
1034
|
+
* // Initialize with feed size
|
|
1035
|
+
* governor.setTotalItems(20);
|
|
1036
|
+
* governor.activate();
|
|
1037
|
+
*
|
|
1038
|
+
* // Handle scroll/swipe
|
|
1039
|
+
* governor.setFocusedIndex(5); // Debounced
|
|
1040
|
+
*
|
|
1041
|
+
* // Listen to allocation changes
|
|
1042
|
+
* governor.addEventListener((event) => {
|
|
1043
|
+
* if (event.type === 'allocationChange') {
|
|
1044
|
+
* event.toMount.forEach(i => mountVideoAt(i));
|
|
1045
|
+
* event.toUnmount.forEach(i => unmountVideoAt(i));
|
|
1046
|
+
* }
|
|
1047
|
+
* });
|
|
1048
|
+
* ```
|
|
1049
|
+
*/
|
|
1050
|
+
declare class ResourceGovernor {
|
|
1051
|
+
/** Zustand vanilla store - Single Source of Truth */
|
|
1052
|
+
readonly store: StoreApi<ResourceState>;
|
|
1053
|
+
/** Resolved configuration */
|
|
1054
|
+
private readonly config;
|
|
1055
|
+
/** Network adapter */
|
|
1056
|
+
private readonly networkAdapter?;
|
|
1057
|
+
/** Video loader adapter for preloading video data */
|
|
1058
|
+
private readonly videoLoader?;
|
|
1059
|
+
/** Poster loader adapter for preloading thumbnails */
|
|
1060
|
+
private readonly posterLoader?;
|
|
1061
|
+
/** Logger adapter */
|
|
1062
|
+
private readonly logger?;
|
|
1063
|
+
/** Event listeners */
|
|
1064
|
+
private readonly eventListeners;
|
|
1065
|
+
/** Focus debounce timer */
|
|
1066
|
+
private focusDebounceTimer;
|
|
1067
|
+
/** Pending focused index (before debounce completes) */
|
|
1068
|
+
private pendingFocusedIndex;
|
|
1069
|
+
/** Network change unsubscribe function */
|
|
1070
|
+
private networkUnsubscribe?;
|
|
1071
|
+
/** Video source getter (injected via setVideoSourceGetter) */
|
|
1072
|
+
private videoSourceGetter?;
|
|
1073
|
+
/** Scroll thrashing detection - timestamps of recent focus changes */
|
|
1074
|
+
private focusChangeTimestamps;
|
|
1075
|
+
/** Scroll thrashing cooldown timer */
|
|
1076
|
+
private thrashingCooldownTimer;
|
|
1077
|
+
constructor(config?: ResourceConfig);
|
|
1078
|
+
/**
|
|
1079
|
+
* Activate the resource governor
|
|
1080
|
+
* Starts network monitoring and performs initial allocation
|
|
1081
|
+
*/
|
|
1082
|
+
activate(): Promise<void>;
|
|
1083
|
+
/**
|
|
1084
|
+
* Deactivate the resource governor
|
|
1085
|
+
* Stops network monitoring and clears allocations
|
|
1086
|
+
*/
|
|
1087
|
+
deactivate(): void;
|
|
1088
|
+
/**
|
|
1089
|
+
* Destroy the resource governor
|
|
1090
|
+
*/
|
|
1091
|
+
destroy(): void;
|
|
1092
|
+
/**
|
|
1093
|
+
* Set total number of items in feed
|
|
1094
|
+
*/
|
|
1095
|
+
setTotalItems(count: number): void;
|
|
1096
|
+
/**
|
|
1097
|
+
* Set focused index with debouncing
|
|
1098
|
+
* This is called during scroll/swipe
|
|
1099
|
+
*/
|
|
1100
|
+
setFocusedIndex(index: number): void;
|
|
1101
|
+
/**
|
|
1102
|
+
* Set focused index immediately (skip debounce)
|
|
1103
|
+
* Use this for programmatic navigation, not scroll
|
|
1104
|
+
*/
|
|
1105
|
+
setFocusedIndexImmediate(index: number): void;
|
|
1106
|
+
/**
|
|
1107
|
+
* Request allocation for given indices
|
|
1108
|
+
* Returns what needs to be mounted/unmounted
|
|
1109
|
+
*/
|
|
1110
|
+
requestAllocation(indices: number[]): AllocationResult;
|
|
1111
|
+
/**
|
|
1112
|
+
* Get current active allocations
|
|
1113
|
+
*/
|
|
1114
|
+
getActiveAllocations(): number[];
|
|
1115
|
+
/**
|
|
1116
|
+
* Check if an index is currently allocated
|
|
1117
|
+
*/
|
|
1118
|
+
isAllocated(index: number): boolean;
|
|
1119
|
+
/**
|
|
1120
|
+
* Get current network type
|
|
1121
|
+
*/
|
|
1122
|
+
getNetworkType(): NetworkType;
|
|
1123
|
+
/**
|
|
1124
|
+
* Get prefetch configuration for current network
|
|
1125
|
+
*/
|
|
1126
|
+
getPrefetchConfig(): PrefetchConfig;
|
|
1127
|
+
/**
|
|
1128
|
+
* Set video source getter function
|
|
1129
|
+
* This is called by SDK to provide video data for preloading
|
|
1130
|
+
*
|
|
1131
|
+
* @param getter - Function that returns video info for a given index
|
|
1132
|
+
*/
|
|
1133
|
+
setVideoSourceGetter(getter: VideoSourceGetter): void;
|
|
1134
|
+
/**
|
|
1135
|
+
* Check if preloading is currently throttled due to scroll thrashing
|
|
1136
|
+
*/
|
|
1137
|
+
isPreloadThrottled(): boolean;
|
|
1138
|
+
/**
|
|
1139
|
+
* Get indices currently being preloaded
|
|
1140
|
+
*/
|
|
1141
|
+
getPreloadingIndices(): number[];
|
|
1142
|
+
/**
|
|
1143
|
+
* Manually trigger preload for specific indices
|
|
1144
|
+
* Respects throttling and network conditions
|
|
1145
|
+
*/
|
|
1146
|
+
triggerPreload(indices: number[]): Promise<void>;
|
|
1147
|
+
/**
|
|
1148
|
+
* Add event listener
|
|
1149
|
+
*/
|
|
1150
|
+
addEventListener(listener: ResourceEventListener): () => void;
|
|
1151
|
+
/**
|
|
1152
|
+
* Remove event listener
|
|
1153
|
+
*/
|
|
1154
|
+
removeEventListener(listener: ResourceEventListener): void;
|
|
1155
|
+
/**
|
|
1156
|
+
* Initialize network detection
|
|
1157
|
+
*/
|
|
1158
|
+
private initializeNetwork;
|
|
1159
|
+
/**
|
|
1160
|
+
* Perform allocation for a given focused index
|
|
1161
|
+
*/
|
|
1162
|
+
private performAllocation;
|
|
1163
|
+
/**
|
|
1164
|
+
* Update prefetch queue based on current state
|
|
1165
|
+
*/
|
|
1166
|
+
private updatePrefetchQueue;
|
|
1167
|
+
/**
|
|
1168
|
+
* Track focus change timestamp for scroll thrashing detection
|
|
1169
|
+
*/
|
|
1170
|
+
private trackFocusChange;
|
|
1171
|
+
/**
|
|
1172
|
+
* Cancel all in-progress preloads
|
|
1173
|
+
*/
|
|
1174
|
+
private cancelAllPreloads;
|
|
1175
|
+
/**
|
|
1176
|
+
* Execute video preloading for given indices
|
|
1177
|
+
*/
|
|
1178
|
+
private executePreload;
|
|
1179
|
+
/**
|
|
1180
|
+
* Execute poster preloading for given indices
|
|
1181
|
+
*/
|
|
1182
|
+
private executePreloadPosters;
|
|
1183
|
+
/**
|
|
1184
|
+
* Emit event to all listeners
|
|
1185
|
+
*/
|
|
1186
|
+
private emitEvent;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
/**
|
|
1190
|
+
* Calculate sliding window allocation around focused index
|
|
1191
|
+
*
|
|
1192
|
+
* The sliding window strategy:
|
|
1193
|
+
* - Always allocate focused index (current video)
|
|
1194
|
+
* - Allocate previous index (for scroll up)
|
|
1195
|
+
* - Allocate next index (for scroll down)
|
|
1196
|
+
* - Total max 3 allocations
|
|
1197
|
+
*
|
|
1198
|
+
* @param focusedIndex Current focused video index
|
|
1199
|
+
* @param totalItems Total number of items in feed
|
|
1200
|
+
* @param maxAllocations Maximum allocations allowed (default: 3)
|
|
1201
|
+
* @returns Array of indices that should be allocated
|
|
1202
|
+
*/
|
|
1203
|
+
declare function calculateWindowIndices(focusedIndex: number, totalItems: number, maxAllocations?: number): number[];
|
|
1204
|
+
/**
|
|
1205
|
+
* Calculate prefetch indices beyond the window
|
|
1206
|
+
*
|
|
1207
|
+
* @param focusedIndex Current focused video index
|
|
1208
|
+
* @param totalItems Total number of items in feed
|
|
1209
|
+
* @param prefetchCount Number of items to prefetch ahead
|
|
1210
|
+
* @param windowIndices Currently allocated window indices
|
|
1211
|
+
* @returns Array of indices to prefetch (posters)
|
|
1212
|
+
*/
|
|
1213
|
+
declare function calculatePrefetchIndices(focusedIndex: number, totalItems: number, prefetchCount: number, windowIndices: number[]): number[];
|
|
1214
|
+
/**
|
|
1215
|
+
* Compute allocation changes between current and desired state
|
|
1216
|
+
*
|
|
1217
|
+
* @param currentAllocations Current active allocations
|
|
1218
|
+
* @param desiredAllocations Desired allocations based on new focus
|
|
1219
|
+
* @returns AllocationResult with toMount and toUnmount arrays
|
|
1220
|
+
*/
|
|
1221
|
+
declare function computeAllocationChanges(currentAllocations: Set<number>, desiredAllocations: number[]): Omit<AllocationResult, 'success'>;
|
|
1222
|
+
|
|
1223
|
+
/**
|
|
1224
|
+
* Types of optimistic actions
|
|
1225
|
+
*/
|
|
1226
|
+
type ActionType = 'like' | 'unlike' | 'follow' | 'unfollow';
|
|
1227
|
+
/**
|
|
1228
|
+
* Pending optimistic action
|
|
1229
|
+
*/
|
|
1230
|
+
interface PendingAction {
|
|
1231
|
+
/** Unique action ID */
|
|
1232
|
+
id: string;
|
|
1233
|
+
/** Action type */
|
|
1234
|
+
type: ActionType;
|
|
1235
|
+
/** Target video ID */
|
|
1236
|
+
videoId: string;
|
|
1237
|
+
/** Data to rollback to on failure */
|
|
1238
|
+
rollbackData: Partial<VideoItem>;
|
|
1239
|
+
/** Timestamp when action was initiated */
|
|
1240
|
+
timestamp: number;
|
|
1241
|
+
/** Current status */
|
|
1242
|
+
status: 'pending' | 'success' | 'failed';
|
|
1243
|
+
/** Number of retry attempts */
|
|
1244
|
+
retryCount: number;
|
|
1245
|
+
/** Error message if failed */
|
|
1246
|
+
error?: string;
|
|
1247
|
+
}
|
|
1248
|
+
/**
|
|
1249
|
+
* Optimistic state for zustand store
|
|
1250
|
+
*/
|
|
1251
|
+
interface OptimisticState {
|
|
1252
|
+
/** Map of pending actions by ID */
|
|
1253
|
+
pendingActions: Map<string, PendingAction>;
|
|
1254
|
+
/** Queue of failed actions for retry */
|
|
1255
|
+
failedQueue: string[];
|
|
1256
|
+
/** Whether there are any pending actions */
|
|
1257
|
+
hasPending: boolean;
|
|
1258
|
+
/** Whether retry is in progress */
|
|
1259
|
+
isRetrying: boolean;
|
|
1260
|
+
}
|
|
1261
|
+
/**
|
|
1262
|
+
* OptimisticManager configuration
|
|
1263
|
+
*/
|
|
1264
|
+
interface OptimisticConfig {
|
|
1265
|
+
/** Interaction adapter for API calls */
|
|
1266
|
+
interaction?: IInteraction;
|
|
1267
|
+
/** FeedManager for updating video state */
|
|
1268
|
+
feedManager?: FeedManager;
|
|
1269
|
+
/** Logger adapter */
|
|
1270
|
+
logger?: ILogger;
|
|
1271
|
+
/** Maximum retry attempts (default: 3) */
|
|
1272
|
+
maxRetries?: number;
|
|
1273
|
+
/** Retry delay in ms (default: 1000) */
|
|
1274
|
+
retryDelayMs?: number;
|
|
1275
|
+
/** Whether to auto-retry failed actions (default: true) */
|
|
1276
|
+
autoRetry?: boolean;
|
|
1277
|
+
}
|
|
1278
|
+
/**
|
|
1279
|
+
* Default optimistic configuration
|
|
1280
|
+
*/
|
|
1281
|
+
declare const DEFAULT_OPTIMISTIC_CONFIG: Required<Omit<OptimisticConfig, 'interaction' | 'feedManager' | 'logger'>>;
|
|
1282
|
+
/**
|
|
1283
|
+
* Optimistic events for external listening
|
|
1284
|
+
*/
|
|
1285
|
+
type OptimisticEvent = {
|
|
1286
|
+
type: 'actionStart';
|
|
1287
|
+
action: PendingAction;
|
|
1288
|
+
} | {
|
|
1289
|
+
type: 'actionSuccess';
|
|
1290
|
+
action: PendingAction;
|
|
1291
|
+
} | {
|
|
1292
|
+
type: 'actionFailed';
|
|
1293
|
+
action: PendingAction;
|
|
1294
|
+
error: Error;
|
|
1295
|
+
} | {
|
|
1296
|
+
type: 'actionRollback';
|
|
1297
|
+
action: PendingAction;
|
|
1298
|
+
} | {
|
|
1299
|
+
type: 'retryStart';
|
|
1300
|
+
actionId: string;
|
|
1301
|
+
} | {
|
|
1302
|
+
type: 'retryExhausted';
|
|
1303
|
+
action: PendingAction;
|
|
1304
|
+
};
|
|
1305
|
+
/**
|
|
1306
|
+
* Optimistic event listener
|
|
1307
|
+
*/
|
|
1308
|
+
type OptimisticEventListener = (event: OptimisticEvent) => void;
|
|
1309
|
+
|
|
1310
|
+
/**
|
|
1311
|
+
* OptimisticManager - Handles optimistic UI updates with rollback
|
|
1312
|
+
*
|
|
1313
|
+
* Pattern:
|
|
1314
|
+
* 1. User action (like) -> Update UI immediately
|
|
1315
|
+
* 2. Call API in background
|
|
1316
|
+
* 3. On success -> Keep UI state, remove pending action
|
|
1317
|
+
* 4. On error -> Rollback to previous state
|
|
1318
|
+
*
|
|
1319
|
+
* Features:
|
|
1320
|
+
* - Immediate UI feedback
|
|
1321
|
+
* - Automatic rollback on failure
|
|
1322
|
+
* - Retry queue for failed actions
|
|
1323
|
+
* - Prevents duplicate pending actions
|
|
1324
|
+
*
|
|
1325
|
+
* @example
|
|
1326
|
+
* ```typescript
|
|
1327
|
+
* const optimistic = new OptimisticManager({
|
|
1328
|
+
* interaction,
|
|
1329
|
+
* feedManager,
|
|
1330
|
+
* logger,
|
|
1331
|
+
* });
|
|
1332
|
+
*
|
|
1333
|
+
* // User taps like button
|
|
1334
|
+
* await optimistic.like(videoId);
|
|
1335
|
+
*
|
|
1336
|
+
* // User taps follow button
|
|
1337
|
+
* await optimistic.follow(videoId);
|
|
1338
|
+
* ```
|
|
1339
|
+
*/
|
|
1340
|
+
declare class OptimisticManager {
|
|
1341
|
+
/** Zustand vanilla store - Single Source of Truth */
|
|
1342
|
+
readonly store: StoreApi<OptimisticState>;
|
|
1343
|
+
/** Resolved configuration */
|
|
1344
|
+
private readonly config;
|
|
1345
|
+
/** Interaction adapter */
|
|
1346
|
+
private readonly interaction?;
|
|
1347
|
+
/** Feed manager for updating video state */
|
|
1348
|
+
private readonly feedManager?;
|
|
1349
|
+
/** Logger adapter */
|
|
1350
|
+
private readonly logger?;
|
|
1351
|
+
/** Event listeners */
|
|
1352
|
+
private readonly eventListeners;
|
|
1353
|
+
/** Retry timer */
|
|
1354
|
+
private retryTimer;
|
|
1355
|
+
constructor(config?: OptimisticConfig);
|
|
1356
|
+
/**
|
|
1357
|
+
* Like a video with optimistic update
|
|
1358
|
+
*/
|
|
1359
|
+
like(videoId: string): Promise<boolean>;
|
|
1360
|
+
/**
|
|
1361
|
+
* Unlike a video with optimistic update
|
|
1362
|
+
*/
|
|
1363
|
+
unlike(videoId: string): Promise<boolean>;
|
|
1364
|
+
/**
|
|
1365
|
+
* Toggle like state (like if not liked, unlike if liked)
|
|
1366
|
+
*/
|
|
1367
|
+
toggleLike(videoId: string): Promise<boolean>;
|
|
1368
|
+
/**
|
|
1369
|
+
* Follow a video author with optimistic update
|
|
1370
|
+
*/
|
|
1371
|
+
follow(videoId: string): Promise<boolean>;
|
|
1372
|
+
/**
|
|
1373
|
+
* Unfollow a video author with optimistic update
|
|
1374
|
+
*/
|
|
1375
|
+
unfollow(videoId: string): Promise<boolean>;
|
|
1376
|
+
/**
|
|
1377
|
+
* Toggle follow state
|
|
1378
|
+
*/
|
|
1379
|
+
toggleFollow(videoId: string): Promise<boolean>;
|
|
1380
|
+
/**
|
|
1381
|
+
* Get all pending actions
|
|
1382
|
+
*/
|
|
1383
|
+
getPendingActions(): PendingAction[];
|
|
1384
|
+
/**
|
|
1385
|
+
* Check if there's a pending action for a video
|
|
1386
|
+
*/
|
|
1387
|
+
hasPendingAction(videoId: string, type?: ActionType): boolean;
|
|
1388
|
+
/**
|
|
1389
|
+
* Get failed actions queue
|
|
1390
|
+
*/
|
|
1391
|
+
getFailedQueue(): PendingAction[];
|
|
1392
|
+
/**
|
|
1393
|
+
* Manually retry failed actions
|
|
1394
|
+
*/
|
|
1395
|
+
retryFailed(): Promise<void>;
|
|
1396
|
+
/**
|
|
1397
|
+
* Clear all failed actions
|
|
1398
|
+
*/
|
|
1399
|
+
clearFailed(): void;
|
|
1400
|
+
/**
|
|
1401
|
+
* Reset manager state
|
|
1402
|
+
*/
|
|
1403
|
+
reset(): void;
|
|
1404
|
+
/**
|
|
1405
|
+
* Destroy manager and cleanup
|
|
1406
|
+
*/
|
|
1407
|
+
destroy(): void;
|
|
1408
|
+
/**
|
|
1409
|
+
* Add event listener
|
|
1410
|
+
*/
|
|
1411
|
+
addEventListener(listener: OptimisticEventListener): () => void;
|
|
1412
|
+
/**
|
|
1413
|
+
* Remove event listener
|
|
1414
|
+
*/
|
|
1415
|
+
removeEventListener(listener: OptimisticEventListener): void;
|
|
1416
|
+
/**
|
|
1417
|
+
* Perform an optimistic action
|
|
1418
|
+
*/
|
|
1419
|
+
private performAction;
|
|
1420
|
+
/**
|
|
1421
|
+
* Create rollback data based on action type
|
|
1422
|
+
*/
|
|
1423
|
+
private createRollbackData;
|
|
1424
|
+
/**
|
|
1425
|
+
* Apply optimistic update to feed
|
|
1426
|
+
*/
|
|
1427
|
+
private applyOptimisticUpdate;
|
|
1428
|
+
/**
|
|
1429
|
+
* Apply rollback
|
|
1430
|
+
*/
|
|
1431
|
+
private applyRollback;
|
|
1432
|
+
/**
|
|
1433
|
+
* Add pending action to store
|
|
1434
|
+
*/
|
|
1435
|
+
private addPendingAction;
|
|
1436
|
+
/**
|
|
1437
|
+
* Mark action as success
|
|
1438
|
+
*/
|
|
1439
|
+
private markActionSuccess;
|
|
1440
|
+
/**
|
|
1441
|
+
* Mark action as failed
|
|
1442
|
+
*/
|
|
1443
|
+
private markActionFailed;
|
|
1444
|
+
/**
|
|
1445
|
+
* Retry a failed action
|
|
1446
|
+
*/
|
|
1447
|
+
private retryAction;
|
|
1448
|
+
/**
|
|
1449
|
+
* Execute API call based on action type
|
|
1450
|
+
*/
|
|
1451
|
+
private executeApiCall;
|
|
1452
|
+
/**
|
|
1453
|
+
* Schedule retry of failed actions
|
|
1454
|
+
*/
|
|
1455
|
+
private scheduleRetry;
|
|
1456
|
+
/**
|
|
1457
|
+
* Cancel retry timer
|
|
1458
|
+
*/
|
|
1459
|
+
private cancelRetryTimer;
|
|
1460
|
+
/**
|
|
1461
|
+
* Emit event to all listeners
|
|
1462
|
+
*/
|
|
1463
|
+
private emitEvent;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
export { type ActionType, type AllocationResult, DEFAULT_FEED_CONFIG, DEFAULT_LIFECYCLE_CONFIG, DEFAULT_OPTIMISTIC_CONFIG, DEFAULT_PLAYER_CONFIG, DEFAULT_PREFETCH_CONFIG, DEFAULT_RESOURCE_CONFIG, type FeedConfig, type FeedError, FeedManager, type FeedState, type LifecycleConfig, type LifecycleEvent, type LifecycleEventListener, LifecycleManager, type LifecycleState, type NetworkType, type OptimisticConfig, type OptimisticEvent, type OptimisticEventListener, OptimisticManager, type OptimisticState, type PendingAction, type PlayerConfig, PlayerEngine, type PlayerError, type PlayerEvent, type PlayerEventListener, type PlayerState, PlayerStatus, type PrefetchConfig, type ResourceConfig, type ResourceEvent, type ResourceEventListener, ResourceGovernor, type ResourceState, type RestoreResult, calculatePrefetchIndices, calculateWindowIndices, canPause, canPlay, canSeek, computeAllocationChanges, isActiveState, isValidTransition, mapNetworkType };
|