@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.
Files changed (3) hide show
  1. package/dist/index.d.ts +687 -495
  2. package/dist/index.js +367 -60
  3. package/package.json +4 -4
package/dist/index.d.ts CHANGED
@@ -1,7 +1,314 @@
1
- import { VideoItem, IDataSource, IStorage, PrefetchCacheData, IAnalytics, ILogger, ISessionStorage, SessionSnapshot, INetworkAdapter, IVideoLoader, IPosterLoader, VideoSource, IInteraction, CommentItem, ReplyItem, ICommentAdapter, InternalLogger, CommentAuthor } from '@xhub-short/contracts';
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 readonly dataSource;
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
- constructor(dataSource: IDataSource, config?: FeedConfig, storage?: IStorage, prefetchConfig?: Partial<PrefetchCacheConfig>);
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(): Promise<void>;
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
- saveSnapshot(data: {
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
- * Clear saved snapshot
1171
+ * Categorize media error
1075
1172
  */
1076
- clearSnapshot(): Promise<void>;
1173
+ private categorizeError;
1077
1174
  /**
1078
- * Mark that pending data should be saved (for use with debouncing)
1079
- *
1080
- * @param data - Data to save when flush is called
1175
+ * Get current circuit breaker state
1081
1176
  */
1082
- setPendingSave(data: Omit<SessionSnapshot, 'savedAt' | 'version'>): void;
1177
+ getCircuitBreakerState(): CircuitBreakerState;
1083
1178
  /**
1084
- * Check if restorePlaybackPosition config is enabled
1085
- * SDK can use this to decide whether to collect playbackTime
1179
+ * Check if circuit breaker is open (blocking loads)
1086
1180
  */
1087
- isPlaybackPositionRestoreEnabled(): boolean;
1181
+ isCircuitOpen(): boolean;
1088
1182
  /**
1089
- * Flush pending save (called on visibility hidden)
1183
+ * Manually reset circuit breaker to CLOSED state
1184
+ * Useful for user-triggered retry after showing error UI
1090
1185
  */
1091
- flushPendingSave(): Promise<void>;
1186
+ resetCircuitBreaker(): void;
1092
1187
  /**
1093
- * Handle visibility state change
1188
+ * Check if circuit breaker allows load operation
1189
+ * Also handles OPEN → HALF_OPEN transition based on timeout
1094
1190
  */
1095
- onVisibilityChange(state: DocumentVisibilityState): void;
1191
+ private checkCircuitBreaker;
1096
1192
  /**
1097
- * Get current visibility state
1193
+ * Record a recoverable error for circuit breaker tracking
1098
1194
  */
1099
- getVisibilityState(): DocumentVisibilityState;
1195
+ private recordCircuitBreakerError;
1100
1196
  /**
1101
- * Add event listener
1197
+ * Record a successful load for circuit breaker tracking
1102
1198
  */
1103
- addEventListener(listener: LifecycleEventListener): () => void;
1199
+ private recordCircuitBreakerSuccess;
1104
1200
  /**
1105
- * Remove event listener
1201
+ * Trip the circuit breaker (CLOSED/HALF_OPEN → OPEN)
1106
1202
  */
1107
- removeEventListener(listener: LifecycleEventListener): void;
1203
+ private tripCircuit;
1108
1204
  /**
1109
- * Setup visibility change listener
1205
+ * Transition from OPEN to HALF_OPEN
1110
1206
  */
1111
- private setupVisibilityListener;
1207
+ private transitionToHalfOpen;
1112
1208
  /**
1113
- * Validate snapshot data
1209
+ * Close the circuit breaker (HALF_OPEN → CLOSED)
1114
1210
  */
1115
- private validateSnapshot;
1211
+ private closeCircuit;
1116
1212
  /**
1117
- * Check if snapshot is stale (needs revalidation)
1213
+ * Schedule automatic transition from OPEN to HALF_OPEN
1118
1214
  */
1119
- private isSnapshotStale;
1215
+ private scheduleHalfOpenTransition;
1120
1216
  /**
1121
- * Emit event to all listeners
1217
+ * Clear circuit reset timer
1122
1218
  */
1123
- private emitEvent;
1219
+ private clearCircuitResetTimer;
1124
1220
  }
1125
1221
 
1126
1222
  /**
1127
- * Network type for prefetch strategy
1128
- * Simplified from contracts NetworkType for prefetch decisions
1223
+ * Check if a state transition is valid
1129
1224
  */
1130
- type NetworkType = 'wifi' | 'cellular' | 'offline';
1225
+ declare function isValidTransition(from: PlayerStatus, to: PlayerStatus): boolean;
1131
1226
  /**
1132
- * Map contracts NetworkType to simplified NetworkType
1227
+ * Check if player is in a "active" state (can receive playback commands)
1133
1228
  */
1134
- declare function mapNetworkType(type: string): NetworkType;
1229
+ declare function isActiveState(status: PlayerStatus): boolean;
1135
1230
  /**
1136
- * Resource state for zustand store
1231
+ * Check if player can start playback
1137
1232
  */
1138
- interface ResourceState {
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
- * Allocation result returned by requestAllocation
1235
+ * Check if player can pause
1158
1236
  */
1159
- interface AllocationResult {
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
- * Prefetch configuration by network type
1239
+ * Check if player can seek
1171
1240
  */
1172
- interface PrefetchConfig {
1173
- /** Number of posters to prefetch ahead (default: wifi=5, cellular=3, offline=1) */
1174
- posterCount: number;
1175
- /** Number of video segments to prefetch (default: wifi=2, cellular=1, offline=0) */
1176
- videoSegmentCount: number;
1177
- /** Whether to prefetch video at all (default: true for wifi/cellular) */
1178
- prefetchVideo: boolean;
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
- * Scroll thrashing configuration
1182
- * Prevents excessive preloading when user scrolls too fast
1263
+ * Restore result
1183
1264
  */
1184
- interface ScrollThrashingConfig {
1185
- /** Time window to measure scroll rate (ms) - default: 1000 */
1186
- windowMs: number;
1187
- /** Max focus changes allowed in window before throttling - default: 3 */
1188
- maxChangesInWindow: number;
1189
- /** Cooldown time after throttle before resuming preload (ms) - default: 500 */
1190
- cooldownMs: number;
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
- * ResourceGovernor configuration
1282
+ * LifecycleManager configuration
1194
1283
  */
1195
- interface ResourceConfig {
1196
- /** Maximum video DOM nodes (default: 3) */
1197
- maxAllocations?: number;
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 prefetch configuration by network type
1215
- */
1216
- declare const DEFAULT_PREFETCH_CONFIG: Record<NetworkType, PrefetchConfig>;
1217
- /**
1218
- * Default resource configuration
1305
+ * Default lifecycle configuration
1219
1306
  */
1220
- declare const DEFAULT_RESOURCE_CONFIG: Required<Omit<ResourceConfig, 'networkAdapter' | 'videoLoader' | 'posterLoader' | 'logger' | 'prefetch' | 'scrollThrashing'>> & {
1221
- prefetch: Record<NetworkType, PrefetchConfig>;
1222
- scrollThrashing: ScrollThrashingConfig;
1223
- };
1307
+ declare const DEFAULT_LIFECYCLE_CONFIG: Required<Omit<LifecycleConfig, 'storage' | 'logger'>>;
1224
1308
  /**
1225
- * Resource events for external listening
1309
+ * Lifecycle events for external listening
1226
1310
  */
1227
- type ResourceEvent = {
1228
- type: 'allocationChange';
1229
- toMount: number[];
1230
- toUnmount: number[];
1311
+ type LifecycleEvent = {
1312
+ type: 'saveStart';
1231
1313
  } | {
1232
- type: 'focusChange';
1233
- index: number;
1234
- previousIndex: number;
1314
+ type: 'saveComplete';
1315
+ timestamp: number;
1235
1316
  } | {
1236
- type: 'networkChange';
1237
- networkType: NetworkType;
1317
+ type: 'saveFailed';
1318
+ error: Error;
1238
1319
  } | {
1239
- type: 'prefetchRequest';
1240
- indices: number[];
1320
+ type: 'restoreStart';
1321
+ } | {
1322
+ type: 'restoreComplete';
1323
+ result: RestoreResult;
1324
+ } | {
1325
+ type: 'visibilityChange';
1326
+ state: DocumentVisibilityState;
1241
1327
  };
1242
1328
  /**
1243
- * Resource event listener
1329
+ * Lifecycle event listener
1244
1330
  */
1245
- type ResourceEventListener = (event: ResourceEvent) => void;
1331
+ type LifecycleEventListener = (event: LifecycleEvent) => void;
1246
1332
 
1247
1333
  /**
1248
- * Video source getter function type
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
- * Features:
1260
- * - Sliding window allocation (max 3 video DOM nodes)
1261
- * - Focus debouncing for smooth swipe
1262
- * - Network-aware prefetch strategy
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
- * Memory Strategy:
1266
- * - Only 3 video elements exist in DOM at any time
1267
- * - Sliding window: [previous, current, next]
1268
- * - When user scrolls, we unmount far elements and mount new ones
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 governor = new ResourceGovernor({ maxAllocations: 3 });
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
- * // Listen to allocation changes
1282
- * governor.addEventListener((event) => {
1283
- * if (event.type === 'allocationChange') {
1284
- * event.toMount.forEach(i => mountVideoAt(i));
1285
- * event.toUnmount.forEach(i => unmountVideoAt(i));
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 ResourceGovernor {
1368
+ declare class LifecycleManager {
1291
1369
  /** Zustand vanilla store - Single Source of Truth */
1292
- readonly store: StoreApi<ResourceState>;
1370
+ readonly store: StoreApi<LifecycleState>;
1293
1371
  /** Resolved configuration */
1294
1372
  private readonly config;
1295
- /** Network adapter */
1296
- private readonly networkAdapter?;
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
- /** Focus debounce timer */
1306
- private focusDebounceTimer;
1307
- /** Pending focused index (before debounce completes) */
1308
- private pendingFocusedIndex;
1309
- /** Network change unsubscribe function */
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
- * Deactivate the resource governor
1325
- * Stops network monitoring and clears allocations
1385
+ * Initialize lifecycle manager and attempt to restore session
1326
1386
  */
1327
- deactivate(): void;
1387
+ initialize(): Promise<RestoreResult>;
1328
1388
  /**
1329
- * Destroy the resource governor
1389
+ * Destroy lifecycle manager and cleanup
1330
1390
  */
1331
1391
  destroy(): void;
1332
1392
  /**
1333
- * Set total number of items in feed
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
- getActiveAllocations(): number[];
1395
+ restoreSession(): Promise<RestoreResult>;
1355
1396
  /**
1356
- * Check if an index is currently allocated
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
- isAllocated(index: number): boolean;
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
- * Get current network type
1417
+ * Clear saved snapshot
1361
1418
  */
1362
- getNetworkType(): NetworkType;
1419
+ clearSnapshot(): Promise<void>;
1363
1420
  /**
1364
- * Get prefetch configuration for current network
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
- getPrefetchConfig(): PrefetchConfig;
1425
+ setPendingSave(data: Omit<SessionSnapshot, 'savedAt' | 'version'>): void;
1367
1426
  /**
1368
- * Set video source getter function
1369
- * This is called by SDK to provide video data for preloading
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
- setVideoSourceGetter(getter: VideoSourceGetter): void;
1430
+ isPlaybackPositionRestoreEnabled(): boolean;
1374
1431
  /**
1375
- * Check if preloading is currently throttled due to scroll thrashing
1432
+ * Flush pending save (called on visibility hidden)
1376
1433
  */
1377
- isPreloadThrottled(): boolean;
1434
+ flushPendingSave(): Promise<void>;
1378
1435
  /**
1379
- * Get indices currently being preloaded
1436
+ * Handle visibility state change
1380
1437
  */
1381
- getPreloadingIndices(): number[];
1438
+ onVisibilityChange(state: DocumentVisibilityState): void;
1382
1439
  /**
1383
- * Manually trigger preload for specific indices
1384
- * Respects throttling and network conditions
1440
+ * Get current visibility state
1385
1441
  */
1386
- triggerPreload(indices: number[]): Promise<void>;
1442
+ getVisibilityState(): DocumentVisibilityState;
1387
1443
  /**
1388
1444
  * Add event listener
1389
1445
  */
1390
- addEventListener(listener: ResourceEventListener): () => void;
1446
+ addEventListener(listener: LifecycleEventListener): () => void;
1391
1447
  /**
1392
1448
  * Remove event listener
1393
1449
  */
1394
- removeEventListener(listener: ResourceEventListener): void;
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
- * Track focus change timestamp for scroll thrashing detection
1452
+ * Setup visibility change listener
1409
1453
  */
1410
- private trackFocusChange;
1454
+ private setupVisibilityListener;
1411
1455
  /**
1412
- * Cancel all in-progress preloads
1456
+ * Validate snapshot data
1413
1457
  */
1414
- private cancelAllPreloads;
1458
+ private validateSnapshot;
1415
1459
  /**
1416
- * Execute video preloading for given indices
1460
+ * Check if snapshot is stale (needs revalidation)
1417
1461
  */
1418
- private executePreload;
1462
+ private isSnapshotStale;
1419
1463
  /**
1420
- * Execute poster preloading for given indices
1464
+ * Create session snapshot from data
1421
1465
  */
1422
- private executePreloadPosters;
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
- 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 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 };
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 };