@xhub-short/core 0.1.0-beta.11 → 0.1.0-beta.12
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 +687 -495
- package/dist/index.js +367 -60
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,314 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { INetworkAdapter, IVideoLoader, IPosterLoader, ILogger, VideoSource, VideoItem, IDataSource, IStorage, PrefetchCacheData, IAnalytics, ISessionStorage, SessionSnapshot, IInteraction, CommentItem, ReplyItem, ICommentAdapter, InternalLogger, CommentAuthor, PlaylistData, ContentItem, PlaylistSummary, IPlaylistDataSource } from '@xhub-short/contracts';
|
|
2
2
|
export { SessionSnapshot } from '@xhub-short/contracts';
|
|
3
3
|
import { StoreApi } from 'zustand/vanilla';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Network type for prefetch strategy
|
|
7
|
+
* Simplified from contracts NetworkType for prefetch decisions
|
|
8
|
+
*/
|
|
9
|
+
type NetworkType = 'wifi' | 'cellular' | 'offline';
|
|
10
|
+
/**
|
|
11
|
+
* Map contracts NetworkType to simplified NetworkType
|
|
12
|
+
*/
|
|
13
|
+
declare function mapNetworkType(type: string): NetworkType;
|
|
14
|
+
/**
|
|
15
|
+
* Resource state for zustand store
|
|
16
|
+
*/
|
|
17
|
+
interface ResourceState {
|
|
18
|
+
/** Currently allocated video slot indices (max 3) */
|
|
19
|
+
activeAllocations: Set<number>;
|
|
20
|
+
/** Queue of indices to preload */
|
|
21
|
+
preloadQueue: number[];
|
|
22
|
+
/** Currently focused/playing video index */
|
|
23
|
+
focusedIndex: number;
|
|
24
|
+
/** Current network type */
|
|
25
|
+
networkType: NetworkType;
|
|
26
|
+
/** Total number of items in feed (for bounds checking) */
|
|
27
|
+
totalItems: number;
|
|
28
|
+
/** Whether resource governor is active */
|
|
29
|
+
isActive: boolean;
|
|
30
|
+
/** Whether preloading is throttled due to scroll thrashing */
|
|
31
|
+
isThrottled: boolean;
|
|
32
|
+
/** Indices currently being preloaded */
|
|
33
|
+
preloadingIndices: Set<number>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Allocation result returned by requestAllocation
|
|
37
|
+
*/
|
|
38
|
+
interface AllocationResult {
|
|
39
|
+
/** Indices that should mount video elements */
|
|
40
|
+
toMount: number[];
|
|
41
|
+
/** Indices that should unmount video elements */
|
|
42
|
+
toUnmount: number[];
|
|
43
|
+
/** Currently active allocations after this operation */
|
|
44
|
+
activeAllocations: number[];
|
|
45
|
+
/** Whether focus change was successful */
|
|
46
|
+
success: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Prefetch configuration by network type
|
|
50
|
+
*/
|
|
51
|
+
interface PrefetchConfig {
|
|
52
|
+
/** Number of posters to prefetch ahead (default: wifi=5, cellular=3, offline=1) */
|
|
53
|
+
posterCount: number;
|
|
54
|
+
/** Number of video segments to prefetch (default: wifi=2, cellular=1, offline=0) */
|
|
55
|
+
videoSegmentCount: number;
|
|
56
|
+
/** Whether to prefetch video at all (default: true for wifi/cellular) */
|
|
57
|
+
prefetchVideo: boolean;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Scroll thrashing configuration
|
|
61
|
+
* Prevents excessive preloading when user scrolls too fast
|
|
62
|
+
*/
|
|
63
|
+
interface ScrollThrashingConfig {
|
|
64
|
+
/** Time window to measure scroll rate (ms) - default: 1000 */
|
|
65
|
+
windowMs: number;
|
|
66
|
+
/** Max focus changes allowed in window before throttling - default: 3 */
|
|
67
|
+
maxChangesInWindow: number;
|
|
68
|
+
/** Cooldown time after throttle before resuming preload (ms) - default: 500 */
|
|
69
|
+
cooldownMs: number;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* ResourceGovernor configuration
|
|
73
|
+
*/
|
|
74
|
+
interface ResourceConfig {
|
|
75
|
+
/** Maximum video DOM nodes (default: 3) */
|
|
76
|
+
maxAllocations?: number;
|
|
77
|
+
/** Focus debounce time in ms (default: 150) */
|
|
78
|
+
focusDebounceMs?: number;
|
|
79
|
+
/** Prefetch configuration overrides by network type */
|
|
80
|
+
prefetch?: Partial<Record<NetworkType, Partial<PrefetchConfig>>>;
|
|
81
|
+
/** Scroll thrashing configuration */
|
|
82
|
+
scrollThrashing?: Partial<ScrollThrashingConfig>;
|
|
83
|
+
/** Network adapter for detecting connection type */
|
|
84
|
+
networkAdapter?: INetworkAdapter;
|
|
85
|
+
/** Video loader adapter for preloading video data */
|
|
86
|
+
videoLoader?: IVideoLoader;
|
|
87
|
+
/** Poster loader adapter for preloading thumbnails */
|
|
88
|
+
posterLoader?: IPosterLoader;
|
|
89
|
+
/** Logger adapter */
|
|
90
|
+
logger?: ILogger;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Default prefetch configuration by network type
|
|
94
|
+
*/
|
|
95
|
+
declare const DEFAULT_PREFETCH_CONFIG: Record<NetworkType, PrefetchConfig>;
|
|
96
|
+
/**
|
|
97
|
+
* Default resource configuration
|
|
98
|
+
*/
|
|
99
|
+
declare const DEFAULT_RESOURCE_CONFIG: Required<Omit<ResourceConfig, 'networkAdapter' | 'videoLoader' | 'posterLoader' | 'logger' | 'prefetch' | 'scrollThrashing'>> & {
|
|
100
|
+
prefetch: Record<NetworkType, PrefetchConfig>;
|
|
101
|
+
scrollThrashing: ScrollThrashingConfig;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Resource events for external listening
|
|
105
|
+
*/
|
|
106
|
+
type ResourceEvent = {
|
|
107
|
+
type: 'allocationChange';
|
|
108
|
+
toMount: number[];
|
|
109
|
+
toUnmount: number[];
|
|
110
|
+
} | {
|
|
111
|
+
type: 'focusChange';
|
|
112
|
+
index: number;
|
|
113
|
+
previousIndex: number;
|
|
114
|
+
} | {
|
|
115
|
+
type: 'networkChange';
|
|
116
|
+
networkType: NetworkType;
|
|
117
|
+
} | {
|
|
118
|
+
type: 'prefetchRequest';
|
|
119
|
+
indices: number[];
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Resource event listener
|
|
123
|
+
*/
|
|
124
|
+
type ResourceEventListener = (event: ResourceEvent) => void;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Video source getter function type
|
|
128
|
+
* ResourceGovernor needs this to get video sources for preloading
|
|
129
|
+
*/
|
|
130
|
+
type VideoSourceGetter = (index: number) => {
|
|
131
|
+
id: string;
|
|
132
|
+
source: VideoSource;
|
|
133
|
+
poster?: string;
|
|
134
|
+
} | null;
|
|
135
|
+
/**
|
|
136
|
+
* ResourceGovernor - Manages video DOM allocation and prefetch strategy
|
|
137
|
+
*
|
|
138
|
+
* Features:
|
|
139
|
+
* - Sliding window allocation (max 3 video DOM nodes)
|
|
140
|
+
* - Focus debouncing for smooth swipe
|
|
141
|
+
* - Network-aware prefetch strategy
|
|
142
|
+
* - Event system for UI synchronization
|
|
143
|
+
*
|
|
144
|
+
* Memory Strategy:
|
|
145
|
+
* - Only 3 video elements exist in DOM at any time
|
|
146
|
+
* - Sliding window: [previous, current, next]
|
|
147
|
+
* - When user scrolls, we unmount far elements and mount new ones
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```typescript
|
|
151
|
+
* const governor = new ResourceGovernor({ maxAllocations: 3 });
|
|
152
|
+
*
|
|
153
|
+
* // Initialize with feed size
|
|
154
|
+
* governor.setTotalItems(20);
|
|
155
|
+
* governor.activate();
|
|
156
|
+
*
|
|
157
|
+
* // Handle scroll/swipe
|
|
158
|
+
* governor.setFocusedIndex(5); // Debounced
|
|
159
|
+
*
|
|
160
|
+
* // Listen to allocation changes
|
|
161
|
+
* governor.addEventListener((event) => {
|
|
162
|
+
* if (event.type === 'allocationChange') {
|
|
163
|
+
* event.toMount.forEach(i => mountVideoAt(i));
|
|
164
|
+
* event.toUnmount.forEach(i => unmountVideoAt(i));
|
|
165
|
+
* }
|
|
166
|
+
* });
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
declare class ResourceGovernor {
|
|
170
|
+
/** Zustand vanilla store - Single Source of Truth */
|
|
171
|
+
readonly store: StoreApi<ResourceState>;
|
|
172
|
+
/** Resolved configuration */
|
|
173
|
+
private readonly config;
|
|
174
|
+
/** Network adapter */
|
|
175
|
+
private readonly networkAdapter?;
|
|
176
|
+
/** Video loader adapter for preloading video data */
|
|
177
|
+
private readonly videoLoader?;
|
|
178
|
+
/** Poster loader adapter for preloading thumbnails */
|
|
179
|
+
private readonly posterLoader?;
|
|
180
|
+
/** Logger adapter */
|
|
181
|
+
private readonly logger?;
|
|
182
|
+
/** Event listeners */
|
|
183
|
+
private readonly eventListeners;
|
|
184
|
+
/** Focus debounce timer */
|
|
185
|
+
private focusDebounceTimer;
|
|
186
|
+
/** Pending focused index (before debounce completes) */
|
|
187
|
+
private pendingFocusedIndex;
|
|
188
|
+
/** Network change unsubscribe function */
|
|
189
|
+
private networkUnsubscribe?;
|
|
190
|
+
/** Video source getter (injected via setVideoSourceGetter) */
|
|
191
|
+
private videoSourceGetter?;
|
|
192
|
+
/** Scroll thrashing detection - timestamps of recent focus changes */
|
|
193
|
+
private focusChangeTimestamps;
|
|
194
|
+
/** Scroll thrashing cooldown timer */
|
|
195
|
+
private thrashingCooldownTimer;
|
|
196
|
+
constructor(config?: ResourceConfig);
|
|
197
|
+
/**
|
|
198
|
+
* Activate the resource governor
|
|
199
|
+
* Starts network monitoring and performs initial allocation
|
|
200
|
+
*/
|
|
201
|
+
activate(): Promise<void>;
|
|
202
|
+
/**
|
|
203
|
+
* Deactivate the resource governor
|
|
204
|
+
* Stops network monitoring and clears allocations
|
|
205
|
+
*/
|
|
206
|
+
deactivate(): void;
|
|
207
|
+
/**
|
|
208
|
+
* Destroy the resource governor
|
|
209
|
+
*/
|
|
210
|
+
destroy(): void;
|
|
211
|
+
/**
|
|
212
|
+
* Set total number of items in feed
|
|
213
|
+
*/
|
|
214
|
+
setTotalItems(count: number): void;
|
|
215
|
+
/**
|
|
216
|
+
* Set focused index with debouncing
|
|
217
|
+
* This is called during scroll/swipe
|
|
218
|
+
*/
|
|
219
|
+
setFocusedIndex(index: number): void;
|
|
220
|
+
/**
|
|
221
|
+
* Set focused index immediately (skip debounce)
|
|
222
|
+
* Use this for programmatic navigation, not scroll
|
|
223
|
+
*/
|
|
224
|
+
setFocusedIndexImmediate(index: number): void;
|
|
225
|
+
/**
|
|
226
|
+
* Request allocation for given indices
|
|
227
|
+
* Returns what needs to be mounted/unmounted
|
|
228
|
+
*/
|
|
229
|
+
requestAllocation(indices: number[]): AllocationResult;
|
|
230
|
+
/**
|
|
231
|
+
* Get current active allocations
|
|
232
|
+
*/
|
|
233
|
+
getActiveAllocations(): number[];
|
|
234
|
+
/**
|
|
235
|
+
* Check if an index is currently allocated
|
|
236
|
+
*/
|
|
237
|
+
isAllocated(index: number): boolean;
|
|
238
|
+
/**
|
|
239
|
+
* Get current network type
|
|
240
|
+
*/
|
|
241
|
+
getNetworkType(): NetworkType;
|
|
242
|
+
/**
|
|
243
|
+
* Get prefetch configuration for current network
|
|
244
|
+
*/
|
|
245
|
+
getPrefetchConfig(): PrefetchConfig;
|
|
246
|
+
/**
|
|
247
|
+
* Set video source getter function
|
|
248
|
+
* This is called by SDK to provide video data for preloading
|
|
249
|
+
*
|
|
250
|
+
* @param getter - Function that returns video info for a given index
|
|
251
|
+
*/
|
|
252
|
+
setVideoSourceGetter(getter: VideoSourceGetter): void;
|
|
253
|
+
/**
|
|
254
|
+
* Check if preloading is currently throttled due to scroll thrashing
|
|
255
|
+
*/
|
|
256
|
+
isPreloadThrottled(): boolean;
|
|
257
|
+
/**
|
|
258
|
+
* Get indices currently being preloaded
|
|
259
|
+
*/
|
|
260
|
+
getPreloadingIndices(): number[];
|
|
261
|
+
/**
|
|
262
|
+
* Manually trigger preload for specific indices
|
|
263
|
+
* Respects throttling and network conditions
|
|
264
|
+
*/
|
|
265
|
+
triggerPreload(indices: number[]): Promise<void>;
|
|
266
|
+
/**
|
|
267
|
+
* Add event listener
|
|
268
|
+
*/
|
|
269
|
+
addEventListener(listener: ResourceEventListener): () => void;
|
|
270
|
+
/**
|
|
271
|
+
* Remove event listener
|
|
272
|
+
*/
|
|
273
|
+
removeEventListener(listener: ResourceEventListener): void;
|
|
274
|
+
/**
|
|
275
|
+
* Initialize network detection
|
|
276
|
+
*/
|
|
277
|
+
private initializeNetwork;
|
|
278
|
+
/**
|
|
279
|
+
* Perform allocation for a given focused index
|
|
280
|
+
*/
|
|
281
|
+
private performAllocation;
|
|
282
|
+
/**
|
|
283
|
+
* Update prefetch queue based on current state
|
|
284
|
+
*/
|
|
285
|
+
private updatePrefetchQueue;
|
|
286
|
+
/**
|
|
287
|
+
* Track focus change timestamp for scroll thrashing detection
|
|
288
|
+
*/
|
|
289
|
+
private trackFocusChange;
|
|
290
|
+
/**
|
|
291
|
+
* Cancel all in-progress preloads
|
|
292
|
+
*/
|
|
293
|
+
private cancelAllPreloads;
|
|
294
|
+
/**
|
|
295
|
+
* Execute video preloading for given indices
|
|
296
|
+
*/
|
|
297
|
+
private executePreload;
|
|
298
|
+
/**
|
|
299
|
+
* Internal helper to preload a single video
|
|
300
|
+
*/
|
|
301
|
+
private preloadOne;
|
|
302
|
+
/**
|
|
303
|
+
* Execute poster preloading for given indices
|
|
304
|
+
*/
|
|
305
|
+
private executePreloadPosters;
|
|
306
|
+
/**
|
|
307
|
+
* Emit event to all listeners
|
|
308
|
+
*/
|
|
309
|
+
private emitEvent;
|
|
310
|
+
}
|
|
311
|
+
|
|
5
312
|
/**
|
|
6
313
|
* Feed state for zustand store
|
|
7
314
|
*/
|
|
@@ -148,7 +455,8 @@ declare const DEFAULT_PREFETCH_CACHE_CONFIG: PrefetchCacheConfig;
|
|
|
148
455
|
* ```
|
|
149
456
|
*/
|
|
150
457
|
declare class FeedManager {
|
|
151
|
-
private
|
|
458
|
+
private dataSource;
|
|
459
|
+
private readonly logger?;
|
|
152
460
|
/** Zustand vanilla store - Single Source of Truth */
|
|
153
461
|
readonly store: StoreApi<FeedState>;
|
|
154
462
|
/** Resolved configuration */
|
|
@@ -169,7 +477,12 @@ declare class FeedManager {
|
|
|
169
477
|
* Used for garbage collection
|
|
170
478
|
*/
|
|
171
479
|
private accessOrder;
|
|
172
|
-
|
|
480
|
+
/**
|
|
481
|
+
* Track videos that have already triggered predictive preload
|
|
482
|
+
* to avoid duplicate requests.
|
|
483
|
+
*/
|
|
484
|
+
private preloadedVideoIds;
|
|
485
|
+
constructor(dataSource: IDataSource, config?: FeedConfig, storage?: IStorage, prefetchConfig?: Partial<PrefetchCacheConfig>, logger?: ILogger | undefined);
|
|
173
486
|
/** Static memory cache for explicit prefetching */
|
|
174
487
|
private static globalMemoryCache;
|
|
175
488
|
/**
|
|
@@ -189,6 +502,21 @@ declare class FeedManager {
|
|
|
189
502
|
* Clear prefetch cache
|
|
190
503
|
*/
|
|
191
504
|
static clearPrefetchCache(): void;
|
|
505
|
+
/**
|
|
506
|
+
* Get current data source
|
|
507
|
+
*/
|
|
508
|
+
getDataSource(): IDataSource;
|
|
509
|
+
/**
|
|
510
|
+
* Update data source dynamically
|
|
511
|
+
* Used for switching between Recommendation and Playlist modes
|
|
512
|
+
*
|
|
513
|
+
* @param dataSource - New data source adapter
|
|
514
|
+
* @param options - Options for state transition
|
|
515
|
+
*/
|
|
516
|
+
setDataSource(dataSource: IDataSource, options?: {
|
|
517
|
+
/** Whether to reset the feed state immediately */
|
|
518
|
+
reset?: boolean;
|
|
519
|
+
}): void;
|
|
192
520
|
/**
|
|
193
521
|
* Load initial feed data
|
|
194
522
|
*
|
|
@@ -201,7 +529,9 @@ declare class FeedManager {
|
|
|
201
529
|
* - If a request for the same cursor is already in-flight, returns the existing Promise
|
|
202
530
|
* - Prevents duplicate API calls from rapid UI interactions
|
|
203
531
|
*/
|
|
204
|
-
loadInitial(
|
|
532
|
+
loadInitial(options?: {
|
|
533
|
+
replace?: boolean;
|
|
534
|
+
}): Promise<void>;
|
|
205
535
|
/**
|
|
206
536
|
* Internal: Execute load initial logic
|
|
207
537
|
*/
|
|
@@ -225,6 +555,15 @@ declare class FeedManager {
|
|
|
225
555
|
* Used when cached data is stale but still shown to user
|
|
226
556
|
*/
|
|
227
557
|
revalidate(): Promise<void>;
|
|
558
|
+
/**
|
|
559
|
+
* Handle playback progress and trigger predictive preloading
|
|
560
|
+
*
|
|
561
|
+
* @param videoId - ID of the currently playing video
|
|
562
|
+
* @param progress - Current playback progress (0-1)
|
|
563
|
+
* @param governor - Resource governor to trigger preload
|
|
564
|
+
* @param threshold - Progress threshold to trigger preload (default: 0.2)
|
|
565
|
+
*/
|
|
566
|
+
handlePlaybackProgress(videoId: string, progress: number, governor: ResourceGovernor, threshold?: number): void;
|
|
228
567
|
/**
|
|
229
568
|
* Get a video by ID
|
|
230
569
|
* Also updates LRU access time for garbage collection
|
|
@@ -238,6 +577,10 @@ declare class FeedManager {
|
|
|
238
577
|
* Update a video in the feed (for optimistic updates)
|
|
239
578
|
*/
|
|
240
579
|
updateVideo(id: string, updates: Partial<VideoItem>): void;
|
|
580
|
+
/**
|
|
581
|
+
* Replace all items in the feed (e.g. for Playlist synchronization)
|
|
582
|
+
*/
|
|
583
|
+
replaceItems(items: VideoItem[]): void;
|
|
241
584
|
/**
|
|
242
585
|
* Remove an item from the feed
|
|
243
586
|
*
|
|
@@ -820,606 +1163,307 @@ declare class PlayerEngine {
|
|
|
820
1163
|
*/
|
|
821
1164
|
private trackCompletion;
|
|
822
1165
|
/**
|
|
823
|
-
* Track when user leaves current video (scrolls to next video)
|
|
824
|
-
* Sends final analytics event before video change
|
|
825
|
-
*/
|
|
826
|
-
private trackLeaveVideo;
|
|
827
|
-
/**
|
|
828
|
-
* Categorize media error
|
|
829
|
-
*/
|
|
830
|
-
private categorizeError;
|
|
831
|
-
/**
|
|
832
|
-
* Get current circuit breaker state
|
|
833
|
-
*/
|
|
834
|
-
getCircuitBreakerState(): CircuitBreakerState;
|
|
835
|
-
/**
|
|
836
|
-
* Check if circuit breaker is open (blocking loads)
|
|
837
|
-
*/
|
|
838
|
-
isCircuitOpen(): boolean;
|
|
839
|
-
/**
|
|
840
|
-
* Manually reset circuit breaker to CLOSED state
|
|
841
|
-
* Useful for user-triggered retry after showing error UI
|
|
842
|
-
*/
|
|
843
|
-
resetCircuitBreaker(): void;
|
|
844
|
-
/**
|
|
845
|
-
* Check if circuit breaker allows load operation
|
|
846
|
-
* Also handles OPEN → HALF_OPEN transition based on timeout
|
|
847
|
-
*/
|
|
848
|
-
private checkCircuitBreaker;
|
|
849
|
-
/**
|
|
850
|
-
* Record a recoverable error for circuit breaker tracking
|
|
851
|
-
*/
|
|
852
|
-
private recordCircuitBreakerError;
|
|
853
|
-
/**
|
|
854
|
-
* Record a successful load for circuit breaker tracking
|
|
855
|
-
*/
|
|
856
|
-
private recordCircuitBreakerSuccess;
|
|
857
|
-
/**
|
|
858
|
-
* Trip the circuit breaker (CLOSED/HALF_OPEN → OPEN)
|
|
859
|
-
*/
|
|
860
|
-
private tripCircuit;
|
|
861
|
-
/**
|
|
862
|
-
* Transition from OPEN to HALF_OPEN
|
|
863
|
-
*/
|
|
864
|
-
private transitionToHalfOpen;
|
|
865
|
-
/**
|
|
866
|
-
* Close the circuit breaker (HALF_OPEN → CLOSED)
|
|
867
|
-
*/
|
|
868
|
-
private closeCircuit;
|
|
869
|
-
/**
|
|
870
|
-
* Schedule automatic transition from OPEN to HALF_OPEN
|
|
871
|
-
*/
|
|
872
|
-
private scheduleHalfOpenTransition;
|
|
873
|
-
/**
|
|
874
|
-
* Clear circuit reset timer
|
|
875
|
-
*/
|
|
876
|
-
private clearCircuitResetTimer;
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
/**
|
|
880
|
-
* Check if a state transition is valid
|
|
881
|
-
*/
|
|
882
|
-
declare function isValidTransition(from: PlayerStatus, to: PlayerStatus): boolean;
|
|
883
|
-
/**
|
|
884
|
-
* Check if player is in a "active" state (can receive playback commands)
|
|
885
|
-
*/
|
|
886
|
-
declare function isActiveState(status: PlayerStatus): boolean;
|
|
887
|
-
/**
|
|
888
|
-
* Check if player can start playback
|
|
889
|
-
*/
|
|
890
|
-
declare function canPlay(status: PlayerStatus): boolean;
|
|
891
|
-
/**
|
|
892
|
-
* Check if player can pause
|
|
893
|
-
*/
|
|
894
|
-
declare function canPause(status: PlayerStatus): boolean;
|
|
895
|
-
/**
|
|
896
|
-
* Check if player can seek
|
|
897
|
-
*/
|
|
898
|
-
declare function canSeek(status: PlayerStatus): boolean;
|
|
899
|
-
|
|
900
|
-
/**
|
|
901
|
-
* Lifecycle state for zustand store
|
|
902
|
-
*/
|
|
903
|
-
interface LifecycleState {
|
|
904
|
-
/** Whether manager is initialized */
|
|
905
|
-
isInitialized: boolean;
|
|
906
|
-
/** Whether there's a pending save operation */
|
|
907
|
-
isSaving: boolean;
|
|
908
|
-
/** Whether there's a pending restore operation */
|
|
909
|
-
isRestoring: boolean;
|
|
910
|
-
/** Last saved timestamp */
|
|
911
|
-
lastSavedAt: number | null;
|
|
912
|
-
/** Last restored timestamp */
|
|
913
|
-
lastRestoredAt: number | null;
|
|
914
|
-
/** Whether the restored snapshot needs revalidation */
|
|
915
|
-
needsRevalidation: boolean;
|
|
916
|
-
/** Current visibility state */
|
|
917
|
-
visibilityState: DocumentVisibilityState;
|
|
918
|
-
}
|
|
919
|
-
/**
|
|
920
|
-
* Restore result
|
|
921
|
-
*/
|
|
922
|
-
interface RestoreResult {
|
|
923
|
-
/** Whether restore was successful */
|
|
924
|
-
success: boolean;
|
|
925
|
-
/** Restored snapshot data (null if no valid snapshot) */
|
|
926
|
-
snapshot: SessionSnapshot | null;
|
|
927
|
-
/** Whether the restored data is stale and needs revalidation */
|
|
928
|
-
needsRevalidation: boolean;
|
|
929
|
-
/** Reason for failure (if any) */
|
|
930
|
-
reason?: 'no_snapshot' | 'expired' | 'invalid' | 'error';
|
|
931
|
-
/** Playback time in seconds (only present if restorePlaybackPosition config is enabled) */
|
|
932
|
-
playbackTime?: number;
|
|
933
|
-
/** Video ID that was playing (only present if restorePlaybackPosition config is enabled) */
|
|
934
|
-
currentVideoId?: string;
|
|
935
|
-
/** Captured video frame at playback position (base64 JPEG, only present if restorePlaybackPosition is enabled) */
|
|
936
|
-
restoreFrame?: string;
|
|
937
|
-
}
|
|
938
|
-
/**
|
|
939
|
-
* LifecycleManager configuration
|
|
940
|
-
*/
|
|
941
|
-
interface LifecycleConfig {
|
|
942
|
-
/** Storage adapter for persistence */
|
|
943
|
-
storage?: ISessionStorage;
|
|
944
|
-
/** Logger adapter */
|
|
945
|
-
logger?: ILogger;
|
|
946
|
-
/** Snapshot expiry time in ms (default: 24 hours) */
|
|
947
|
-
snapshotExpiryMs?: number;
|
|
948
|
-
/** Revalidation threshold in ms (default: 5 minutes) */
|
|
949
|
-
revalidationThresholdMs?: number;
|
|
950
|
-
/** Auto-save on visibility change (default: true) */
|
|
951
|
-
autoSaveOnHidden?: boolean;
|
|
952
|
-
/**
|
|
953
|
-
* Enable restoring video playback position (default: false)
|
|
954
|
-
* When enabled, saves and restores the exact playback time (seconds)
|
|
955
|
-
* so video can seek() to the exact position user was watching
|
|
956
|
-
*/
|
|
957
|
-
restorePlaybackPosition?: boolean;
|
|
958
|
-
/** SDK version for compatibility */
|
|
959
|
-
version?: string;
|
|
960
|
-
}
|
|
961
|
-
/**
|
|
962
|
-
* Default lifecycle configuration
|
|
963
|
-
*/
|
|
964
|
-
declare const DEFAULT_LIFECYCLE_CONFIG: Required<Omit<LifecycleConfig, 'storage' | 'logger'>>;
|
|
965
|
-
/**
|
|
966
|
-
* Lifecycle events for external listening
|
|
967
|
-
*/
|
|
968
|
-
type LifecycleEvent = {
|
|
969
|
-
type: 'saveStart';
|
|
970
|
-
} | {
|
|
971
|
-
type: 'saveComplete';
|
|
972
|
-
timestamp: number;
|
|
973
|
-
} | {
|
|
974
|
-
type: 'saveFailed';
|
|
975
|
-
error: Error;
|
|
976
|
-
} | {
|
|
977
|
-
type: 'restoreStart';
|
|
978
|
-
} | {
|
|
979
|
-
type: 'restoreComplete';
|
|
980
|
-
result: RestoreResult;
|
|
981
|
-
} | {
|
|
982
|
-
type: 'visibilityChange';
|
|
983
|
-
state: DocumentVisibilityState;
|
|
984
|
-
};
|
|
985
|
-
/**
|
|
986
|
-
* Lifecycle event listener
|
|
987
|
-
*/
|
|
988
|
-
type LifecycleEventListener = (event: LifecycleEvent) => void;
|
|
989
|
-
|
|
990
|
-
/**
|
|
991
|
-
* LifecycleManager - Handles session persistence and restoration
|
|
992
|
-
*
|
|
993
|
-
* Strategy: "State Persistence, DOM Destruction"
|
|
994
|
-
* - Save snapshot to storage when user leaves
|
|
995
|
-
* - Restore state from storage when user returns
|
|
996
|
-
* - Video DOM nodes are destroyed to free RAM
|
|
997
|
-
*
|
|
998
|
-
* Features:
|
|
999
|
-
* - Auto-save on visibility change (optional)
|
|
1000
|
-
* - Snapshot expiry (24 hours default)
|
|
1001
|
-
* - Stale data detection for revalidation
|
|
1002
|
-
* - Event system for UI coordination
|
|
1003
|
-
*
|
|
1004
|
-
* @example
|
|
1005
|
-
* ```typescript
|
|
1006
|
-
* const lifecycle = new LifecycleManager({ storage, logger });
|
|
1007
|
-
*
|
|
1008
|
-
* // Initialize and attempt restore
|
|
1009
|
-
* const result = await lifecycle.initialize();
|
|
1010
|
-
* if (result.success) {
|
|
1011
|
-
* feedManager.restoreFromSnapshot(result.snapshot);
|
|
1012
|
-
* if (result.needsRevalidation) {
|
|
1013
|
-
* feedManager.revalidate();
|
|
1014
|
-
* }
|
|
1015
|
-
* }
|
|
1016
|
-
*
|
|
1017
|
-
* // Save snapshot before leaving
|
|
1018
|
-
* lifecycle.saveSnapshot({
|
|
1019
|
-
* items: feedManager.getVideos(),
|
|
1020
|
-
* cursor: feedManager.store.getState().cursor,
|
|
1021
|
-
* focusedIndex: resourceGovernor.store.getState().focusedIndex,
|
|
1022
|
-
* });
|
|
1023
|
-
* ```
|
|
1024
|
-
*/
|
|
1025
|
-
declare class LifecycleManager {
|
|
1026
|
-
/** Zustand vanilla store - Single Source of Truth */
|
|
1027
|
-
readonly store: StoreApi<LifecycleState>;
|
|
1028
|
-
/** Resolved configuration */
|
|
1029
|
-
private readonly config;
|
|
1030
|
-
/** Storage adapter */
|
|
1031
|
-
private readonly storage?;
|
|
1032
|
-
/** Logger adapter */
|
|
1033
|
-
private readonly logger?;
|
|
1034
|
-
/** Event listeners */
|
|
1035
|
-
private readonly eventListeners;
|
|
1036
|
-
/** Visibility change handler reference (for cleanup) */
|
|
1037
|
-
private visibilityHandler?;
|
|
1038
|
-
/** Pending save data (for debouncing) */
|
|
1039
|
-
private pendingSaveData?;
|
|
1040
|
-
constructor(config?: LifecycleConfig);
|
|
1041
|
-
/**
|
|
1042
|
-
* Initialize lifecycle manager and attempt to restore session
|
|
1043
|
-
*/
|
|
1044
|
-
initialize(): Promise<RestoreResult>;
|
|
1045
|
-
/**
|
|
1046
|
-
* Destroy lifecycle manager and cleanup
|
|
1047
|
-
*/
|
|
1048
|
-
destroy(): void;
|
|
1049
|
-
/**
|
|
1050
|
-
* Restore session from storage
|
|
1051
|
-
*/
|
|
1052
|
-
restoreSession(): Promise<RestoreResult>;
|
|
1053
|
-
/**
|
|
1054
|
-
* Save session snapshot to storage
|
|
1055
|
-
*
|
|
1056
|
-
* @param data - Snapshot data to save
|
|
1057
|
-
* @param data.playbackTime - Current video playback position (only saved if restorePlaybackPosition config is enabled)
|
|
1058
|
-
* @param data.currentVideoId - Current video ID (only saved if restorePlaybackPosition config is enabled)
|
|
1059
|
-
* @param data.restoreFrame - Captured video frame at playback position (only saved if restorePlaybackPosition is enabled)
|
|
1166
|
+
* Track when user leaves current video (scrolls to next video)
|
|
1167
|
+
* Sends final analytics event before video change
|
|
1060
1168
|
*/
|
|
1061
|
-
|
|
1062
|
-
items: VideoItem[];
|
|
1063
|
-
cursor: string | null;
|
|
1064
|
-
focusedIndex: number;
|
|
1065
|
-
scrollPosition?: number;
|
|
1066
|
-
/** Current video playback time in seconds (only used when restorePlaybackPosition is enabled) */
|
|
1067
|
-
playbackTime?: number;
|
|
1068
|
-
/** Current video ID (only used when restorePlaybackPosition is enabled) */
|
|
1069
|
-
currentVideoId?: string;
|
|
1070
|
-
/** Captured video frame at playback position (only used when restorePlaybackPosition is enabled) */
|
|
1071
|
-
restoreFrame?: string;
|
|
1072
|
-
}): Promise<boolean>;
|
|
1169
|
+
private trackLeaveVideo;
|
|
1073
1170
|
/**
|
|
1074
|
-
*
|
|
1171
|
+
* Categorize media error
|
|
1075
1172
|
*/
|
|
1076
|
-
|
|
1173
|
+
private categorizeError;
|
|
1077
1174
|
/**
|
|
1078
|
-
*
|
|
1079
|
-
*
|
|
1080
|
-
* @param data - Data to save when flush is called
|
|
1175
|
+
* Get current circuit breaker state
|
|
1081
1176
|
*/
|
|
1082
|
-
|
|
1177
|
+
getCircuitBreakerState(): CircuitBreakerState;
|
|
1083
1178
|
/**
|
|
1084
|
-
* Check if
|
|
1085
|
-
* SDK can use this to decide whether to collect playbackTime
|
|
1179
|
+
* Check if circuit breaker is open (blocking loads)
|
|
1086
1180
|
*/
|
|
1087
|
-
|
|
1181
|
+
isCircuitOpen(): boolean;
|
|
1088
1182
|
/**
|
|
1089
|
-
*
|
|
1183
|
+
* Manually reset circuit breaker to CLOSED state
|
|
1184
|
+
* Useful for user-triggered retry after showing error UI
|
|
1090
1185
|
*/
|
|
1091
|
-
|
|
1186
|
+
resetCircuitBreaker(): void;
|
|
1092
1187
|
/**
|
|
1093
|
-
*
|
|
1188
|
+
* Check if circuit breaker allows load operation
|
|
1189
|
+
* Also handles OPEN → HALF_OPEN transition based on timeout
|
|
1094
1190
|
*/
|
|
1095
|
-
|
|
1191
|
+
private checkCircuitBreaker;
|
|
1096
1192
|
/**
|
|
1097
|
-
*
|
|
1193
|
+
* Record a recoverable error for circuit breaker tracking
|
|
1098
1194
|
*/
|
|
1099
|
-
|
|
1195
|
+
private recordCircuitBreakerError;
|
|
1100
1196
|
/**
|
|
1101
|
-
*
|
|
1197
|
+
* Record a successful load for circuit breaker tracking
|
|
1102
1198
|
*/
|
|
1103
|
-
|
|
1199
|
+
private recordCircuitBreakerSuccess;
|
|
1104
1200
|
/**
|
|
1105
|
-
*
|
|
1201
|
+
* Trip the circuit breaker (CLOSED/HALF_OPEN → OPEN)
|
|
1106
1202
|
*/
|
|
1107
|
-
|
|
1203
|
+
private tripCircuit;
|
|
1108
1204
|
/**
|
|
1109
|
-
*
|
|
1205
|
+
* Transition from OPEN to HALF_OPEN
|
|
1110
1206
|
*/
|
|
1111
|
-
private
|
|
1207
|
+
private transitionToHalfOpen;
|
|
1112
1208
|
/**
|
|
1113
|
-
*
|
|
1209
|
+
* Close the circuit breaker (HALF_OPEN → CLOSED)
|
|
1114
1210
|
*/
|
|
1115
|
-
private
|
|
1211
|
+
private closeCircuit;
|
|
1116
1212
|
/**
|
|
1117
|
-
*
|
|
1213
|
+
* Schedule automatic transition from OPEN to HALF_OPEN
|
|
1118
1214
|
*/
|
|
1119
|
-
private
|
|
1215
|
+
private scheduleHalfOpenTransition;
|
|
1120
1216
|
/**
|
|
1121
|
-
*
|
|
1217
|
+
* Clear circuit reset timer
|
|
1122
1218
|
*/
|
|
1123
|
-
private
|
|
1219
|
+
private clearCircuitResetTimer;
|
|
1124
1220
|
}
|
|
1125
1221
|
|
|
1126
1222
|
/**
|
|
1127
|
-
*
|
|
1128
|
-
* Simplified from contracts NetworkType for prefetch decisions
|
|
1223
|
+
* Check if a state transition is valid
|
|
1129
1224
|
*/
|
|
1130
|
-
|
|
1225
|
+
declare function isValidTransition(from: PlayerStatus, to: PlayerStatus): boolean;
|
|
1131
1226
|
/**
|
|
1132
|
-
*
|
|
1227
|
+
* Check if player is in a "active" state (can receive playback commands)
|
|
1133
1228
|
*/
|
|
1134
|
-
declare function
|
|
1229
|
+
declare function isActiveState(status: PlayerStatus): boolean;
|
|
1135
1230
|
/**
|
|
1136
|
-
*
|
|
1231
|
+
* Check if player can start playback
|
|
1137
1232
|
*/
|
|
1138
|
-
|
|
1139
|
-
/** Currently allocated video slot indices (max 3) */
|
|
1140
|
-
activeAllocations: Set<number>;
|
|
1141
|
-
/** Queue of indices to preload */
|
|
1142
|
-
preloadQueue: number[];
|
|
1143
|
-
/** Currently focused/playing video index */
|
|
1144
|
-
focusedIndex: number;
|
|
1145
|
-
/** Current network type */
|
|
1146
|
-
networkType: NetworkType;
|
|
1147
|
-
/** Total number of items in feed (for bounds checking) */
|
|
1148
|
-
totalItems: number;
|
|
1149
|
-
/** Whether resource governor is active */
|
|
1150
|
-
isActive: boolean;
|
|
1151
|
-
/** Whether preloading is throttled due to scroll thrashing */
|
|
1152
|
-
isThrottled: boolean;
|
|
1153
|
-
/** Indices currently being preloaded */
|
|
1154
|
-
preloadingIndices: Set<number>;
|
|
1155
|
-
}
|
|
1233
|
+
declare function canPlay(status: PlayerStatus): boolean;
|
|
1156
1234
|
/**
|
|
1157
|
-
*
|
|
1235
|
+
* Check if player can pause
|
|
1158
1236
|
*/
|
|
1159
|
-
|
|
1160
|
-
/** Indices that should mount video elements */
|
|
1161
|
-
toMount: number[];
|
|
1162
|
-
/** Indices that should unmount video elements */
|
|
1163
|
-
toUnmount: number[];
|
|
1164
|
-
/** Currently active allocations after this operation */
|
|
1165
|
-
activeAllocations: number[];
|
|
1166
|
-
/** Whether focus change was successful */
|
|
1167
|
-
success: boolean;
|
|
1168
|
-
}
|
|
1237
|
+
declare function canPause(status: PlayerStatus): boolean;
|
|
1169
1238
|
/**
|
|
1170
|
-
*
|
|
1239
|
+
* Check if player can seek
|
|
1171
1240
|
*/
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1241
|
+
declare function canSeek(status: PlayerStatus): boolean;
|
|
1242
|
+
|
|
1243
|
+
/**
|
|
1244
|
+
* Lifecycle state for zustand store
|
|
1245
|
+
*/
|
|
1246
|
+
interface LifecycleState {
|
|
1247
|
+
/** Whether manager is initialized */
|
|
1248
|
+
isInitialized: boolean;
|
|
1249
|
+
/** Whether there's a pending save operation */
|
|
1250
|
+
isSaving: boolean;
|
|
1251
|
+
/** Whether there's a pending restore operation */
|
|
1252
|
+
isRestoring: boolean;
|
|
1253
|
+
/** Last saved timestamp */
|
|
1254
|
+
lastSavedAt: number | null;
|
|
1255
|
+
/** Last restored timestamp */
|
|
1256
|
+
lastRestoredAt: number | null;
|
|
1257
|
+
/** Whether the restored snapshot needs revalidation */
|
|
1258
|
+
needsRevalidation: boolean;
|
|
1259
|
+
/** Current visibility state */
|
|
1260
|
+
visibilityState: DocumentVisibilityState;
|
|
1179
1261
|
}
|
|
1180
1262
|
/**
|
|
1181
|
-
*
|
|
1182
|
-
* Prevents excessive preloading when user scrolls too fast
|
|
1263
|
+
* Restore result
|
|
1183
1264
|
*/
|
|
1184
|
-
interface
|
|
1185
|
-
/**
|
|
1186
|
-
|
|
1187
|
-
/**
|
|
1188
|
-
|
|
1189
|
-
/**
|
|
1190
|
-
|
|
1265
|
+
interface RestoreResult {
|
|
1266
|
+
/** Whether restore was successful */
|
|
1267
|
+
success: boolean;
|
|
1268
|
+
/** Restored snapshot data (null if no valid snapshot) */
|
|
1269
|
+
snapshot: SessionSnapshot | null;
|
|
1270
|
+
/** Whether the restored data is stale and needs revalidation */
|
|
1271
|
+
needsRevalidation: boolean;
|
|
1272
|
+
/** Reason for failure (if any) */
|
|
1273
|
+
reason?: 'no_snapshot' | 'expired' | 'invalid' | 'error';
|
|
1274
|
+
/** Playback time in seconds (only present if restorePlaybackPosition config is enabled) */
|
|
1275
|
+
playbackTime?: number;
|
|
1276
|
+
/** Video ID that was playing (only present if restorePlaybackPosition config is enabled) */
|
|
1277
|
+
currentVideoId?: string;
|
|
1278
|
+
/** Captured video frame at playback position (base64 JPEG, only present if restorePlaybackPosition is enabled) */
|
|
1279
|
+
restoreFrame?: string;
|
|
1191
1280
|
}
|
|
1192
1281
|
/**
|
|
1193
|
-
*
|
|
1282
|
+
* LifecycleManager configuration
|
|
1194
1283
|
*/
|
|
1195
|
-
interface
|
|
1196
|
-
/**
|
|
1197
|
-
|
|
1198
|
-
/** Focus debounce time in ms (default: 150) */
|
|
1199
|
-
focusDebounceMs?: number;
|
|
1200
|
-
/** Prefetch configuration overrides by network type */
|
|
1201
|
-
prefetch?: Partial<Record<NetworkType, Partial<PrefetchConfig>>>;
|
|
1202
|
-
/** Scroll thrashing configuration */
|
|
1203
|
-
scrollThrashing?: Partial<ScrollThrashingConfig>;
|
|
1204
|
-
/** Network adapter for detecting connection type */
|
|
1205
|
-
networkAdapter?: INetworkAdapter;
|
|
1206
|
-
/** Video loader adapter for preloading video data */
|
|
1207
|
-
videoLoader?: IVideoLoader;
|
|
1208
|
-
/** Poster loader adapter for preloading thumbnails */
|
|
1209
|
-
posterLoader?: IPosterLoader;
|
|
1284
|
+
interface LifecycleConfig {
|
|
1285
|
+
/** Storage adapter for persistence */
|
|
1286
|
+
storage?: ISessionStorage;
|
|
1210
1287
|
/** Logger adapter */
|
|
1211
1288
|
logger?: ILogger;
|
|
1289
|
+
/** Snapshot expiry time in ms (default: 24 hours) */
|
|
1290
|
+
snapshotExpiryMs?: number;
|
|
1291
|
+
/** Revalidation threshold in ms (default: 5 minutes) */
|
|
1292
|
+
revalidationThresholdMs?: number;
|
|
1293
|
+
/** Auto-save on visibility change (default: true) */
|
|
1294
|
+
autoSaveOnHidden?: boolean;
|
|
1295
|
+
/**
|
|
1296
|
+
* Enable restoring video playback position (default: false)
|
|
1297
|
+
* When enabled, saves and restores the exact playback time (seconds)
|
|
1298
|
+
* so video can seek() to the exact position user was watching
|
|
1299
|
+
*/
|
|
1300
|
+
restorePlaybackPosition?: boolean;
|
|
1301
|
+
/** SDK version for compatibility */
|
|
1302
|
+
version?: string;
|
|
1212
1303
|
}
|
|
1213
1304
|
/**
|
|
1214
|
-
* Default
|
|
1215
|
-
*/
|
|
1216
|
-
declare const DEFAULT_PREFETCH_CONFIG: Record<NetworkType, PrefetchConfig>;
|
|
1217
|
-
/**
|
|
1218
|
-
* Default resource configuration
|
|
1305
|
+
* Default lifecycle configuration
|
|
1219
1306
|
*/
|
|
1220
|
-
declare const
|
|
1221
|
-
prefetch: Record<NetworkType, PrefetchConfig>;
|
|
1222
|
-
scrollThrashing: ScrollThrashingConfig;
|
|
1223
|
-
};
|
|
1307
|
+
declare const DEFAULT_LIFECYCLE_CONFIG: Required<Omit<LifecycleConfig, 'storage' | 'logger'>>;
|
|
1224
1308
|
/**
|
|
1225
|
-
*
|
|
1309
|
+
* Lifecycle events for external listening
|
|
1226
1310
|
*/
|
|
1227
|
-
type
|
|
1228
|
-
type: '
|
|
1229
|
-
toMount: number[];
|
|
1230
|
-
toUnmount: number[];
|
|
1311
|
+
type LifecycleEvent = {
|
|
1312
|
+
type: 'saveStart';
|
|
1231
1313
|
} | {
|
|
1232
|
-
type: '
|
|
1233
|
-
|
|
1234
|
-
previousIndex: number;
|
|
1314
|
+
type: 'saveComplete';
|
|
1315
|
+
timestamp: number;
|
|
1235
1316
|
} | {
|
|
1236
|
-
type: '
|
|
1237
|
-
|
|
1317
|
+
type: 'saveFailed';
|
|
1318
|
+
error: Error;
|
|
1238
1319
|
} | {
|
|
1239
|
-
type: '
|
|
1240
|
-
|
|
1320
|
+
type: 'restoreStart';
|
|
1321
|
+
} | {
|
|
1322
|
+
type: 'restoreComplete';
|
|
1323
|
+
result: RestoreResult;
|
|
1324
|
+
} | {
|
|
1325
|
+
type: 'visibilityChange';
|
|
1326
|
+
state: DocumentVisibilityState;
|
|
1241
1327
|
};
|
|
1242
1328
|
/**
|
|
1243
|
-
*
|
|
1329
|
+
* Lifecycle event listener
|
|
1244
1330
|
*/
|
|
1245
|
-
type
|
|
1331
|
+
type LifecycleEventListener = (event: LifecycleEvent) => void;
|
|
1246
1332
|
|
|
1247
1333
|
/**
|
|
1248
|
-
*
|
|
1249
|
-
* ResourceGovernor needs this to get video sources for preloading
|
|
1250
|
-
*/
|
|
1251
|
-
type VideoSourceGetter = (index: number) => {
|
|
1252
|
-
id: string;
|
|
1253
|
-
source: VideoSource;
|
|
1254
|
-
poster?: string;
|
|
1255
|
-
} | null;
|
|
1256
|
-
/**
|
|
1257
|
-
* ResourceGovernor - Manages video DOM allocation and prefetch strategy
|
|
1334
|
+
* LifecycleManager - Handles session persistence and restoration
|
|
1258
1335
|
*
|
|
1259
|
-
*
|
|
1260
|
-
* -
|
|
1261
|
-
* -
|
|
1262
|
-
* -
|
|
1263
|
-
* - Event system for UI synchronization
|
|
1336
|
+
* Strategy: "State Persistence, DOM Destruction"
|
|
1337
|
+
* - Save snapshot to storage when user leaves
|
|
1338
|
+
* - Restore state from storage when user returns
|
|
1339
|
+
* - Video DOM nodes are destroyed to free RAM
|
|
1264
1340
|
*
|
|
1265
|
-
*
|
|
1266
|
-
* -
|
|
1267
|
-
* -
|
|
1268
|
-
* -
|
|
1341
|
+
* Features:
|
|
1342
|
+
* - Auto-save on visibility change (optional)
|
|
1343
|
+
* - Snapshot expiry (24 hours default)
|
|
1344
|
+
* - Stale data detection for revalidation
|
|
1345
|
+
* - Event system for UI coordination
|
|
1269
1346
|
*
|
|
1270
1347
|
* @example
|
|
1271
1348
|
* ```typescript
|
|
1272
|
-
* const
|
|
1273
|
-
*
|
|
1274
|
-
* // Initialize with feed size
|
|
1275
|
-
* governor.setTotalItems(20);
|
|
1276
|
-
* governor.activate();
|
|
1277
|
-
*
|
|
1278
|
-
* // Handle scroll/swipe
|
|
1279
|
-
* governor.setFocusedIndex(5); // Debounced
|
|
1349
|
+
* const lifecycle = new LifecycleManager({ storage, logger });
|
|
1280
1350
|
*
|
|
1281
|
-
* //
|
|
1282
|
-
*
|
|
1283
|
-
*
|
|
1284
|
-
*
|
|
1285
|
-
*
|
|
1351
|
+
* // Initialize and attempt restore
|
|
1352
|
+
* const result = await lifecycle.initialize();
|
|
1353
|
+
* if (result.success) {
|
|
1354
|
+
* feedManager.restoreFromSnapshot(result.snapshot);
|
|
1355
|
+
* if (result.needsRevalidation) {
|
|
1356
|
+
* feedManager.revalidate();
|
|
1286
1357
|
* }
|
|
1358
|
+
* }
|
|
1359
|
+
*
|
|
1360
|
+
* // Save snapshot before leaving
|
|
1361
|
+
* lifecycle.saveSnapshot({
|
|
1362
|
+
* items: feedManager.getVideos(),
|
|
1363
|
+
* cursor: feedManager.store.getState().cursor,
|
|
1364
|
+
* focusedIndex: resourceGovernor.store.getState().focusedIndex,
|
|
1287
1365
|
* });
|
|
1288
1366
|
* ```
|
|
1289
1367
|
*/
|
|
1290
|
-
declare class
|
|
1368
|
+
declare class LifecycleManager {
|
|
1291
1369
|
/** Zustand vanilla store - Single Source of Truth */
|
|
1292
|
-
readonly store: StoreApi<
|
|
1370
|
+
readonly store: StoreApi<LifecycleState>;
|
|
1293
1371
|
/** Resolved configuration */
|
|
1294
1372
|
private readonly config;
|
|
1295
|
-
/**
|
|
1296
|
-
private readonly
|
|
1297
|
-
/** Video loader adapter for preloading video data */
|
|
1298
|
-
private readonly videoLoader?;
|
|
1299
|
-
/** Poster loader adapter for preloading thumbnails */
|
|
1300
|
-
private readonly posterLoader?;
|
|
1373
|
+
/** Storage adapter */
|
|
1374
|
+
private readonly storage?;
|
|
1301
1375
|
/** Logger adapter */
|
|
1302
1376
|
private readonly logger?;
|
|
1303
1377
|
/** Event listeners */
|
|
1304
1378
|
private readonly eventListeners;
|
|
1305
|
-
/**
|
|
1306
|
-
private
|
|
1307
|
-
/** Pending
|
|
1308
|
-
private
|
|
1309
|
-
|
|
1310
|
-
private networkUnsubscribe?;
|
|
1311
|
-
/** Video source getter (injected via setVideoSourceGetter) */
|
|
1312
|
-
private videoSourceGetter?;
|
|
1313
|
-
/** Scroll thrashing detection - timestamps of recent focus changes */
|
|
1314
|
-
private focusChangeTimestamps;
|
|
1315
|
-
/** Scroll thrashing cooldown timer */
|
|
1316
|
-
private thrashingCooldownTimer;
|
|
1317
|
-
constructor(config?: ResourceConfig);
|
|
1318
|
-
/**
|
|
1319
|
-
* Activate the resource governor
|
|
1320
|
-
* Starts network monitoring and performs initial allocation
|
|
1321
|
-
*/
|
|
1322
|
-
activate(): Promise<void>;
|
|
1379
|
+
/** Visibility change handler reference (for cleanup) */
|
|
1380
|
+
private visibilityHandler?;
|
|
1381
|
+
/** Pending save data (for debouncing) */
|
|
1382
|
+
private pendingSaveData?;
|
|
1383
|
+
constructor(config?: LifecycleConfig);
|
|
1323
1384
|
/**
|
|
1324
|
-
*
|
|
1325
|
-
* Stops network monitoring and clears allocations
|
|
1385
|
+
* Initialize lifecycle manager and attempt to restore session
|
|
1326
1386
|
*/
|
|
1327
|
-
|
|
1387
|
+
initialize(): Promise<RestoreResult>;
|
|
1328
1388
|
/**
|
|
1329
|
-
* Destroy
|
|
1389
|
+
* Destroy lifecycle manager and cleanup
|
|
1330
1390
|
*/
|
|
1331
1391
|
destroy(): void;
|
|
1332
1392
|
/**
|
|
1333
|
-
*
|
|
1334
|
-
*/
|
|
1335
|
-
setTotalItems(count: number): void;
|
|
1336
|
-
/**
|
|
1337
|
-
* Set focused index with debouncing
|
|
1338
|
-
* This is called during scroll/swipe
|
|
1339
|
-
*/
|
|
1340
|
-
setFocusedIndex(index: number): void;
|
|
1341
|
-
/**
|
|
1342
|
-
* Set focused index immediately (skip debounce)
|
|
1343
|
-
* Use this for programmatic navigation, not scroll
|
|
1344
|
-
*/
|
|
1345
|
-
setFocusedIndexImmediate(index: number): void;
|
|
1346
|
-
/**
|
|
1347
|
-
* Request allocation for given indices
|
|
1348
|
-
* Returns what needs to be mounted/unmounted
|
|
1349
|
-
*/
|
|
1350
|
-
requestAllocation(indices: number[]): AllocationResult;
|
|
1351
|
-
/**
|
|
1352
|
-
* Get current active allocations
|
|
1393
|
+
* Restore session from storage
|
|
1353
1394
|
*/
|
|
1354
|
-
|
|
1395
|
+
restoreSession(): Promise<RestoreResult>;
|
|
1355
1396
|
/**
|
|
1356
|
-
*
|
|
1397
|
+
* Save session snapshot to storage
|
|
1398
|
+
*
|
|
1399
|
+
* @param data - Snapshot data to save
|
|
1400
|
+
* @param data.playbackTime - Current video playback position (only saved if restorePlaybackPosition config is enabled)
|
|
1401
|
+
* @param data.currentVideoId - Current video ID (only saved if restorePlaybackPosition config is enabled)
|
|
1402
|
+
* @param data.restoreFrame - Captured video frame at playback position (only saved if restorePlaybackPosition is enabled)
|
|
1357
1403
|
*/
|
|
1358
|
-
|
|
1404
|
+
saveSnapshot(data: {
|
|
1405
|
+
items: VideoItem[];
|
|
1406
|
+
cursor: string | null;
|
|
1407
|
+
focusedIndex: number;
|
|
1408
|
+
scrollPosition?: number;
|
|
1409
|
+
/** Current video playback time in seconds (only used when restorePlaybackPosition is enabled) */
|
|
1410
|
+
playbackTime?: number;
|
|
1411
|
+
/** Current video ID (only used when restorePlaybackPosition is enabled) */
|
|
1412
|
+
currentVideoId?: string;
|
|
1413
|
+
/** Captured video frame at playback position (only used when restorePlaybackPosition is enabled) */
|
|
1414
|
+
restoreFrame?: string;
|
|
1415
|
+
}): Promise<boolean>;
|
|
1359
1416
|
/**
|
|
1360
|
-
*
|
|
1417
|
+
* Clear saved snapshot
|
|
1361
1418
|
*/
|
|
1362
|
-
|
|
1419
|
+
clearSnapshot(): Promise<void>;
|
|
1363
1420
|
/**
|
|
1364
|
-
*
|
|
1421
|
+
* Mark that pending data should be saved (for use with debouncing)
|
|
1422
|
+
*
|
|
1423
|
+
* @param data - Data to save when flush is called
|
|
1365
1424
|
*/
|
|
1366
|
-
|
|
1425
|
+
setPendingSave(data: Omit<SessionSnapshot, 'savedAt' | 'version'>): void;
|
|
1367
1426
|
/**
|
|
1368
|
-
*
|
|
1369
|
-
*
|
|
1370
|
-
*
|
|
1371
|
-
* @param getter - Function that returns video info for a given index
|
|
1427
|
+
* Check if restorePlaybackPosition config is enabled
|
|
1428
|
+
* SDK can use this to decide whether to collect playbackTime
|
|
1372
1429
|
*/
|
|
1373
|
-
|
|
1430
|
+
isPlaybackPositionRestoreEnabled(): boolean;
|
|
1374
1431
|
/**
|
|
1375
|
-
*
|
|
1432
|
+
* Flush pending save (called on visibility hidden)
|
|
1376
1433
|
*/
|
|
1377
|
-
|
|
1434
|
+
flushPendingSave(): Promise<void>;
|
|
1378
1435
|
/**
|
|
1379
|
-
*
|
|
1436
|
+
* Handle visibility state change
|
|
1380
1437
|
*/
|
|
1381
|
-
|
|
1438
|
+
onVisibilityChange(state: DocumentVisibilityState): void;
|
|
1382
1439
|
/**
|
|
1383
|
-
*
|
|
1384
|
-
* Respects throttling and network conditions
|
|
1440
|
+
* Get current visibility state
|
|
1385
1441
|
*/
|
|
1386
|
-
|
|
1442
|
+
getVisibilityState(): DocumentVisibilityState;
|
|
1387
1443
|
/**
|
|
1388
1444
|
* Add event listener
|
|
1389
1445
|
*/
|
|
1390
|
-
addEventListener(listener:
|
|
1446
|
+
addEventListener(listener: LifecycleEventListener): () => void;
|
|
1391
1447
|
/**
|
|
1392
1448
|
* Remove event listener
|
|
1393
1449
|
*/
|
|
1394
|
-
removeEventListener(listener:
|
|
1395
|
-
/**
|
|
1396
|
-
* Initialize network detection
|
|
1397
|
-
*/
|
|
1398
|
-
private initializeNetwork;
|
|
1399
|
-
/**
|
|
1400
|
-
* Perform allocation for a given focused index
|
|
1401
|
-
*/
|
|
1402
|
-
private performAllocation;
|
|
1403
|
-
/**
|
|
1404
|
-
* Update prefetch queue based on current state
|
|
1405
|
-
*/
|
|
1406
|
-
private updatePrefetchQueue;
|
|
1450
|
+
removeEventListener(listener: LifecycleEventListener): void;
|
|
1407
1451
|
/**
|
|
1408
|
-
*
|
|
1452
|
+
* Setup visibility change listener
|
|
1409
1453
|
*/
|
|
1410
|
-
private
|
|
1454
|
+
private setupVisibilityListener;
|
|
1411
1455
|
/**
|
|
1412
|
-
*
|
|
1456
|
+
* Validate snapshot data
|
|
1413
1457
|
*/
|
|
1414
|
-
private
|
|
1458
|
+
private validateSnapshot;
|
|
1415
1459
|
/**
|
|
1416
|
-
*
|
|
1460
|
+
* Check if snapshot is stale (needs revalidation)
|
|
1417
1461
|
*/
|
|
1418
|
-
private
|
|
1462
|
+
private isSnapshotStale;
|
|
1419
1463
|
/**
|
|
1420
|
-
*
|
|
1464
|
+
* Create session snapshot from data
|
|
1421
1465
|
*/
|
|
1422
|
-
private
|
|
1466
|
+
private createSnapshot;
|
|
1423
1467
|
/**
|
|
1424
1468
|
* Emit event to all listeners
|
|
1425
1469
|
*/
|
|
@@ -1959,4 +2003,152 @@ declare class CommentManager {
|
|
|
1959
2003
|
private createError;
|
|
1960
2004
|
}
|
|
1961
2005
|
|
|
1962
|
-
|
|
2006
|
+
/**
|
|
2007
|
+
* Playlist state for zustand store
|
|
2008
|
+
*/
|
|
2009
|
+
interface PlaylistState {
|
|
2010
|
+
/** Currently loaded playlist metadata */
|
|
2011
|
+
playlist: PlaylistData | null;
|
|
2012
|
+
/** Current active item index */
|
|
2013
|
+
currentIndex: number;
|
|
2014
|
+
/** List of items in the playlist (shortcut to playlist.items) */
|
|
2015
|
+
items: ContentItem[];
|
|
2016
|
+
/** Loading state */
|
|
2017
|
+
loading: boolean;
|
|
2018
|
+
/** Error state */
|
|
2019
|
+
error: Error | null;
|
|
2020
|
+
}
|
|
2021
|
+
/**
|
|
2022
|
+
* Configuration for PlaylistManager
|
|
2023
|
+
*/
|
|
2024
|
+
interface PlaylistConfig {
|
|
2025
|
+
/**
|
|
2026
|
+
* Number of items to keep full metadata for around the current index
|
|
2027
|
+
* @default 10
|
|
2028
|
+
*/
|
|
2029
|
+
metadataWindowSize?: number;
|
|
2030
|
+
}
|
|
2031
|
+
/**
|
|
2032
|
+
* Actions for PlaylistManager
|
|
2033
|
+
*/
|
|
2034
|
+
interface PlaylistActions {
|
|
2035
|
+
/** Load a playlist by ID */
|
|
2036
|
+
loadPlaylist: (id: string) => Promise<void>;
|
|
2037
|
+
/** Set a playlist directly */
|
|
2038
|
+
setPlaylist: (playlist: PlaylistData) => void;
|
|
2039
|
+
/** Navigate to next item */
|
|
2040
|
+
next: () => void;
|
|
2041
|
+
/** Navigate to previous item */
|
|
2042
|
+
prev: () => void;
|
|
2043
|
+
/** Jump to specific index */
|
|
2044
|
+
jumpTo: (index: number) => void;
|
|
2045
|
+
/** Reset state */
|
|
2046
|
+
reset: () => void;
|
|
2047
|
+
}
|
|
2048
|
+
/**
|
|
2049
|
+
* State for PlaylistCollectionManager
|
|
2050
|
+
*/
|
|
2051
|
+
interface PlaylistCollectionState {
|
|
2052
|
+
/** Array of playlist summaries */
|
|
2053
|
+
playlists: PlaylistSummary[];
|
|
2054
|
+
/** Whether loading is in progress */
|
|
2055
|
+
loading: boolean;
|
|
2056
|
+
/** Current pagination cursor */
|
|
2057
|
+
cursor: string | null;
|
|
2058
|
+
/** Whether more items can be loaded */
|
|
2059
|
+
hasMore: boolean;
|
|
2060
|
+
/** Error state */
|
|
2061
|
+
error: Error | null;
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
/**
|
|
2065
|
+
* PlaylistManager - Manages playlist state and navigation
|
|
2066
|
+
*
|
|
2067
|
+
* Features:
|
|
2068
|
+
* - Tracks current playlist and active index
|
|
2069
|
+
* - Provides navigation actions (next, prev, jumpTo)
|
|
2070
|
+
* - Uses vanilla Zustand store for state management
|
|
2071
|
+
*/
|
|
2072
|
+
declare class PlaylistManager {
|
|
2073
|
+
private readonly dataSource?;
|
|
2074
|
+
private readonly governor?;
|
|
2075
|
+
/** Zustand vanilla store */
|
|
2076
|
+
readonly store: StoreApi<PlaylistState>;
|
|
2077
|
+
/** Resolved configuration */
|
|
2078
|
+
private readonly config;
|
|
2079
|
+
/**
|
|
2080
|
+
* Internal cache of full metadata items.
|
|
2081
|
+
* Items in store.items may be minified to save memory.
|
|
2082
|
+
*/
|
|
2083
|
+
private fullMetadataItems;
|
|
2084
|
+
/** Unsubscribe from governor events */
|
|
2085
|
+
private governorUnsubscribe?;
|
|
2086
|
+
constructor(dataSource?: IPlaylistDataSource | undefined, config?: PlaylistConfig, governor?: ResourceGovernor | undefined);
|
|
2087
|
+
/**
|
|
2088
|
+
* Load a playlist by ID
|
|
2089
|
+
*/
|
|
2090
|
+
loadPlaylist(id: string): Promise<void>;
|
|
2091
|
+
/**
|
|
2092
|
+
* Set playlist data directly
|
|
2093
|
+
*/
|
|
2094
|
+
setPlaylist(playlist: PlaylistData): void;
|
|
2095
|
+
/**
|
|
2096
|
+
* Navigate to next item
|
|
2097
|
+
*/
|
|
2098
|
+
next(): void;
|
|
2099
|
+
/**
|
|
2100
|
+
* Navigate to previous item
|
|
2101
|
+
*/
|
|
2102
|
+
prev(): void;
|
|
2103
|
+
/**
|
|
2104
|
+
* Jump to specific index
|
|
2105
|
+
*/
|
|
2106
|
+
jumpTo(index: number): void;
|
|
2107
|
+
/**
|
|
2108
|
+
* Reset state
|
|
2109
|
+
*/
|
|
2110
|
+
reset(): void;
|
|
2111
|
+
/**
|
|
2112
|
+
* Destroy the manager
|
|
2113
|
+
*/
|
|
2114
|
+
destroy(): void;
|
|
2115
|
+
/**
|
|
2116
|
+
* Update the sliding window of full metadata items.
|
|
2117
|
+
* Items outside the window are minified to save memory.
|
|
2118
|
+
*/
|
|
2119
|
+
private updateMetadataWindow;
|
|
2120
|
+
/**
|
|
2121
|
+
* Create a minified version of a ContentItem to save memory.
|
|
2122
|
+
* Keeps only essential fields for identification and basic UI.
|
|
2123
|
+
*/
|
|
2124
|
+
private minifyItem;
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
/**
|
|
2128
|
+
* PlaylistCollectionManager - Manages the discovery of playlists.
|
|
2129
|
+
*
|
|
2130
|
+
* Responsibilities:
|
|
2131
|
+
* - Fetching playlist summaries from DataSource
|
|
2132
|
+
* - Handling pagination and cursor management
|
|
2133
|
+
* - Maintaining loading and error states
|
|
2134
|
+
*/
|
|
2135
|
+
declare class PlaylistCollectionManager {
|
|
2136
|
+
private readonly dataSource?;
|
|
2137
|
+
/** Zustand vanilla store */
|
|
2138
|
+
readonly store: StoreApi<PlaylistCollectionState>;
|
|
2139
|
+
constructor(dataSource?: IPlaylistDataSource | undefined);
|
|
2140
|
+
/**
|
|
2141
|
+
* Load more playlists (pagination)
|
|
2142
|
+
*/
|
|
2143
|
+
loadMore(): Promise<void>;
|
|
2144
|
+
/**
|
|
2145
|
+
* Refresh the collection (reset and re-fetch)
|
|
2146
|
+
*/
|
|
2147
|
+
refresh(): Promise<void>;
|
|
2148
|
+
/**
|
|
2149
|
+
* Reset the store to initial state
|
|
2150
|
+
*/
|
|
2151
|
+
reset(): void;
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
export { type ActionType, type AllocationResult, type CommentError, CommentManager, type CommentManagerConfig, type CommentManagerState, DEFAULT_COMMENT_MANAGER_CONFIG, DEFAULT_FEED_CONFIG, DEFAULT_LIFECYCLE_CONFIG, DEFAULT_OPTIMISTIC_CONFIG, DEFAULT_PLAYER_CONFIG, DEFAULT_PREFETCH_CACHE_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 OptimisticComment, type OptimisticConfig, type OptimisticEvent, type OptimisticEventListener, OptimisticManager, type OptimisticReply, type OptimisticState, type PendingAction, type PlayerConfig, PlayerEngine, type PlayerError, type PlayerEvent, type PlayerEventListener, type PlayerState, PlayerStatus, type PlaylistActions, PlaylistCollectionManager, type PlaylistCollectionState, PlaylistManager, type PlaylistState, type PrefetchCacheConfig, type PrefetchConfig, type ResourceConfig, type ResourceEvent, type ResourceEventListener, ResourceGovernor, type ResourceState, type RestoreResult, type VideoCommentState, calculatePrefetchIndices, calculateWindowIndices, canPause, canPlay, canSeek, computeAllocationChanges, createInitialCommentState, createInitialVideoCommentState, isActiveState, isValidTransition, mapNetworkType };
|