@xhub-short/core 0.1.0-beta.10 → 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 +707 -495
  2. package/dist/index.js +402 -59
  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,30 @@ 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;
584
+ /**
585
+ * Remove an item from the feed
586
+ *
587
+ * Used for:
588
+ * - Report: Remove reported content from feed
589
+ * - Not Interested: Remove content user doesn't want to see
590
+ *
591
+ * @param id - Content ID to remove
592
+ * @returns true if item was removed, false if not found
593
+ *
594
+ * @example
595
+ * ```typescript
596
+ * // User reports a video
597
+ * const wasRemoved = feedManager.removeItem(videoId);
598
+ * if (wasRemoved) {
599
+ * // Navigate to next video
600
+ * }
601
+ * ```
602
+ */
603
+ removeItem(id: string): boolean;
241
604
  /**
242
605
  * Check if data is stale and needs revalidation
243
606
  */
@@ -800,606 +1163,307 @@ declare class PlayerEngine {
800
1163
  */
801
1164
  private trackCompletion;
802
1165
  /**
803
- * Track when user leaves current video (scrolls to next video)
804
- * Sends final analytics event before video change
805
- */
806
- private trackLeaveVideo;
807
- /**
808
- * Categorize media error
809
- */
810
- private categorizeError;
811
- /**
812
- * Get current circuit breaker state
813
- */
814
- getCircuitBreakerState(): CircuitBreakerState;
815
- /**
816
- * Check if circuit breaker is open (blocking loads)
817
- */
818
- isCircuitOpen(): boolean;
819
- /**
820
- * Manually reset circuit breaker to CLOSED state
821
- * Useful for user-triggered retry after showing error UI
822
- */
823
- resetCircuitBreaker(): void;
824
- /**
825
- * Check if circuit breaker allows load operation
826
- * Also handles OPEN → HALF_OPEN transition based on timeout
827
- */
828
- private checkCircuitBreaker;
829
- /**
830
- * Record a recoverable error for circuit breaker tracking
831
- */
832
- private recordCircuitBreakerError;
833
- /**
834
- * Record a successful load for circuit breaker tracking
835
- */
836
- private recordCircuitBreakerSuccess;
837
- /**
838
- * Trip the circuit breaker (CLOSED/HALF_OPEN → OPEN)
839
- */
840
- private tripCircuit;
841
- /**
842
- * Transition from OPEN to HALF_OPEN
843
- */
844
- private transitionToHalfOpen;
845
- /**
846
- * Close the circuit breaker (HALF_OPEN → CLOSED)
847
- */
848
- private closeCircuit;
849
- /**
850
- * Schedule automatic transition from OPEN to HALF_OPEN
851
- */
852
- private scheduleHalfOpenTransition;
853
- /**
854
- * Clear circuit reset timer
855
- */
856
- private clearCircuitResetTimer;
857
- }
858
-
859
- /**
860
- * Check if a state transition is valid
861
- */
862
- declare function isValidTransition(from: PlayerStatus, to: PlayerStatus): boolean;
863
- /**
864
- * Check if player is in a "active" state (can receive playback commands)
865
- */
866
- declare function isActiveState(status: PlayerStatus): boolean;
867
- /**
868
- * Check if player can start playback
869
- */
870
- declare function canPlay(status: PlayerStatus): boolean;
871
- /**
872
- * Check if player can pause
873
- */
874
- declare function canPause(status: PlayerStatus): boolean;
875
- /**
876
- * Check if player can seek
877
- */
878
- declare function canSeek(status: PlayerStatus): boolean;
879
-
880
- /**
881
- * Lifecycle state for zustand store
882
- */
883
- interface LifecycleState {
884
- /** Whether manager is initialized */
885
- isInitialized: boolean;
886
- /** Whether there's a pending save operation */
887
- isSaving: boolean;
888
- /** Whether there's a pending restore operation */
889
- isRestoring: boolean;
890
- /** Last saved timestamp */
891
- lastSavedAt: number | null;
892
- /** Last restored timestamp */
893
- lastRestoredAt: number | null;
894
- /** Whether the restored snapshot needs revalidation */
895
- needsRevalidation: boolean;
896
- /** Current visibility state */
897
- visibilityState: DocumentVisibilityState;
898
- }
899
- /**
900
- * Restore result
901
- */
902
- interface RestoreResult {
903
- /** Whether restore was successful */
904
- success: boolean;
905
- /** Restored snapshot data (null if no valid snapshot) */
906
- snapshot: SessionSnapshot | null;
907
- /** Whether the restored data is stale and needs revalidation */
908
- needsRevalidation: boolean;
909
- /** Reason for failure (if any) */
910
- reason?: 'no_snapshot' | 'expired' | 'invalid' | 'error';
911
- /** Playback time in seconds (only present if restorePlaybackPosition config is enabled) */
912
- playbackTime?: number;
913
- /** Video ID that was playing (only present if restorePlaybackPosition config is enabled) */
914
- currentVideoId?: string;
915
- /** Captured video frame at playback position (base64 JPEG, only present if restorePlaybackPosition is enabled) */
916
- restoreFrame?: string;
917
- }
918
- /**
919
- * LifecycleManager configuration
920
- */
921
- interface LifecycleConfig {
922
- /** Storage adapter for persistence */
923
- storage?: ISessionStorage;
924
- /** Logger adapter */
925
- logger?: ILogger;
926
- /** Snapshot expiry time in ms (default: 24 hours) */
927
- snapshotExpiryMs?: number;
928
- /** Revalidation threshold in ms (default: 5 minutes) */
929
- revalidationThresholdMs?: number;
930
- /** Auto-save on visibility change (default: true) */
931
- autoSaveOnHidden?: boolean;
932
- /**
933
- * Enable restoring video playback position (default: false)
934
- * When enabled, saves and restores the exact playback time (seconds)
935
- * so video can seek() to the exact position user was watching
936
- */
937
- restorePlaybackPosition?: boolean;
938
- /** SDK version for compatibility */
939
- version?: string;
940
- }
941
- /**
942
- * Default lifecycle configuration
943
- */
944
- declare const DEFAULT_LIFECYCLE_CONFIG: Required<Omit<LifecycleConfig, 'storage' | 'logger'>>;
945
- /**
946
- * Lifecycle events for external listening
947
- */
948
- type LifecycleEvent = {
949
- type: 'saveStart';
950
- } | {
951
- type: 'saveComplete';
952
- timestamp: number;
953
- } | {
954
- type: 'saveFailed';
955
- error: Error;
956
- } | {
957
- type: 'restoreStart';
958
- } | {
959
- type: 'restoreComplete';
960
- result: RestoreResult;
961
- } | {
962
- type: 'visibilityChange';
963
- state: DocumentVisibilityState;
964
- };
965
- /**
966
- * Lifecycle event listener
967
- */
968
- type LifecycleEventListener = (event: LifecycleEvent) => void;
969
-
970
- /**
971
- * LifecycleManager - Handles session persistence and restoration
972
- *
973
- * Strategy: "State Persistence, DOM Destruction"
974
- * - Save snapshot to storage when user leaves
975
- * - Restore state from storage when user returns
976
- * - Video DOM nodes are destroyed to free RAM
977
- *
978
- * Features:
979
- * - Auto-save on visibility change (optional)
980
- * - Snapshot expiry (24 hours default)
981
- * - Stale data detection for revalidation
982
- * - Event system for UI coordination
983
- *
984
- * @example
985
- * ```typescript
986
- * const lifecycle = new LifecycleManager({ storage, logger });
987
- *
988
- * // Initialize and attempt restore
989
- * const result = await lifecycle.initialize();
990
- * if (result.success) {
991
- * feedManager.restoreFromSnapshot(result.snapshot);
992
- * if (result.needsRevalidation) {
993
- * feedManager.revalidate();
994
- * }
995
- * }
996
- *
997
- * // Save snapshot before leaving
998
- * lifecycle.saveSnapshot({
999
- * items: feedManager.getVideos(),
1000
- * cursor: feedManager.store.getState().cursor,
1001
- * focusedIndex: resourceGovernor.store.getState().focusedIndex,
1002
- * });
1003
- * ```
1004
- */
1005
- declare class LifecycleManager {
1006
- /** Zustand vanilla store - Single Source of Truth */
1007
- readonly store: StoreApi<LifecycleState>;
1008
- /** Resolved configuration */
1009
- private readonly config;
1010
- /** Storage adapter */
1011
- private readonly storage?;
1012
- /** Logger adapter */
1013
- private readonly logger?;
1014
- /** Event listeners */
1015
- private readonly eventListeners;
1016
- /** Visibility change handler reference (for cleanup) */
1017
- private visibilityHandler?;
1018
- /** Pending save data (for debouncing) */
1019
- private pendingSaveData?;
1020
- constructor(config?: LifecycleConfig);
1021
- /**
1022
- * Initialize lifecycle manager and attempt to restore session
1023
- */
1024
- initialize(): Promise<RestoreResult>;
1025
- /**
1026
- * Destroy lifecycle manager and cleanup
1027
- */
1028
- destroy(): void;
1029
- /**
1030
- * Restore session from storage
1031
- */
1032
- restoreSession(): Promise<RestoreResult>;
1033
- /**
1034
- * Save session snapshot to storage
1035
- *
1036
- * @param data - Snapshot data to save
1037
- * @param data.playbackTime - Current video playback position (only saved if restorePlaybackPosition config is enabled)
1038
- * @param data.currentVideoId - Current video ID (only saved if restorePlaybackPosition config is enabled)
1039
- * @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
1040
1168
  */
1041
- saveSnapshot(data: {
1042
- items: VideoItem[];
1043
- cursor: string | null;
1044
- focusedIndex: number;
1045
- scrollPosition?: number;
1046
- /** Current video playback time in seconds (only used when restorePlaybackPosition is enabled) */
1047
- playbackTime?: number;
1048
- /** Current video ID (only used when restorePlaybackPosition is enabled) */
1049
- currentVideoId?: string;
1050
- /** Captured video frame at playback position (only used when restorePlaybackPosition is enabled) */
1051
- restoreFrame?: string;
1052
- }): Promise<boolean>;
1169
+ private trackLeaveVideo;
1053
1170
  /**
1054
- * Clear saved snapshot
1171
+ * Categorize media error
1055
1172
  */
1056
- clearSnapshot(): Promise<void>;
1173
+ private categorizeError;
1057
1174
  /**
1058
- * Mark that pending data should be saved (for use with debouncing)
1059
- *
1060
- * @param data - Data to save when flush is called
1175
+ * Get current circuit breaker state
1061
1176
  */
1062
- setPendingSave(data: Omit<SessionSnapshot, 'savedAt' | 'version'>): void;
1177
+ getCircuitBreakerState(): CircuitBreakerState;
1063
1178
  /**
1064
- * Check if restorePlaybackPosition config is enabled
1065
- * SDK can use this to decide whether to collect playbackTime
1179
+ * Check if circuit breaker is open (blocking loads)
1066
1180
  */
1067
- isPlaybackPositionRestoreEnabled(): boolean;
1181
+ isCircuitOpen(): boolean;
1068
1182
  /**
1069
- * 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
1070
1185
  */
1071
- flushPendingSave(): Promise<void>;
1186
+ resetCircuitBreaker(): void;
1072
1187
  /**
1073
- * Handle visibility state change
1188
+ * Check if circuit breaker allows load operation
1189
+ * Also handles OPEN → HALF_OPEN transition based on timeout
1074
1190
  */
1075
- onVisibilityChange(state: DocumentVisibilityState): void;
1191
+ private checkCircuitBreaker;
1076
1192
  /**
1077
- * Get current visibility state
1193
+ * Record a recoverable error for circuit breaker tracking
1078
1194
  */
1079
- getVisibilityState(): DocumentVisibilityState;
1195
+ private recordCircuitBreakerError;
1080
1196
  /**
1081
- * Add event listener
1197
+ * Record a successful load for circuit breaker tracking
1082
1198
  */
1083
- addEventListener(listener: LifecycleEventListener): () => void;
1199
+ private recordCircuitBreakerSuccess;
1084
1200
  /**
1085
- * Remove event listener
1201
+ * Trip the circuit breaker (CLOSED/HALF_OPEN → OPEN)
1086
1202
  */
1087
- removeEventListener(listener: LifecycleEventListener): void;
1203
+ private tripCircuit;
1088
1204
  /**
1089
- * Setup visibility change listener
1205
+ * Transition from OPEN to HALF_OPEN
1090
1206
  */
1091
- private setupVisibilityListener;
1207
+ private transitionToHalfOpen;
1092
1208
  /**
1093
- * Validate snapshot data
1209
+ * Close the circuit breaker (HALF_OPEN → CLOSED)
1094
1210
  */
1095
- private validateSnapshot;
1211
+ private closeCircuit;
1096
1212
  /**
1097
- * Check if snapshot is stale (needs revalidation)
1213
+ * Schedule automatic transition from OPEN to HALF_OPEN
1098
1214
  */
1099
- private isSnapshotStale;
1215
+ private scheduleHalfOpenTransition;
1100
1216
  /**
1101
- * Emit event to all listeners
1217
+ * Clear circuit reset timer
1102
1218
  */
1103
- private emitEvent;
1219
+ private clearCircuitResetTimer;
1104
1220
  }
1105
1221
 
1106
1222
  /**
1107
- * Network type for prefetch strategy
1108
- * Simplified from contracts NetworkType for prefetch decisions
1223
+ * Check if a state transition is valid
1109
1224
  */
1110
- type NetworkType = 'wifi' | 'cellular' | 'offline';
1225
+ declare function isValidTransition(from: PlayerStatus, to: PlayerStatus): boolean;
1111
1226
  /**
1112
- * Map contracts NetworkType to simplified NetworkType
1227
+ * Check if player is in a "active" state (can receive playback commands)
1113
1228
  */
1114
- declare function mapNetworkType(type: string): NetworkType;
1229
+ declare function isActiveState(status: PlayerStatus): boolean;
1115
1230
  /**
1116
- * Resource state for zustand store
1231
+ * Check if player can start playback
1117
1232
  */
1118
- interface ResourceState {
1119
- /** Currently allocated video slot indices (max 3) */
1120
- activeAllocations: Set<number>;
1121
- /** Queue of indices to preload */
1122
- preloadQueue: number[];
1123
- /** Currently focused/playing video index */
1124
- focusedIndex: number;
1125
- /** Current network type */
1126
- networkType: NetworkType;
1127
- /** Total number of items in feed (for bounds checking) */
1128
- totalItems: number;
1129
- /** Whether resource governor is active */
1130
- isActive: boolean;
1131
- /** Whether preloading is throttled due to scroll thrashing */
1132
- isThrottled: boolean;
1133
- /** Indices currently being preloaded */
1134
- preloadingIndices: Set<number>;
1135
- }
1233
+ declare function canPlay(status: PlayerStatus): boolean;
1136
1234
  /**
1137
- * Allocation result returned by requestAllocation
1235
+ * Check if player can pause
1138
1236
  */
1139
- interface AllocationResult {
1140
- /** Indices that should mount video elements */
1141
- toMount: number[];
1142
- /** Indices that should unmount video elements */
1143
- toUnmount: number[];
1144
- /** Currently active allocations after this operation */
1145
- activeAllocations: number[];
1146
- /** Whether focus change was successful */
1147
- success: boolean;
1148
- }
1237
+ declare function canPause(status: PlayerStatus): boolean;
1149
1238
  /**
1150
- * Prefetch configuration by network type
1239
+ * Check if player can seek
1151
1240
  */
1152
- interface PrefetchConfig {
1153
- /** Number of posters to prefetch ahead (default: wifi=5, cellular=3, offline=1) */
1154
- posterCount: number;
1155
- /** Number of video segments to prefetch (default: wifi=2, cellular=1, offline=0) */
1156
- videoSegmentCount: number;
1157
- /** Whether to prefetch video at all (default: true for wifi/cellular) */
1158
- 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;
1159
1261
  }
1160
1262
  /**
1161
- * Scroll thrashing configuration
1162
- * Prevents excessive preloading when user scrolls too fast
1263
+ * Restore result
1163
1264
  */
1164
- interface ScrollThrashingConfig {
1165
- /** Time window to measure scroll rate (ms) - default: 1000 */
1166
- windowMs: number;
1167
- /** Max focus changes allowed in window before throttling - default: 3 */
1168
- maxChangesInWindow: number;
1169
- /** Cooldown time after throttle before resuming preload (ms) - default: 500 */
1170
- 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;
1171
1280
  }
1172
1281
  /**
1173
- * ResourceGovernor configuration
1282
+ * LifecycleManager configuration
1174
1283
  */
1175
- interface ResourceConfig {
1176
- /** Maximum video DOM nodes (default: 3) */
1177
- maxAllocations?: number;
1178
- /** Focus debounce time in ms (default: 150) */
1179
- focusDebounceMs?: number;
1180
- /** Prefetch configuration overrides by network type */
1181
- prefetch?: Partial<Record<NetworkType, Partial<PrefetchConfig>>>;
1182
- /** Scroll thrashing configuration */
1183
- scrollThrashing?: Partial<ScrollThrashingConfig>;
1184
- /** Network adapter for detecting connection type */
1185
- networkAdapter?: INetworkAdapter;
1186
- /** Video loader adapter for preloading video data */
1187
- videoLoader?: IVideoLoader;
1188
- /** Poster loader adapter for preloading thumbnails */
1189
- posterLoader?: IPosterLoader;
1284
+ interface LifecycleConfig {
1285
+ /** Storage adapter for persistence */
1286
+ storage?: ISessionStorage;
1190
1287
  /** Logger adapter */
1191
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;
1192
1303
  }
1193
1304
  /**
1194
- * Default prefetch configuration by network type
1195
- */
1196
- declare const DEFAULT_PREFETCH_CONFIG: Record<NetworkType, PrefetchConfig>;
1197
- /**
1198
- * Default resource configuration
1305
+ * Default lifecycle configuration
1199
1306
  */
1200
- declare const DEFAULT_RESOURCE_CONFIG: Required<Omit<ResourceConfig, 'networkAdapter' | 'videoLoader' | 'posterLoader' | 'logger' | 'prefetch' | 'scrollThrashing'>> & {
1201
- prefetch: Record<NetworkType, PrefetchConfig>;
1202
- scrollThrashing: ScrollThrashingConfig;
1203
- };
1307
+ declare const DEFAULT_LIFECYCLE_CONFIG: Required<Omit<LifecycleConfig, 'storage' | 'logger'>>;
1204
1308
  /**
1205
- * Resource events for external listening
1309
+ * Lifecycle events for external listening
1206
1310
  */
1207
- type ResourceEvent = {
1208
- type: 'allocationChange';
1209
- toMount: number[];
1210
- toUnmount: number[];
1311
+ type LifecycleEvent = {
1312
+ type: 'saveStart';
1211
1313
  } | {
1212
- type: 'focusChange';
1213
- index: number;
1214
- previousIndex: number;
1314
+ type: 'saveComplete';
1315
+ timestamp: number;
1215
1316
  } | {
1216
- type: 'networkChange';
1217
- networkType: NetworkType;
1317
+ type: 'saveFailed';
1318
+ error: Error;
1218
1319
  } | {
1219
- type: 'prefetchRequest';
1220
- indices: number[];
1320
+ type: 'restoreStart';
1321
+ } | {
1322
+ type: 'restoreComplete';
1323
+ result: RestoreResult;
1324
+ } | {
1325
+ type: 'visibilityChange';
1326
+ state: DocumentVisibilityState;
1221
1327
  };
1222
1328
  /**
1223
- * Resource event listener
1329
+ * Lifecycle event listener
1224
1330
  */
1225
- type ResourceEventListener = (event: ResourceEvent) => void;
1331
+ type LifecycleEventListener = (event: LifecycleEvent) => void;
1226
1332
 
1227
1333
  /**
1228
- * Video source getter function type
1229
- * ResourceGovernor needs this to get video sources for preloading
1230
- */
1231
- type VideoSourceGetter = (index: number) => {
1232
- id: string;
1233
- source: VideoSource;
1234
- poster?: string;
1235
- } | null;
1236
- /**
1237
- * ResourceGovernor - Manages video DOM allocation and prefetch strategy
1334
+ * LifecycleManager - Handles session persistence and restoration
1238
1335
  *
1239
- * Features:
1240
- * - Sliding window allocation (max 3 video DOM nodes)
1241
- * - Focus debouncing for smooth swipe
1242
- * - Network-aware prefetch strategy
1243
- * - 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
1244
1340
  *
1245
- * Memory Strategy:
1246
- * - Only 3 video elements exist in DOM at any time
1247
- * - Sliding window: [previous, current, next]
1248
- * - 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
1249
1346
  *
1250
1347
  * @example
1251
1348
  * ```typescript
1252
- * const governor = new ResourceGovernor({ maxAllocations: 3 });
1253
- *
1254
- * // Initialize with feed size
1255
- * governor.setTotalItems(20);
1256
- * governor.activate();
1257
- *
1258
- * // Handle scroll/swipe
1259
- * governor.setFocusedIndex(5); // Debounced
1349
+ * const lifecycle = new LifecycleManager({ storage, logger });
1260
1350
  *
1261
- * // Listen to allocation changes
1262
- * governor.addEventListener((event) => {
1263
- * if (event.type === 'allocationChange') {
1264
- * event.toMount.forEach(i => mountVideoAt(i));
1265
- * 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();
1266
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,
1267
1365
  * });
1268
1366
  * ```
1269
1367
  */
1270
- declare class ResourceGovernor {
1368
+ declare class LifecycleManager {
1271
1369
  /** Zustand vanilla store - Single Source of Truth */
1272
- readonly store: StoreApi<ResourceState>;
1370
+ readonly store: StoreApi<LifecycleState>;
1273
1371
  /** Resolved configuration */
1274
1372
  private readonly config;
1275
- /** Network adapter */
1276
- private readonly networkAdapter?;
1277
- /** Video loader adapter for preloading video data */
1278
- private readonly videoLoader?;
1279
- /** Poster loader adapter for preloading thumbnails */
1280
- private readonly posterLoader?;
1373
+ /** Storage adapter */
1374
+ private readonly storage?;
1281
1375
  /** Logger adapter */
1282
1376
  private readonly logger?;
1283
1377
  /** Event listeners */
1284
1378
  private readonly eventListeners;
1285
- /** Focus debounce timer */
1286
- private focusDebounceTimer;
1287
- /** Pending focused index (before debounce completes) */
1288
- private pendingFocusedIndex;
1289
- /** Network change unsubscribe function */
1290
- private networkUnsubscribe?;
1291
- /** Video source getter (injected via setVideoSourceGetter) */
1292
- private videoSourceGetter?;
1293
- /** Scroll thrashing detection - timestamps of recent focus changes */
1294
- private focusChangeTimestamps;
1295
- /** Scroll thrashing cooldown timer */
1296
- private thrashingCooldownTimer;
1297
- constructor(config?: ResourceConfig);
1298
- /**
1299
- * Activate the resource governor
1300
- * Starts network monitoring and performs initial allocation
1301
- */
1302
- 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);
1303
1384
  /**
1304
- * Deactivate the resource governor
1305
- * Stops network monitoring and clears allocations
1385
+ * Initialize lifecycle manager and attempt to restore session
1306
1386
  */
1307
- deactivate(): void;
1387
+ initialize(): Promise<RestoreResult>;
1308
1388
  /**
1309
- * Destroy the resource governor
1389
+ * Destroy lifecycle manager and cleanup
1310
1390
  */
1311
1391
  destroy(): void;
1312
1392
  /**
1313
- * Set total number of items in feed
1314
- */
1315
- setTotalItems(count: number): void;
1316
- /**
1317
- * Set focused index with debouncing
1318
- * This is called during scroll/swipe
1319
- */
1320
- setFocusedIndex(index: number): void;
1321
- /**
1322
- * Set focused index immediately (skip debounce)
1323
- * Use this for programmatic navigation, not scroll
1324
- */
1325
- setFocusedIndexImmediate(index: number): void;
1326
- /**
1327
- * Request allocation for given indices
1328
- * Returns what needs to be mounted/unmounted
1329
- */
1330
- requestAllocation(indices: number[]): AllocationResult;
1331
- /**
1332
- * Get current active allocations
1393
+ * Restore session from storage
1333
1394
  */
1334
- getActiveAllocations(): number[];
1395
+ restoreSession(): Promise<RestoreResult>;
1335
1396
  /**
1336
- * 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)
1337
1403
  */
1338
- 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>;
1339
1416
  /**
1340
- * Get current network type
1417
+ * Clear saved snapshot
1341
1418
  */
1342
- getNetworkType(): NetworkType;
1419
+ clearSnapshot(): Promise<void>;
1343
1420
  /**
1344
- * 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
1345
1424
  */
1346
- getPrefetchConfig(): PrefetchConfig;
1425
+ setPendingSave(data: Omit<SessionSnapshot, 'savedAt' | 'version'>): void;
1347
1426
  /**
1348
- * Set video source getter function
1349
- * This is called by SDK to provide video data for preloading
1350
- *
1351
- * @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
1352
1429
  */
1353
- setVideoSourceGetter(getter: VideoSourceGetter): void;
1430
+ isPlaybackPositionRestoreEnabled(): boolean;
1354
1431
  /**
1355
- * Check if preloading is currently throttled due to scroll thrashing
1432
+ * Flush pending save (called on visibility hidden)
1356
1433
  */
1357
- isPreloadThrottled(): boolean;
1434
+ flushPendingSave(): Promise<void>;
1358
1435
  /**
1359
- * Get indices currently being preloaded
1436
+ * Handle visibility state change
1360
1437
  */
1361
- getPreloadingIndices(): number[];
1438
+ onVisibilityChange(state: DocumentVisibilityState): void;
1362
1439
  /**
1363
- * Manually trigger preload for specific indices
1364
- * Respects throttling and network conditions
1440
+ * Get current visibility state
1365
1441
  */
1366
- triggerPreload(indices: number[]): Promise<void>;
1442
+ getVisibilityState(): DocumentVisibilityState;
1367
1443
  /**
1368
1444
  * Add event listener
1369
1445
  */
1370
- addEventListener(listener: ResourceEventListener): () => void;
1446
+ addEventListener(listener: LifecycleEventListener): () => void;
1371
1447
  /**
1372
1448
  * Remove event listener
1373
1449
  */
1374
- removeEventListener(listener: ResourceEventListener): void;
1375
- /**
1376
- * Initialize network detection
1377
- */
1378
- private initializeNetwork;
1379
- /**
1380
- * Perform allocation for a given focused index
1381
- */
1382
- private performAllocation;
1383
- /**
1384
- * Update prefetch queue based on current state
1385
- */
1386
- private updatePrefetchQueue;
1450
+ removeEventListener(listener: LifecycleEventListener): void;
1387
1451
  /**
1388
- * Track focus change timestamp for scroll thrashing detection
1452
+ * Setup visibility change listener
1389
1453
  */
1390
- private trackFocusChange;
1454
+ private setupVisibilityListener;
1391
1455
  /**
1392
- * Cancel all in-progress preloads
1456
+ * Validate snapshot data
1393
1457
  */
1394
- private cancelAllPreloads;
1458
+ private validateSnapshot;
1395
1459
  /**
1396
- * Execute video preloading for given indices
1460
+ * Check if snapshot is stale (needs revalidation)
1397
1461
  */
1398
- private executePreload;
1462
+ private isSnapshotStale;
1399
1463
  /**
1400
- * Execute poster preloading for given indices
1464
+ * Create session snapshot from data
1401
1465
  */
1402
- private executePreloadPosters;
1466
+ private createSnapshot;
1403
1467
  /**
1404
1468
  * Emit event to all listeners
1405
1469
  */
@@ -1939,4 +2003,152 @@ declare class CommentManager {
1939
2003
  private createError;
1940
2004
  }
1941
2005
 
1942
- 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 };