@xhub-short/contracts 0.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1474 -0
- package/dist/index.js +1 -0
- package/package.json +32 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1474 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core types for XHub-short SDK
|
|
3
|
+
* These types are the foundation of the SDK's data model.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Video source configuration
|
|
7
|
+
* Supports both MP4 (native) and HLS streaming
|
|
8
|
+
*/
|
|
9
|
+
interface VideoSource {
|
|
10
|
+
/** Video URL - can be MP4 or HLS (.m3u8) */
|
|
11
|
+
url: string;
|
|
12
|
+
/** Source type for playback strategy selection */
|
|
13
|
+
type: 'mp4' | 'hls';
|
|
14
|
+
/** Optional quality variants for adaptive streaming */
|
|
15
|
+
qualities?: VideoQuality[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Video quality variant for adaptive bitrate streaming
|
|
19
|
+
*/
|
|
20
|
+
interface VideoQuality {
|
|
21
|
+
/** Quality label (e.g., '720p', '1080p') */
|
|
22
|
+
label: string;
|
|
23
|
+
/** Bitrate in kbps */
|
|
24
|
+
bitrate: number;
|
|
25
|
+
/** Resolution width */
|
|
26
|
+
width: number;
|
|
27
|
+
/** Resolution height */
|
|
28
|
+
height: number;
|
|
29
|
+
/** URL for this quality */
|
|
30
|
+
url: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Video author information
|
|
34
|
+
*/
|
|
35
|
+
interface Author {
|
|
36
|
+
/** Unique author ID */
|
|
37
|
+
id: string;
|
|
38
|
+
/** Display name */
|
|
39
|
+
name: string;
|
|
40
|
+
/** Avatar URL */
|
|
41
|
+
avatar?: string;
|
|
42
|
+
/** Verified status */
|
|
43
|
+
isVerified?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Video statistics
|
|
47
|
+
*/
|
|
48
|
+
interface VideoStats {
|
|
49
|
+
/** Total view count */
|
|
50
|
+
views: number;
|
|
51
|
+
/** Total like count */
|
|
52
|
+
likes: number;
|
|
53
|
+
/** Total comment count */
|
|
54
|
+
comments: number;
|
|
55
|
+
/** Total share count */
|
|
56
|
+
shares: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Main video item structure
|
|
60
|
+
* This is the core data model for a video in the feed
|
|
61
|
+
*/
|
|
62
|
+
interface VideoItem {
|
|
63
|
+
/** Unique video ID */
|
|
64
|
+
id: string;
|
|
65
|
+
/** Video source configuration */
|
|
66
|
+
source: VideoSource;
|
|
67
|
+
/** Poster/thumbnail image URL */
|
|
68
|
+
poster?: string;
|
|
69
|
+
/** Video duration in seconds */
|
|
70
|
+
duration: number;
|
|
71
|
+
/** Video title/description */
|
|
72
|
+
title?: string;
|
|
73
|
+
/** Author information */
|
|
74
|
+
author: Author;
|
|
75
|
+
/** Video statistics */
|
|
76
|
+
stats: VideoStats;
|
|
77
|
+
/** Whether current user has liked this video */
|
|
78
|
+
isLiked: boolean;
|
|
79
|
+
/** Whether current user is following the author */
|
|
80
|
+
isFollowing: boolean;
|
|
81
|
+
/** Video creation timestamp */
|
|
82
|
+
createdAt: string;
|
|
83
|
+
/** Optional hashtags */
|
|
84
|
+
hashtags?: string[];
|
|
85
|
+
/** Optional music/sound information */
|
|
86
|
+
music?: MusicInfo;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Music/Sound information for a video
|
|
90
|
+
*/
|
|
91
|
+
interface MusicInfo {
|
|
92
|
+
/** Music ID */
|
|
93
|
+
id: string;
|
|
94
|
+
/** Music title */
|
|
95
|
+
title: string;
|
|
96
|
+
/** Artist name */
|
|
97
|
+
artist: string;
|
|
98
|
+
/** Cover image URL */
|
|
99
|
+
cover?: string;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Feed pagination response
|
|
103
|
+
*/
|
|
104
|
+
interface FeedResponse {
|
|
105
|
+
/** List of video items */
|
|
106
|
+
items: VideoItem[];
|
|
107
|
+
/** Cursor for next page (null if no more data) */
|
|
108
|
+
nextCursor: string | null;
|
|
109
|
+
/** Whether there are more items to load */
|
|
110
|
+
hasMore: boolean;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Feed state for state management
|
|
114
|
+
*/
|
|
115
|
+
interface FeedState {
|
|
116
|
+
/** Map of video items by ID (for O(1) lookup and deduplication) */
|
|
117
|
+
itemsMap: Map<string, VideoItem>;
|
|
118
|
+
/** Ordered list of video IDs for rendering */
|
|
119
|
+
orderedIds: string[];
|
|
120
|
+
/** Current cursor for pagination */
|
|
121
|
+
cursor: string | null;
|
|
122
|
+
/** Whether more items can be loaded */
|
|
123
|
+
hasMore: boolean;
|
|
124
|
+
/** Loading state */
|
|
125
|
+
isLoading: boolean;
|
|
126
|
+
/** Error state */
|
|
127
|
+
error: Error | null;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Playback state enum
|
|
131
|
+
*/
|
|
132
|
+
type PlaybackState = 'idle' | 'loading' | 'ready' | 'playing' | 'paused' | 'buffering' | 'ended' | 'error';
|
|
133
|
+
/**
|
|
134
|
+
* Player state for a single video
|
|
135
|
+
*/
|
|
136
|
+
interface PlayerState {
|
|
137
|
+
/** Current playback state */
|
|
138
|
+
playbackState: PlaybackState;
|
|
139
|
+
/** Current playback time in seconds */
|
|
140
|
+
currentTime: number;
|
|
141
|
+
/** Total duration in seconds */
|
|
142
|
+
duration: number;
|
|
143
|
+
/** Buffered time ranges */
|
|
144
|
+
buffered: number;
|
|
145
|
+
/** Whether video is muted */
|
|
146
|
+
isMuted: boolean;
|
|
147
|
+
/** Current volume (0-1) */
|
|
148
|
+
volume: number;
|
|
149
|
+
/** Playback rate */
|
|
150
|
+
playbackRate: number;
|
|
151
|
+
/** Error information if playback failed */
|
|
152
|
+
error: PlayerError | null;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Player error information
|
|
156
|
+
*/
|
|
157
|
+
interface PlayerError {
|
|
158
|
+
/** Error code for programmatic handling */
|
|
159
|
+
code: string;
|
|
160
|
+
/** Human-readable error message */
|
|
161
|
+
message: string;
|
|
162
|
+
/** Whether error is recoverable */
|
|
163
|
+
recoverable: boolean;
|
|
164
|
+
/** Retry count for circuit breaker */
|
|
165
|
+
retryCount: number;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Session snapshot for state persistence
|
|
169
|
+
* Used by Lifecycle Manager for state restoration
|
|
170
|
+
*/
|
|
171
|
+
interface SessionSnapshot {
|
|
172
|
+
/** Video items from feed (for offline restore) */
|
|
173
|
+
items: VideoItem[];
|
|
174
|
+
/** Feed cursor position for pagination */
|
|
175
|
+
cursor: string | null;
|
|
176
|
+
/** Index of the currently focused video */
|
|
177
|
+
focusedIndex: number;
|
|
178
|
+
/** Scroll position (optional, for virtual scroller) */
|
|
179
|
+
scrollPosition?: number;
|
|
180
|
+
/**
|
|
181
|
+
* Video playback position in seconds (optional)
|
|
182
|
+
* Only saved when restorePlaybackPosition config is enabled
|
|
183
|
+
* Used to seek() video to exact position when restoring
|
|
184
|
+
*/
|
|
185
|
+
playbackTime?: number;
|
|
186
|
+
/**
|
|
187
|
+
* ID of the currently playing video (optional)
|
|
188
|
+
* Backup for focusedIndex in case feed order changed
|
|
189
|
+
*/
|
|
190
|
+
currentVideoId?: string;
|
|
191
|
+
/** Snapshot creation time */
|
|
192
|
+
savedAt: number;
|
|
193
|
+
/** SDK version for compatibility checking */
|
|
194
|
+
version: string;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Comment structure
|
|
198
|
+
*/
|
|
199
|
+
interface Comment {
|
|
200
|
+
/** Comment ID */
|
|
201
|
+
id: string;
|
|
202
|
+
/** Comment text */
|
|
203
|
+
text: string;
|
|
204
|
+
/** Comment author */
|
|
205
|
+
author: Author;
|
|
206
|
+
/** Like count */
|
|
207
|
+
likes: number;
|
|
208
|
+
/** Whether current user liked this comment */
|
|
209
|
+
isLiked: boolean;
|
|
210
|
+
/** Creation timestamp */
|
|
211
|
+
createdAt: string;
|
|
212
|
+
/** Reply count */
|
|
213
|
+
replyCount: number;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Optimistic action for tracking pending operations
|
|
217
|
+
*/
|
|
218
|
+
interface OptimisticAction<T = unknown> {
|
|
219
|
+
/** Unique action ID */
|
|
220
|
+
id: string;
|
|
221
|
+
/** Action type */
|
|
222
|
+
type: 'like' | 'unlike' | 'follow' | 'unfollow' | 'comment';
|
|
223
|
+
/** Target resource ID */
|
|
224
|
+
targetId: string;
|
|
225
|
+
/** Previous state for rollback */
|
|
226
|
+
previousState: T;
|
|
227
|
+
/** Timestamp for timeout handling */
|
|
228
|
+
timestamp: number;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Analytics event types
|
|
232
|
+
*/
|
|
233
|
+
type AnalyticsEventType = 'video_view' | 'video_play' | 'video_pause' | 'video_complete' | 'video_seek' | 'video_error' | 'like' | 'unlike' | 'share' | 'comment' | 'follow' | 'unfollow' | 'scroll' | 'impression';
|
|
234
|
+
/**
|
|
235
|
+
* Analytics event structure
|
|
236
|
+
*/
|
|
237
|
+
interface AnalyticsEvent {
|
|
238
|
+
/** Event type */
|
|
239
|
+
type: AnalyticsEventType;
|
|
240
|
+
/** Video ID (if applicable) */
|
|
241
|
+
videoId?: string;
|
|
242
|
+
/** Event timestamp */
|
|
243
|
+
timestamp: number;
|
|
244
|
+
/** Additional event data */
|
|
245
|
+
data?: Record<string, unknown>;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* SDK configuration options
|
|
249
|
+
*/
|
|
250
|
+
interface SDKConfig {
|
|
251
|
+
/** Data source adapter */
|
|
252
|
+
dataSource?: unknown;
|
|
253
|
+
/** Interaction adapter */
|
|
254
|
+
interaction?: unknown;
|
|
255
|
+
/** Storage adapter */
|
|
256
|
+
storage?: unknown;
|
|
257
|
+
/** Analytics adapter */
|
|
258
|
+
analytics?: unknown;
|
|
259
|
+
/** Logger adapter */
|
|
260
|
+
logger?: unknown;
|
|
261
|
+
/** Enable debug mode */
|
|
262
|
+
debug?: boolean;
|
|
263
|
+
/** Maximum number of video DOM nodes (default: 3) */
|
|
264
|
+
maxVideoDomNodes?: number;
|
|
265
|
+
/** Preload strategy */
|
|
266
|
+
preloadStrategy?: 'none' | 'next' | 'adjacent';
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Log levels
|
|
270
|
+
*/
|
|
271
|
+
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
272
|
+
/**
|
|
273
|
+
* Log entry structure
|
|
274
|
+
*/
|
|
275
|
+
interface LogEntry {
|
|
276
|
+
/** Log level */
|
|
277
|
+
level: LogLevel;
|
|
278
|
+
/** Log message */
|
|
279
|
+
message: string;
|
|
280
|
+
/** Timestamp */
|
|
281
|
+
timestamp: number;
|
|
282
|
+
/** Additional context */
|
|
283
|
+
context?: Record<string, unknown>;
|
|
284
|
+
/** Error stack trace (if error) */
|
|
285
|
+
stack?: string;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* IDataSource - Data Port Interface
|
|
290
|
+
*
|
|
291
|
+
* This interface defines the contract for fetching video data.
|
|
292
|
+
* SDK core never knows about specific APIs - it only talks to this interface.
|
|
293
|
+
*
|
|
294
|
+
* Implementations:
|
|
295
|
+
* - DefaultApiAdapter: Uses config.apiEndpoint to fetch
|
|
296
|
+
* - MockDataAdapter: Returns mock data for testing/development
|
|
297
|
+
* - Custom: Host App can provide their own implementation
|
|
298
|
+
*
|
|
299
|
+
* @example
|
|
300
|
+
* ```typescript
|
|
301
|
+
* class MyDataAdapter implements IDataSource {
|
|
302
|
+
* async fetchFeed(cursor?: string): Promise<FeedResponse> {
|
|
303
|
+
* const response = await myApi.getVideos(cursor);
|
|
304
|
+
* return {
|
|
305
|
+
* items: response.data.map(transformToVideoItem),
|
|
306
|
+
* nextCursor: response.pagination.next,
|
|
307
|
+
* hasMore: response.pagination.hasMore,
|
|
308
|
+
* };
|
|
309
|
+
* }
|
|
310
|
+
* }
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
313
|
+
interface IDataSource {
|
|
314
|
+
/**
|
|
315
|
+
* Fetch a page of videos for the feed
|
|
316
|
+
*
|
|
317
|
+
* @param cursor - Pagination cursor from previous response (undefined for first page)
|
|
318
|
+
* @returns Promise resolving to feed response with items and pagination info
|
|
319
|
+
* @throws Error if network fails or API returns error
|
|
320
|
+
*
|
|
321
|
+
* Implementation notes:
|
|
322
|
+
* - Must handle network errors gracefully
|
|
323
|
+
* - Should return empty items array (not throw) if no data
|
|
324
|
+
* - Cursor format is opaque to SDK - can be page number, timestamp, etc.
|
|
325
|
+
*/
|
|
326
|
+
fetchFeed(cursor?: string): Promise<FeedResponse>;
|
|
327
|
+
/**
|
|
328
|
+
* Get detailed information for a specific video
|
|
329
|
+
*
|
|
330
|
+
* @param id - Video ID
|
|
331
|
+
* @returns Promise resolving to video item details
|
|
332
|
+
* @throws Error if video not found or network fails
|
|
333
|
+
*
|
|
334
|
+
* Implementation notes:
|
|
335
|
+
* - May return cached data if available
|
|
336
|
+
* - Should throw specific error for 404 (video deleted/not found)
|
|
337
|
+
*/
|
|
338
|
+
getVideoDetail(id: string): Promise<VideoItem>;
|
|
339
|
+
/**
|
|
340
|
+
* Optional: Prefetch videos for smoother scrolling
|
|
341
|
+
*
|
|
342
|
+
* @param ids - Array of video IDs to prefetch
|
|
343
|
+
* @returns Promise resolving when prefetch is complete
|
|
344
|
+
*
|
|
345
|
+
* Implementation notes:
|
|
346
|
+
* - This is optional - implementation can be no-op
|
|
347
|
+
* - Used by Resource Governor for preloading
|
|
348
|
+
* - Should not throw errors - fail silently
|
|
349
|
+
*/
|
|
350
|
+
prefetch?(ids: string[]): Promise<void>;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* IInteraction - Interaction Port Interface
|
|
355
|
+
*
|
|
356
|
+
* This interface defines the contract for user interactions with videos.
|
|
357
|
+
* Used with Optimistic UI pattern - UI updates immediately, API call happens async.
|
|
358
|
+
*
|
|
359
|
+
* Flow:
|
|
360
|
+
* 1. User taps like
|
|
361
|
+
* 2. UI updates immediately (optimistic)
|
|
362
|
+
* 3. This adapter is called
|
|
363
|
+
* 4. On error -> UI rolls back
|
|
364
|
+
*
|
|
365
|
+
* @example
|
|
366
|
+
* ```typescript
|
|
367
|
+
* class MyInteractionAdapter implements IInteraction {
|
|
368
|
+
* async like(videoId: string): Promise<void> {
|
|
369
|
+
* await api.post(`/videos/${videoId}/like`);
|
|
370
|
+
* }
|
|
371
|
+
*
|
|
372
|
+
* async unlike(videoId: string): Promise<void> {
|
|
373
|
+
* await api.delete(`/videos/${videoId}/like`);
|
|
374
|
+
* }
|
|
375
|
+
* }
|
|
376
|
+
* ```
|
|
377
|
+
*/
|
|
378
|
+
interface IInteraction {
|
|
379
|
+
/**
|
|
380
|
+
* Like a video
|
|
381
|
+
*
|
|
382
|
+
* @param videoId - ID of the video to like
|
|
383
|
+
* @returns Promise that resolves when like is persisted
|
|
384
|
+
* @throws Error if API fails (triggers rollback in OptimisticManager)
|
|
385
|
+
*
|
|
386
|
+
* Implementation notes:
|
|
387
|
+
* - Should be idempotent (liking twice = same result)
|
|
388
|
+
* - Throw error to trigger UI rollback
|
|
389
|
+
*/
|
|
390
|
+
like(videoId: string): Promise<void>;
|
|
391
|
+
/**
|
|
392
|
+
* Remove like from a video
|
|
393
|
+
*
|
|
394
|
+
* @param videoId - ID of the video to unlike
|
|
395
|
+
* @returns Promise that resolves when unlike is persisted
|
|
396
|
+
* @throws Error if API fails (triggers rollback in OptimisticManager)
|
|
397
|
+
*/
|
|
398
|
+
unlike(videoId: string): Promise<void>;
|
|
399
|
+
/**
|
|
400
|
+
* Follow a video author
|
|
401
|
+
*
|
|
402
|
+
* @param authorId - ID of the author to follow
|
|
403
|
+
* @returns Promise that resolves when follow is persisted
|
|
404
|
+
* @throws Error if API fails (triggers rollback in OptimisticManager)
|
|
405
|
+
*/
|
|
406
|
+
follow(authorId: string): Promise<void>;
|
|
407
|
+
/**
|
|
408
|
+
* Unfollow a video author
|
|
409
|
+
*
|
|
410
|
+
* @param authorId - ID of the author to unfollow
|
|
411
|
+
* @returns Promise that resolves when unfollow is persisted
|
|
412
|
+
* @throws Error if API fails (triggers rollback in OptimisticManager)
|
|
413
|
+
*/
|
|
414
|
+
unfollow(authorId: string): Promise<void>;
|
|
415
|
+
/**
|
|
416
|
+
* Post a comment on a video
|
|
417
|
+
*
|
|
418
|
+
* @param videoId - ID of the video to comment on
|
|
419
|
+
* @param text - Comment text content
|
|
420
|
+
* @returns Promise resolving to the created comment
|
|
421
|
+
* @throws Error if API fails
|
|
422
|
+
*
|
|
423
|
+
* Implementation notes:
|
|
424
|
+
* - Returns the full Comment object for UI display
|
|
425
|
+
* - Comment ID is generated by server
|
|
426
|
+
*/
|
|
427
|
+
comment(videoId: string, text: string): Promise<Comment>;
|
|
428
|
+
/**
|
|
429
|
+
* Delete a comment
|
|
430
|
+
*
|
|
431
|
+
* @param commentId - ID of the comment to delete
|
|
432
|
+
* @returns Promise that resolves when comment is deleted
|
|
433
|
+
* @throws Error if API fails or comment not found
|
|
434
|
+
*/
|
|
435
|
+
deleteComment(commentId: string): Promise<void>;
|
|
436
|
+
/**
|
|
437
|
+
* Like a comment
|
|
438
|
+
*
|
|
439
|
+
* @param commentId - ID of the comment to like
|
|
440
|
+
* @returns Promise that resolves when like is persisted
|
|
441
|
+
*/
|
|
442
|
+
likeComment(commentId: string): Promise<void>;
|
|
443
|
+
/**
|
|
444
|
+
* Unlike a comment
|
|
445
|
+
*
|
|
446
|
+
* @param commentId - ID of the comment to unlike
|
|
447
|
+
* @returns Promise that resolves when unlike is persisted
|
|
448
|
+
*/
|
|
449
|
+
unlikeComment(commentId: string): Promise<void>;
|
|
450
|
+
/**
|
|
451
|
+
* Share a video (optional tracking)
|
|
452
|
+
*
|
|
453
|
+
* @param videoId - ID of the video being shared
|
|
454
|
+
* @param platform - Optional platform identifier (e.g., 'facebook', 'twitter')
|
|
455
|
+
* @returns Promise that resolves when share is tracked
|
|
456
|
+
*
|
|
457
|
+
* Implementation notes:
|
|
458
|
+
* - This is primarily for tracking/analytics
|
|
459
|
+
* - Actual share mechanism is handled by Host App
|
|
460
|
+
*/
|
|
461
|
+
share?(videoId: string, platform?: string): Promise<void>;
|
|
462
|
+
/**
|
|
463
|
+
* Report a video
|
|
464
|
+
*
|
|
465
|
+
* @param videoId - ID of the video to report
|
|
466
|
+
* @param reason - Report reason code
|
|
467
|
+
* @param description - Optional additional description
|
|
468
|
+
* @returns Promise that resolves when report is submitted
|
|
469
|
+
*/
|
|
470
|
+
report?(videoId: string, reason: string, description?: string): Promise<void>;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* IStorage - Storage Port Interface
|
|
475
|
+
*
|
|
476
|
+
* This interface defines the contract for persistent storage.
|
|
477
|
+
* Used by Lifecycle Manager for state restoration when user returns.
|
|
478
|
+
*
|
|
479
|
+
* Implementations:
|
|
480
|
+
* - LocalStorageAdapter: Uses browser localStorage
|
|
481
|
+
* - HybridStorageAdapter: Uses Flutter native storage via bridge
|
|
482
|
+
* - MemoryStorageAdapter: In-memory (for testing)
|
|
483
|
+
*
|
|
484
|
+
* Key Design Decision:
|
|
485
|
+
* - Storage is simple key-value
|
|
486
|
+
* - Complex objects are JSON serialized by the adapter
|
|
487
|
+
* - SDK stores session snapshots for state restoration
|
|
488
|
+
*
|
|
489
|
+
* @example
|
|
490
|
+
* ```typescript
|
|
491
|
+
* class LocalStorageAdapter implements IStorage {
|
|
492
|
+
* async get<T>(key: string): Promise<T | null> {
|
|
493
|
+
* const value = localStorage.getItem(key);
|
|
494
|
+
* return value ? JSON.parse(value) : null;
|
|
495
|
+
* }
|
|
496
|
+
*
|
|
497
|
+
* async set<T>(key: string, value: T): Promise<void> {
|
|
498
|
+
* localStorage.setItem(key, JSON.stringify(value));
|
|
499
|
+
* }
|
|
500
|
+
* }
|
|
501
|
+
* ```
|
|
502
|
+
*/
|
|
503
|
+
interface IStorage {
|
|
504
|
+
/**
|
|
505
|
+
* Get a value from storage
|
|
506
|
+
*
|
|
507
|
+
* @param key - Storage key
|
|
508
|
+
* @returns Promise resolving to the stored value or null if not found
|
|
509
|
+
*
|
|
510
|
+
* Implementation notes:
|
|
511
|
+
* - Should return null (not throw) if key doesn't exist
|
|
512
|
+
* - Handle JSON parse errors gracefully (return null)
|
|
513
|
+
*/
|
|
514
|
+
get<T>(key: string): Promise<T | null>;
|
|
515
|
+
/**
|
|
516
|
+
* Set a value in storage
|
|
517
|
+
*
|
|
518
|
+
* @param key - Storage key
|
|
519
|
+
* @param value - Value to store (will be serialized)
|
|
520
|
+
* @returns Promise that resolves when value is persisted
|
|
521
|
+
* @throws Error if storage is full (QuotaExceededError)
|
|
522
|
+
*
|
|
523
|
+
* Implementation notes:
|
|
524
|
+
* - Handle QuotaExceededError by implementing LRU eviction
|
|
525
|
+
* - Serialize complex objects to JSON
|
|
526
|
+
*/
|
|
527
|
+
set<T>(key: string, value: T): Promise<void>;
|
|
528
|
+
/**
|
|
529
|
+
* Remove a value from storage
|
|
530
|
+
*
|
|
531
|
+
* @param key - Storage key to remove
|
|
532
|
+
* @returns Promise that resolves when value is removed
|
|
533
|
+
*
|
|
534
|
+
* Implementation notes:
|
|
535
|
+
* - Should not throw if key doesn't exist
|
|
536
|
+
*/
|
|
537
|
+
remove(key: string): Promise<void>;
|
|
538
|
+
/**
|
|
539
|
+
* Clear all SDK-related storage
|
|
540
|
+
*
|
|
541
|
+
* @returns Promise that resolves when storage is cleared
|
|
542
|
+
*
|
|
543
|
+
* Implementation notes:
|
|
544
|
+
* - Should only clear SDK-namespaced keys
|
|
545
|
+
* - Do not clear unrelated Host App storage
|
|
546
|
+
*/
|
|
547
|
+
clear(): Promise<void>;
|
|
548
|
+
/**
|
|
549
|
+
* Get all keys in storage (for debugging/maintenance)
|
|
550
|
+
*
|
|
551
|
+
* @returns Promise resolving to array of storage keys
|
|
552
|
+
*
|
|
553
|
+
* Implementation notes:
|
|
554
|
+
* - Only return SDK-namespaced keys
|
|
555
|
+
* - Used for debugging and LRU eviction
|
|
556
|
+
*/
|
|
557
|
+
keys(): Promise<string[]>;
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Specialized interface for session snapshot storage
|
|
561
|
+
* Extends base IStorage with type-safe session methods
|
|
562
|
+
*/
|
|
563
|
+
interface ISessionStorage extends IStorage {
|
|
564
|
+
/**
|
|
565
|
+
* Save session snapshot for state restoration
|
|
566
|
+
*
|
|
567
|
+
* @param snapshot - Session state to persist
|
|
568
|
+
* @returns Promise that resolves when saved
|
|
569
|
+
*
|
|
570
|
+
* Implementation notes:
|
|
571
|
+
* - Called when user navigates away
|
|
572
|
+
* - Should handle QuotaExceeded by clearing old snapshots
|
|
573
|
+
*/
|
|
574
|
+
saveSnapshot(snapshot: SessionSnapshot): Promise<void>;
|
|
575
|
+
/**
|
|
576
|
+
* Load the most recent session snapshot
|
|
577
|
+
*
|
|
578
|
+
* @returns Promise resolving to snapshot or null if none exists
|
|
579
|
+
*
|
|
580
|
+
* Implementation notes:
|
|
581
|
+
* - Called when SDK mounts
|
|
582
|
+
* - May return stale snapshot - let SDK decide if valid
|
|
583
|
+
*/
|
|
584
|
+
loadSnapshot(): Promise<SessionSnapshot | null>;
|
|
585
|
+
/**
|
|
586
|
+
* Clear session snapshot
|
|
587
|
+
*
|
|
588
|
+
* @returns Promise that resolves when cleared
|
|
589
|
+
*
|
|
590
|
+
* Implementation notes:
|
|
591
|
+
* - Called when session is explicitly ended
|
|
592
|
+
* - Or when snapshot is too old to be useful
|
|
593
|
+
*/
|
|
594
|
+
clearSnapshot(): Promise<void>;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* IAnalytics - Analytics Port Interface
|
|
599
|
+
*
|
|
600
|
+
* This interface defines the contract for analytics tracking.
|
|
601
|
+
* Uses batching strategy - events are queued and sent in batches.
|
|
602
|
+
*
|
|
603
|
+
* Batching Strategy (per ADD):
|
|
604
|
+
* 1. Events are pushed to internal queue
|
|
605
|
+
* 2. Flush conditions:
|
|
606
|
+
* - Queue > 10 items
|
|
607
|
+
* - User changes video
|
|
608
|
+
* - visibilitychange (user tabs away)
|
|
609
|
+
* 3. Uses navigator.sendBeacon for reliability
|
|
610
|
+
*
|
|
611
|
+
* @example
|
|
612
|
+
* ```typescript
|
|
613
|
+
* class MyAnalyticsAdapter implements IAnalytics {
|
|
614
|
+
* private queue: AnalyticsEvent[] = [];
|
|
615
|
+
*
|
|
616
|
+
* track(event: AnalyticsEvent): void {
|
|
617
|
+
* this.queue.push(event);
|
|
618
|
+
* if (this.queue.length >= 10) {
|
|
619
|
+
* this.flush();
|
|
620
|
+
* }
|
|
621
|
+
* }
|
|
622
|
+
*
|
|
623
|
+
* async flush(): Promise<void> {
|
|
624
|
+
* if (this.queue.length === 0) return;
|
|
625
|
+
*
|
|
626
|
+
* const events = [...this.queue];
|
|
627
|
+
* this.queue = [];
|
|
628
|
+
*
|
|
629
|
+
* navigator.sendBeacon('/analytics', JSON.stringify(events));
|
|
630
|
+
* }
|
|
631
|
+
* }
|
|
632
|
+
* ```
|
|
633
|
+
*/
|
|
634
|
+
interface IAnalytics {
|
|
635
|
+
/**
|
|
636
|
+
* Track an analytics event
|
|
637
|
+
*
|
|
638
|
+
* @param event - Event to track
|
|
639
|
+
*
|
|
640
|
+
* Implementation notes:
|
|
641
|
+
* - Should NOT throw errors (fire-and-forget)
|
|
642
|
+
* - Queue events internally for batching
|
|
643
|
+
* - Don't block main thread
|
|
644
|
+
*/
|
|
645
|
+
track(event: AnalyticsEvent): void;
|
|
646
|
+
/**
|
|
647
|
+
* Flush queued events immediately
|
|
648
|
+
*
|
|
649
|
+
* @returns Promise that resolves when flush is complete
|
|
650
|
+
*
|
|
651
|
+
* Implementation notes:
|
|
652
|
+
* - Called on visibilitychange
|
|
653
|
+
* - Called when user changes video
|
|
654
|
+
* - Use navigator.sendBeacon for reliability
|
|
655
|
+
* - Should not throw - fail silently
|
|
656
|
+
*/
|
|
657
|
+
flush(): Promise<void>;
|
|
658
|
+
/**
|
|
659
|
+
* Track video view duration
|
|
660
|
+
*
|
|
661
|
+
* @param videoId - ID of the video
|
|
662
|
+
* @param duration - Watch duration in seconds
|
|
663
|
+
* @param totalDuration - Total video duration in seconds
|
|
664
|
+
*
|
|
665
|
+
* Implementation notes:
|
|
666
|
+
* - Called periodically during playback (heartbeat)
|
|
667
|
+
* - Used to calculate engagement metrics
|
|
668
|
+
*/
|
|
669
|
+
trackViewDuration(videoId: string, duration: number, totalDuration: number): void;
|
|
670
|
+
/**
|
|
671
|
+
* Track video completion
|
|
672
|
+
*
|
|
673
|
+
* @param videoId - ID of the video
|
|
674
|
+
* @param watchTime - Total time watched in seconds
|
|
675
|
+
* @param loops - Number of times video looped
|
|
676
|
+
*
|
|
677
|
+
* Implementation notes:
|
|
678
|
+
* - Called when video ends or user navigates away
|
|
679
|
+
* - loops > 0 means user watched multiple times
|
|
680
|
+
*/
|
|
681
|
+
trackCompletion(videoId: string, watchTime: number, loops: number): void;
|
|
682
|
+
/**
|
|
683
|
+
* Set user context for analytics
|
|
684
|
+
*
|
|
685
|
+
* @param userId - Optional user ID (null for anonymous)
|
|
686
|
+
* @param properties - Optional user properties
|
|
687
|
+
*
|
|
688
|
+
* Implementation notes:
|
|
689
|
+
* - Called once when SDK initializes
|
|
690
|
+
* - Properties are attached to all subsequent events
|
|
691
|
+
*/
|
|
692
|
+
setUser?(userId: string | null, properties?: Record<string, unknown>): void;
|
|
693
|
+
/**
|
|
694
|
+
* Get current queue size (for debugging)
|
|
695
|
+
*
|
|
696
|
+
* @returns Number of events in queue
|
|
697
|
+
*/
|
|
698
|
+
getQueueSize?(): number;
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Analytics configuration options
|
|
702
|
+
*/
|
|
703
|
+
interface AnalyticsConfig {
|
|
704
|
+
/** Batch size threshold for auto-flush (default: 10) */
|
|
705
|
+
batchSize?: number;
|
|
706
|
+
/** Flush interval in milliseconds (default: 30000) */
|
|
707
|
+
flushInterval?: number;
|
|
708
|
+
/** Endpoint URL for sending events */
|
|
709
|
+
endpoint?: string;
|
|
710
|
+
/** Whether to use sendBeacon API (default: true) */
|
|
711
|
+
useSendBeacon?: boolean;
|
|
712
|
+
/** Whether to track automatically (default: true) */
|
|
713
|
+
autoTrack?: boolean;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* ILogger - Logger Port Interface
|
|
718
|
+
*
|
|
719
|
+
* This interface defines the contract for logging.
|
|
720
|
+
* SDK uses internal Circular Buffer Logger pattern.
|
|
721
|
+
*
|
|
722
|
+
* Design (per ADD - Observability Strategy):
|
|
723
|
+
* - Maintain circular buffer of last 50 log entries in RAM
|
|
724
|
+
* - Normal operation: Only write to buffer (silent)
|
|
725
|
+
* - On error: Flush buffer + error to this adapter (loud)
|
|
726
|
+
*
|
|
727
|
+
* This gives context for debugging ("what happened before crash")
|
|
728
|
+
* without constant network traffic.
|
|
729
|
+
*
|
|
730
|
+
* @example
|
|
731
|
+
* ```typescript
|
|
732
|
+
* class ConsoleLoggerAdapter implements ILogger {
|
|
733
|
+
* log(entry: LogEntry): void {
|
|
734
|
+
* const method = entry.level === 'error' ? 'error' : 'log';
|
|
735
|
+
* console[method](`[${entry.level}] ${entry.message}`, entry.context);
|
|
736
|
+
* }
|
|
737
|
+
*
|
|
738
|
+
* flush(entries: LogEntry[]): void {
|
|
739
|
+
* console.group('SDK Log Flush');
|
|
740
|
+
* entries.forEach(e => this.log(e));
|
|
741
|
+
* console.groupEnd();
|
|
742
|
+
* }
|
|
743
|
+
* }
|
|
744
|
+
* ```
|
|
745
|
+
*/
|
|
746
|
+
interface ILogger {
|
|
747
|
+
/**
|
|
748
|
+
* Log a single entry
|
|
749
|
+
*
|
|
750
|
+
* @param entry - Log entry to record
|
|
751
|
+
*
|
|
752
|
+
* Implementation notes:
|
|
753
|
+
* - Should not throw errors
|
|
754
|
+
* - Host App decides where logs go (console, Sentry, Firebase, etc.)
|
|
755
|
+
*/
|
|
756
|
+
log(entry: LogEntry): void;
|
|
757
|
+
/**
|
|
758
|
+
* Flush multiple log entries (on error)
|
|
759
|
+
*
|
|
760
|
+
* @param entries - Array of recent log entries for context
|
|
761
|
+
*
|
|
762
|
+
* Implementation notes:
|
|
763
|
+
* - Called when SDK encounters an error
|
|
764
|
+
* - Entries are the last N items from circular buffer
|
|
765
|
+
* - Use this to send to error tracking service (Sentry, etc.)
|
|
766
|
+
*/
|
|
767
|
+
flush(entries: LogEntry[]): void;
|
|
768
|
+
/**
|
|
769
|
+
* Log debug message (convenience method)
|
|
770
|
+
*
|
|
771
|
+
* @param message - Debug message
|
|
772
|
+
* @param context - Optional context data
|
|
773
|
+
*/
|
|
774
|
+
debug(message: string, context?: Record<string, unknown>): void;
|
|
775
|
+
/**
|
|
776
|
+
* Log info message (convenience method)
|
|
777
|
+
*
|
|
778
|
+
* @param message - Info message
|
|
779
|
+
* @param context - Optional context data
|
|
780
|
+
*/
|
|
781
|
+
info(message: string, context?: Record<string, unknown>): void;
|
|
782
|
+
/**
|
|
783
|
+
* Log warning message (convenience method)
|
|
784
|
+
*
|
|
785
|
+
* @param message - Warning message
|
|
786
|
+
* @param context - Optional context data
|
|
787
|
+
*/
|
|
788
|
+
warn(message: string, context?: Record<string, unknown>): void;
|
|
789
|
+
/**
|
|
790
|
+
* Log error message (convenience method)
|
|
791
|
+
*
|
|
792
|
+
* @param message - Error message
|
|
793
|
+
* @param error - Optional Error object
|
|
794
|
+
* @param context - Optional context data
|
|
795
|
+
*/
|
|
796
|
+
error(message: string, error?: Error, context?: Record<string, unknown>): void;
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Logger configuration options
|
|
800
|
+
*/
|
|
801
|
+
interface LoggerConfig {
|
|
802
|
+
/** Minimum log level to record (default: 'info' in prod, 'debug' in dev) */
|
|
803
|
+
minLevel?: LogLevel;
|
|
804
|
+
/** Maximum entries in circular buffer (default: 50) */
|
|
805
|
+
bufferSize?: number;
|
|
806
|
+
/** Whether to also log to console in dev mode (default: true) */
|
|
807
|
+
consoleInDev?: boolean;
|
|
808
|
+
/** Whether to include timestamps (default: true) */
|
|
809
|
+
includeTimestamp?: boolean;
|
|
810
|
+
/** Whether to redact sensitive data (default: true) */
|
|
811
|
+
redactSensitive?: boolean;
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Internal logger instance type
|
|
815
|
+
* Used by SDK internal components
|
|
816
|
+
*/
|
|
817
|
+
interface InternalLogger {
|
|
818
|
+
/** Log at debug level */
|
|
819
|
+
debug(message: string, context?: Record<string, unknown>): void;
|
|
820
|
+
/** Log at info level */
|
|
821
|
+
info(message: string, context?: Record<string, unknown>): void;
|
|
822
|
+
/** Log at warn level */
|
|
823
|
+
warn(message: string, context?: Record<string, unknown>): void;
|
|
824
|
+
/** Log at error level - triggers flush */
|
|
825
|
+
error(message: string, error?: Error, context?: Record<string, unknown>): void;
|
|
826
|
+
/** Get buffer contents (for debugging) */
|
|
827
|
+
getBuffer(): LogEntry[];
|
|
828
|
+
/** Manually trigger flush */
|
|
829
|
+
forceFlush(): void;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* Network Adapter Interface
|
|
834
|
+
*
|
|
835
|
+
* Provides network information for adaptive prefetch/preload strategies.
|
|
836
|
+
* Implementations can use Web API (navigator.connection) or native bridge.
|
|
837
|
+
*/
|
|
838
|
+
/**
|
|
839
|
+
* Network connection type
|
|
840
|
+
*/
|
|
841
|
+
type NetworkType = 'wifi' | 'cellular' | '4g' | '3g' | 'slow' | 'offline';
|
|
842
|
+
/**
|
|
843
|
+
* Network quality metrics
|
|
844
|
+
*/
|
|
845
|
+
interface NetworkQuality {
|
|
846
|
+
/** Effective connection type */
|
|
847
|
+
type: NetworkType;
|
|
848
|
+
/** Estimated downlink speed in Mbps */
|
|
849
|
+
downlink?: number;
|
|
850
|
+
/** Round-trip time in ms */
|
|
851
|
+
rtt?: number;
|
|
852
|
+
/** Whether connection is metered (cellular data) */
|
|
853
|
+
isMetered?: boolean;
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Network adapter interface (Port)
|
|
857
|
+
*
|
|
858
|
+
* Abstracts network detection for cross-platform support:
|
|
859
|
+
* - Web: Uses Navigator.connection API (with fallbacks)
|
|
860
|
+
* - Flutter WebView: Uses native bridge for accurate info
|
|
861
|
+
*
|
|
862
|
+
* @example
|
|
863
|
+
* ```typescript
|
|
864
|
+
* // Web implementation
|
|
865
|
+
* class WebNetworkAdapter implements INetworkAdapter {
|
|
866
|
+
* async getNetworkType() {
|
|
867
|
+
* const conn = navigator.connection;
|
|
868
|
+
* return conn?.effectiveType ?? 'wifi';
|
|
869
|
+
* }
|
|
870
|
+
* }
|
|
871
|
+
*
|
|
872
|
+
* // Flutter bridge implementation
|
|
873
|
+
* class FlutterNetworkAdapter implements INetworkAdapter {
|
|
874
|
+
* async getNetworkType() {
|
|
875
|
+
* return await flutterBridge.getNetworkType();
|
|
876
|
+
* }
|
|
877
|
+
* }
|
|
878
|
+
* ```
|
|
879
|
+
*/
|
|
880
|
+
interface INetworkAdapter {
|
|
881
|
+
/**
|
|
882
|
+
* Get current effective network type
|
|
883
|
+
*
|
|
884
|
+
* @returns Promise resolving to network type
|
|
885
|
+
*/
|
|
886
|
+
getNetworkType(): Promise<NetworkType>;
|
|
887
|
+
/**
|
|
888
|
+
* Get detailed network quality metrics
|
|
889
|
+
*
|
|
890
|
+
* @returns Promise resolving to quality metrics
|
|
891
|
+
*/
|
|
892
|
+
getNetworkQuality(): Promise<NetworkQuality>;
|
|
893
|
+
/**
|
|
894
|
+
* Subscribe to network changes
|
|
895
|
+
*
|
|
896
|
+
* @param callback - Called when network type changes
|
|
897
|
+
* @returns Unsubscribe function
|
|
898
|
+
*/
|
|
899
|
+
onNetworkChange(callback: (type: NetworkType) => void): () => void;
|
|
900
|
+
/**
|
|
901
|
+
* Check if currently online
|
|
902
|
+
*
|
|
903
|
+
* @returns Promise resolving to online status
|
|
904
|
+
*/
|
|
905
|
+
isOnline(): Promise<boolean>;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
/**
|
|
909
|
+
* Video Loader Interface
|
|
910
|
+
*
|
|
911
|
+
* Abstracts video preloading for different video types (MP4 vs HLS).
|
|
912
|
+
* ResourceGovernor uses this to prefetch videos without knowing implementation details.
|
|
913
|
+
*/
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Preload status for a video
|
|
917
|
+
*/
|
|
918
|
+
type PreloadStatus = 'idle' | 'loading' | 'ready' | 'error';
|
|
919
|
+
/**
|
|
920
|
+
* Preload result with status and optional error
|
|
921
|
+
*/
|
|
922
|
+
interface PreloadResult {
|
|
923
|
+
/** Video ID */
|
|
924
|
+
videoId: string;
|
|
925
|
+
/** Preload status */
|
|
926
|
+
status: PreloadStatus;
|
|
927
|
+
/** Preloaded bytes (if available) */
|
|
928
|
+
loadedBytes?: number;
|
|
929
|
+
/** Error if preload failed */
|
|
930
|
+
error?: Error;
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* Preload configuration
|
|
934
|
+
*/
|
|
935
|
+
interface PreloadConfig {
|
|
936
|
+
/** Priority (higher = more important) */
|
|
937
|
+
priority?: number;
|
|
938
|
+
/** Maximum bytes to preload (for range requests) */
|
|
939
|
+
maxBytes?: number;
|
|
940
|
+
/** Timeout in ms */
|
|
941
|
+
timeout?: number;
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Video loader interface (Port)
|
|
945
|
+
*
|
|
946
|
+
* Abstracts video preloading strategy based on video type:
|
|
947
|
+
*
|
|
948
|
+
* - **MP4:** Uses fetch with Range header to preload first N bytes
|
|
949
|
+
* - **HLS:** Uses hls.js API (startLoad) to preload initial segments
|
|
950
|
+
* - **Native HLS (Safari):** Uses video.preload="auto" with ghost video element
|
|
951
|
+
*
|
|
952
|
+
* @example
|
|
953
|
+
* ```typescript
|
|
954
|
+
* // ResourceGovernor uses IVideoLoader without knowing MP4 vs HLS
|
|
955
|
+
* class ResourceGovernor {
|
|
956
|
+
* constructor(private videoLoader: IVideoLoader) {}
|
|
957
|
+
*
|
|
958
|
+
* async prefetchNext(videoId: string, source: VideoSource) {
|
|
959
|
+
* if (this.networkType === 'wifi') {
|
|
960
|
+
* await this.videoLoader.preload(videoId, source);
|
|
961
|
+
* }
|
|
962
|
+
* }
|
|
963
|
+
* }
|
|
964
|
+
* ```
|
|
965
|
+
*/
|
|
966
|
+
interface IVideoLoader {
|
|
967
|
+
/**
|
|
968
|
+
* Preload a video source
|
|
969
|
+
*
|
|
970
|
+
* Implementation varies by source type:
|
|
971
|
+
* - MP4: Fetch first chunk (500KB-1MB)
|
|
972
|
+
* - HLS: Load manifest + first segment via hls.js
|
|
973
|
+
*
|
|
974
|
+
* @param videoId - Unique video identifier
|
|
975
|
+
* @param source - Video source configuration
|
|
976
|
+
* @param config - Optional preload configuration
|
|
977
|
+
* @returns Promise resolving to preload result
|
|
978
|
+
*/
|
|
979
|
+
preload(videoId: string, source: VideoSource, config?: PreloadConfig): Promise<PreloadResult>;
|
|
980
|
+
/**
|
|
981
|
+
* Cancel an in-progress preload
|
|
982
|
+
*
|
|
983
|
+
* @param videoId - Video ID to cancel
|
|
984
|
+
*/
|
|
985
|
+
cancelPreload(videoId: string): void;
|
|
986
|
+
/**
|
|
987
|
+
* Check if a video is preloaded
|
|
988
|
+
*
|
|
989
|
+
* @param videoId - Video ID to check
|
|
990
|
+
* @returns Whether video data is cached/preloaded
|
|
991
|
+
*/
|
|
992
|
+
isPreloaded(videoId: string): boolean;
|
|
993
|
+
/**
|
|
994
|
+
* Get preload status for a video
|
|
995
|
+
*
|
|
996
|
+
* @param videoId - Video ID to check
|
|
997
|
+
* @returns Current preload status
|
|
998
|
+
*/
|
|
999
|
+
getPreloadStatus(videoId: string): PreloadStatus;
|
|
1000
|
+
/**
|
|
1001
|
+
* Clear preloaded data for a video (memory management)
|
|
1002
|
+
*
|
|
1003
|
+
* @param videoId - Video ID to clear
|
|
1004
|
+
*/
|
|
1005
|
+
clearPreload(videoId: string): void;
|
|
1006
|
+
/**
|
|
1007
|
+
* Clear all preloaded data
|
|
1008
|
+
*/
|
|
1009
|
+
clearAll(): void;
|
|
1010
|
+
/**
|
|
1011
|
+
* Get total preloaded bytes (for memory monitoring)
|
|
1012
|
+
*
|
|
1013
|
+
* @returns Total bytes currently preloaded
|
|
1014
|
+
*/
|
|
1015
|
+
getTotalPreloadedBytes(): number;
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Poster/thumbnail preloader interface
|
|
1019
|
+
*
|
|
1020
|
+
* Separate from video preloader for simpler implementation
|
|
1021
|
+
* and to ensure posters are always loaded (even on slow networks)
|
|
1022
|
+
*/
|
|
1023
|
+
interface IPosterLoader {
|
|
1024
|
+
/**
|
|
1025
|
+
* Preload a poster image
|
|
1026
|
+
*
|
|
1027
|
+
* @param url - Poster image URL
|
|
1028
|
+
* @returns Promise resolving when image is loaded
|
|
1029
|
+
*/
|
|
1030
|
+
preload(url: string): Promise<void>;
|
|
1031
|
+
/**
|
|
1032
|
+
* Check if poster is cached
|
|
1033
|
+
*
|
|
1034
|
+
* @param url - Poster URL to check
|
|
1035
|
+
* @returns Whether poster is in cache
|
|
1036
|
+
*/
|
|
1037
|
+
isCached(url: string): boolean;
|
|
1038
|
+
/**
|
|
1039
|
+
* Clear poster cache
|
|
1040
|
+
*/
|
|
1041
|
+
clearCache(): void;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/**
|
|
1045
|
+
* UI Component Types for XHub-short SDK
|
|
1046
|
+
*
|
|
1047
|
+
* These types define the props for headless UI components and their wired versions.
|
|
1048
|
+
* Used by:
|
|
1049
|
+
* - @xhub-short/ui (headless components receive these as props)
|
|
1050
|
+
* - @xhub-short/sdk (wired components inject these via HOC)
|
|
1051
|
+
*
|
|
1052
|
+
* Architecture: Two-Tier Export Pattern (Headless UI + SDK Wiring)
|
|
1053
|
+
*
|
|
1054
|
+
* NOTE: This package has NO DEPENDENCIES. React types are defined as generics
|
|
1055
|
+
* and will be resolved by the consuming package (@xhub-short/ui or @xhub-short/sdk).
|
|
1056
|
+
*/
|
|
1057
|
+
|
|
1058
|
+
/**
|
|
1059
|
+
* Generic ReactNode type (resolved by consuming package)
|
|
1060
|
+
* Use React.ReactNode when importing in @xhub-short/ui
|
|
1061
|
+
*/
|
|
1062
|
+
type UINode = unknown;
|
|
1063
|
+
/**
|
|
1064
|
+
* Generic React ref type
|
|
1065
|
+
*/
|
|
1066
|
+
type UIRef<T> = {
|
|
1067
|
+
current: T | null;
|
|
1068
|
+
} | ((instance: T | null) => void);
|
|
1069
|
+
/**
|
|
1070
|
+
* Generic React component type
|
|
1071
|
+
*/
|
|
1072
|
+
type UIComponent<P = object> = (props: P) => UINode;
|
|
1073
|
+
/**
|
|
1074
|
+
* Feed state from useFeed() hook
|
|
1075
|
+
*/
|
|
1076
|
+
interface UIFeedState {
|
|
1077
|
+
/** List of video items */
|
|
1078
|
+
videos: VideoItem[];
|
|
1079
|
+
/** Whether feed is loading */
|
|
1080
|
+
isLoading: boolean;
|
|
1081
|
+
/** Whether there are more items to load */
|
|
1082
|
+
hasMore: boolean;
|
|
1083
|
+
/** Error if any */
|
|
1084
|
+
error: Error | null;
|
|
1085
|
+
/** Load more items */
|
|
1086
|
+
loadMore: () => Promise<void>;
|
|
1087
|
+
/** Refresh feed */
|
|
1088
|
+
refresh: () => Promise<void>;
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Swipe/scroll state from useSwipeGesture() hook
|
|
1092
|
+
*/
|
|
1093
|
+
interface UISwipeState {
|
|
1094
|
+
/** Current active video index */
|
|
1095
|
+
activeIndex: number;
|
|
1096
|
+
/** Whether user is currently swiping */
|
|
1097
|
+
isSwiping: boolean;
|
|
1098
|
+
/** Drag offset in pixels (during swipe) */
|
|
1099
|
+
dragOffset: number;
|
|
1100
|
+
/** Swipe direction */
|
|
1101
|
+
direction: 'up' | 'down' | null;
|
|
1102
|
+
/** Go to specific index */
|
|
1103
|
+
goToIndex: (index: number) => void;
|
|
1104
|
+
/** Go to next video */
|
|
1105
|
+
goToNext: () => void;
|
|
1106
|
+
/** Go to previous video */
|
|
1107
|
+
goToPrev: () => void;
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Resource allocation state from useResourceAllocation() hook
|
|
1111
|
+
*/
|
|
1112
|
+
interface UIResourceState {
|
|
1113
|
+
/** Whether this slot has an allocated video DOM node */
|
|
1114
|
+
isAllocated: boolean;
|
|
1115
|
+
/** Whether video data is preloaded */
|
|
1116
|
+
isPreloaded: boolean;
|
|
1117
|
+
/** Whether this is the active (focused) slot */
|
|
1118
|
+
isActive: boolean;
|
|
1119
|
+
/** Distance from active slot (0 = active, 1 = adjacent, etc.) */
|
|
1120
|
+
distance: number;
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* Player state from usePlayerForVideo() hook
|
|
1124
|
+
*/
|
|
1125
|
+
interface UIPlayerState {
|
|
1126
|
+
/** Current playback state */
|
|
1127
|
+
state: PlayerState['playbackState'];
|
|
1128
|
+
/** Current time in seconds */
|
|
1129
|
+
currentTime: number;
|
|
1130
|
+
/** Total duration in seconds */
|
|
1131
|
+
duration: number;
|
|
1132
|
+
/** Buffered percentage (0-1) */
|
|
1133
|
+
buffered: number;
|
|
1134
|
+
/** Whether video is muted */
|
|
1135
|
+
isMuted: boolean;
|
|
1136
|
+
/** Volume level (0-1) */
|
|
1137
|
+
volume: number;
|
|
1138
|
+
/** Whether video is playing */
|
|
1139
|
+
isPlaying: boolean;
|
|
1140
|
+
/** Whether video is loading */
|
|
1141
|
+
isLoading: boolean;
|
|
1142
|
+
/** Error if any */
|
|
1143
|
+
error: Error | null;
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Player controls from usePlayerForVideo() hook
|
|
1147
|
+
*/
|
|
1148
|
+
interface UIPlayerControls {
|
|
1149
|
+
/** Play video */
|
|
1150
|
+
play: () => void;
|
|
1151
|
+
/** Pause video */
|
|
1152
|
+
pause: () => void;
|
|
1153
|
+
/** Toggle play/pause */
|
|
1154
|
+
toggle: () => void;
|
|
1155
|
+
/** Seek to time in seconds */
|
|
1156
|
+
seek: (time: number) => void;
|
|
1157
|
+
/** Set volume (0-1) */
|
|
1158
|
+
setVolume: (volume: number) => void;
|
|
1159
|
+
/** Toggle mute */
|
|
1160
|
+
toggleMute: () => void;
|
|
1161
|
+
/** Set muted state */
|
|
1162
|
+
setMuted: (muted: boolean) => void;
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* Interaction state from useOptimistic() hook
|
|
1166
|
+
*/
|
|
1167
|
+
interface UIInteractionState {
|
|
1168
|
+
/** Whether video is liked by current user */
|
|
1169
|
+
isLiked: boolean;
|
|
1170
|
+
/** Like count */
|
|
1171
|
+
likeCount: number;
|
|
1172
|
+
/** Comment count */
|
|
1173
|
+
commentCount: number;
|
|
1174
|
+
/** Share count */
|
|
1175
|
+
shareCount: number;
|
|
1176
|
+
/** Whether like action is pending */
|
|
1177
|
+
isLikePending: boolean;
|
|
1178
|
+
}
|
|
1179
|
+
/**
|
|
1180
|
+
* Interaction actions from useOptimistic() hook
|
|
1181
|
+
*/
|
|
1182
|
+
interface UIInteractionActions {
|
|
1183
|
+
/** Toggle like state */
|
|
1184
|
+
toggleLike: () => Promise<void>;
|
|
1185
|
+
/** Share video */
|
|
1186
|
+
share: () => Promise<void>;
|
|
1187
|
+
/** Open comments */
|
|
1188
|
+
openComments: () => void;
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Author interaction state from useOptimistic() hook
|
|
1192
|
+
*/
|
|
1193
|
+
interface UIAuthorState {
|
|
1194
|
+
/** Author info */
|
|
1195
|
+
author: Author;
|
|
1196
|
+
/** Whether current user is following */
|
|
1197
|
+
isFollowing: boolean;
|
|
1198
|
+
/** Whether follow action is pending */
|
|
1199
|
+
isFollowPending: boolean;
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Author interaction actions from useOptimistic() hook
|
|
1203
|
+
*/
|
|
1204
|
+
interface UIAuthorActions {
|
|
1205
|
+
/** Toggle follow state */
|
|
1206
|
+
toggleFollow: () => Promise<void>;
|
|
1207
|
+
/** Open author profile */
|
|
1208
|
+
openProfile: () => void;
|
|
1209
|
+
}
|
|
1210
|
+
/**
|
|
1211
|
+
* VideoFeed headless component props
|
|
1212
|
+
*/
|
|
1213
|
+
interface VideoFeedHeadlessProps {
|
|
1214
|
+
/** Feed state (videos, loading, etc.) */
|
|
1215
|
+
feedState: UIFeedState;
|
|
1216
|
+
/** Swipe/scroll state */
|
|
1217
|
+
swipeState: UISwipeState;
|
|
1218
|
+
/** Container height (usually 100vh) */
|
|
1219
|
+
height?: number | string;
|
|
1220
|
+
/** Additional CSS classes */
|
|
1221
|
+
className?: string;
|
|
1222
|
+
/** Render function for each video slot */
|
|
1223
|
+
renderSlot: (video: VideoItem, index: number) => UINode;
|
|
1224
|
+
/** Called when active index changes */
|
|
1225
|
+
onIndexChange?: (index: number) => void;
|
|
1226
|
+
/** Called when reaching near end of feed */
|
|
1227
|
+
onEndReached?: () => void;
|
|
1228
|
+
/** Threshold for triggering onEndReached (default: 2) */
|
|
1229
|
+
endReachedThreshold?: number;
|
|
1230
|
+
}
|
|
1231
|
+
/**
|
|
1232
|
+
* VideoSlot headless component props
|
|
1233
|
+
*/
|
|
1234
|
+
interface VideoSlotHeadlessProps {
|
|
1235
|
+
/** Video item to display */
|
|
1236
|
+
video: VideoItem;
|
|
1237
|
+
/** Resource allocation state */
|
|
1238
|
+
resourceState: UIResourceState;
|
|
1239
|
+
/** Player state */
|
|
1240
|
+
playerState: UIPlayerState;
|
|
1241
|
+
/** Player controls */
|
|
1242
|
+
playerControls: UIPlayerControls;
|
|
1243
|
+
/** Additional CSS classes */
|
|
1244
|
+
className?: string;
|
|
1245
|
+
/** Children (overlay content) */
|
|
1246
|
+
children?: UINode;
|
|
1247
|
+
/** Called when video becomes visible */
|
|
1248
|
+
onVisible?: () => void;
|
|
1249
|
+
/** Called when video becomes hidden */
|
|
1250
|
+
onHidden?: () => void;
|
|
1251
|
+
}
|
|
1252
|
+
/**
|
|
1253
|
+
* VideoPlayer headless component props
|
|
1254
|
+
*/
|
|
1255
|
+
interface VideoPlayerHeadlessProps {
|
|
1256
|
+
/** Video source URL */
|
|
1257
|
+
src: string;
|
|
1258
|
+
/** Source type */
|
|
1259
|
+
type: 'mp4' | 'hls';
|
|
1260
|
+
/** Poster image URL */
|
|
1261
|
+
poster?: string;
|
|
1262
|
+
/** Whether video should autoplay */
|
|
1263
|
+
autoPlay?: boolean;
|
|
1264
|
+
/** Whether video should loop */
|
|
1265
|
+
loop?: boolean;
|
|
1266
|
+
/** Whether video is muted */
|
|
1267
|
+
muted?: boolean;
|
|
1268
|
+
/** Initial volume (0-1) */
|
|
1269
|
+
volume?: number;
|
|
1270
|
+
/** Additional CSS classes */
|
|
1271
|
+
className?: string;
|
|
1272
|
+
/** Video element ref callback */
|
|
1273
|
+
videoRef?: UIRef<HTMLVideoElement>;
|
|
1274
|
+
/** Called when video can play */
|
|
1275
|
+
onCanPlay?: () => void;
|
|
1276
|
+
/** Called when video starts playing (play event) */
|
|
1277
|
+
onPlay?: () => void;
|
|
1278
|
+
/** Called when playback resumes after buffering (playing event) */
|
|
1279
|
+
onPlaying?: () => void;
|
|
1280
|
+
/** Called when video is paused */
|
|
1281
|
+
onPause?: () => void;
|
|
1282
|
+
/** Called when video ends */
|
|
1283
|
+
onEnded?: () => void;
|
|
1284
|
+
/** Called on time update */
|
|
1285
|
+
onTimeUpdate?: (currentTime: number) => void;
|
|
1286
|
+
/** Called when duration is known */
|
|
1287
|
+
onDurationChange?: (duration: number) => void;
|
|
1288
|
+
/** Called when video is waiting for data */
|
|
1289
|
+
onWaiting?: () => void;
|
|
1290
|
+
/** Called on error */
|
|
1291
|
+
onError?: (error: Error) => void;
|
|
1292
|
+
/** Called when first frame is captured */
|
|
1293
|
+
onFirstFrameCapture?: (dataUrl: string) => void;
|
|
1294
|
+
}
|
|
1295
|
+
/**
|
|
1296
|
+
* ProgressBar headless component props
|
|
1297
|
+
*/
|
|
1298
|
+
interface ProgressBarHeadlessProps {
|
|
1299
|
+
/** Current time in seconds */
|
|
1300
|
+
currentTime: number;
|
|
1301
|
+
/** Total duration in seconds */
|
|
1302
|
+
duration: number;
|
|
1303
|
+
/** Buffered percentage (0-1) */
|
|
1304
|
+
buffered?: number;
|
|
1305
|
+
/** Whether seeking is enabled */
|
|
1306
|
+
seekable?: boolean;
|
|
1307
|
+
/** Additional CSS classes */
|
|
1308
|
+
className?: string;
|
|
1309
|
+
/** Called when user seeks */
|
|
1310
|
+
onSeek?: (time: number) => void;
|
|
1311
|
+
/** Called when user starts seeking */
|
|
1312
|
+
onSeekStart?: () => void;
|
|
1313
|
+
/** Called when user ends seeking */
|
|
1314
|
+
onSeekEnd?: () => void;
|
|
1315
|
+
}
|
|
1316
|
+
/**
|
|
1317
|
+
* ActionBar headless component props
|
|
1318
|
+
*/
|
|
1319
|
+
interface ActionBarHeadlessProps {
|
|
1320
|
+
/** Interaction state */
|
|
1321
|
+
interactionState: UIInteractionState;
|
|
1322
|
+
/** Interaction actions */
|
|
1323
|
+
interactionActions: UIInteractionActions;
|
|
1324
|
+
/** Additional CSS classes */
|
|
1325
|
+
className?: string;
|
|
1326
|
+
/** Children (action buttons) */
|
|
1327
|
+
children?: UINode;
|
|
1328
|
+
}
|
|
1329
|
+
/**
|
|
1330
|
+
* ActionButton (Like, Comment, Share) headless component props
|
|
1331
|
+
*/
|
|
1332
|
+
interface ActionButtonHeadlessProps {
|
|
1333
|
+
/** Button icon (inactive state) */
|
|
1334
|
+
icon: UINode;
|
|
1335
|
+
/** Button icon (active state, e.g., filled heart) */
|
|
1336
|
+
activeIcon?: UINode;
|
|
1337
|
+
/** Whether button is in active state */
|
|
1338
|
+
isActive?: boolean;
|
|
1339
|
+
/** Count to display */
|
|
1340
|
+
count?: number;
|
|
1341
|
+
/** Whether action is pending */
|
|
1342
|
+
isPending?: boolean;
|
|
1343
|
+
/** Click handler */
|
|
1344
|
+
onClick?: () => void;
|
|
1345
|
+
/** Additional CSS classes */
|
|
1346
|
+
className?: string;
|
|
1347
|
+
/** Accessible label */
|
|
1348
|
+
ariaLabel?: string;
|
|
1349
|
+
}
|
|
1350
|
+
/**
|
|
1351
|
+
* AuthorInfo headless component props
|
|
1352
|
+
*/
|
|
1353
|
+
interface AuthorInfoHeadlessProps {
|
|
1354
|
+
/** Author state */
|
|
1355
|
+
authorState: UIAuthorState;
|
|
1356
|
+
/** Author actions */
|
|
1357
|
+
authorActions: UIAuthorActions;
|
|
1358
|
+
/** Whether to show follow button */
|
|
1359
|
+
showFollowButton?: boolean;
|
|
1360
|
+
/** Additional CSS classes */
|
|
1361
|
+
className?: string;
|
|
1362
|
+
/** Children (for compound pattern) */
|
|
1363
|
+
children?: UINode;
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1366
|
+
* ErrorBoundary component props
|
|
1367
|
+
*/
|
|
1368
|
+
interface ErrorBoundaryProps {
|
|
1369
|
+
/** Children to render */
|
|
1370
|
+
children: UINode;
|
|
1371
|
+
/** Error handling mode */
|
|
1372
|
+
mode?: 'show-error' | 'auto-skip' | 'auto-skip-with-toast';
|
|
1373
|
+
/** Custom error fallback */
|
|
1374
|
+
fallback?: UINode | ((error: Error, reset: () => void) => UINode);
|
|
1375
|
+
/** Called when error occurs */
|
|
1376
|
+
onError?: (error: Error) => void;
|
|
1377
|
+
/** Called when reset is triggered */
|
|
1378
|
+
onReset?: () => void;
|
|
1379
|
+
}
|
|
1380
|
+
/**
|
|
1381
|
+
* Skeleton component props
|
|
1382
|
+
*/
|
|
1383
|
+
interface SkeletonProps {
|
|
1384
|
+
/** Width */
|
|
1385
|
+
width?: number | string;
|
|
1386
|
+
/** Height */
|
|
1387
|
+
height?: number | string;
|
|
1388
|
+
/** Border radius */
|
|
1389
|
+
borderRadius?: number | string;
|
|
1390
|
+
/** Animation type */
|
|
1391
|
+
animation?: 'pulse' | 'wave' | 'none';
|
|
1392
|
+
/** Additional CSS classes */
|
|
1393
|
+
className?: string;
|
|
1394
|
+
}
|
|
1395
|
+
/**
|
|
1396
|
+
* VideoFeed wired component props
|
|
1397
|
+
* feedState and swipeState are injected by SDK
|
|
1398
|
+
*/
|
|
1399
|
+
interface VideoFeedProps extends Omit<VideoFeedHeadlessProps, 'feedState' | 'swipeState'> {
|
|
1400
|
+
/** Override feed state (optional) */
|
|
1401
|
+
feedState?: UIFeedState;
|
|
1402
|
+
/** Override swipe state (optional) */
|
|
1403
|
+
swipeState?: UISwipeState;
|
|
1404
|
+
}
|
|
1405
|
+
/**
|
|
1406
|
+
* VideoSlot wired component props
|
|
1407
|
+
* resourceState, playerState, playerControls are injected by SDK
|
|
1408
|
+
*/
|
|
1409
|
+
interface VideoSlotProps extends Omit<VideoSlotHeadlessProps, 'resourceState' | 'playerState' | 'playerControls'> {
|
|
1410
|
+
/** Override resource state (optional) */
|
|
1411
|
+
resourceState?: UIResourceState;
|
|
1412
|
+
/** Override player state (optional) */
|
|
1413
|
+
playerState?: UIPlayerState;
|
|
1414
|
+
/** Override player controls (optional) */
|
|
1415
|
+
playerControls?: UIPlayerControls;
|
|
1416
|
+
}
|
|
1417
|
+
/**
|
|
1418
|
+
* VideoPlayer wired component props
|
|
1419
|
+
* Uses video from context, only needs overrides
|
|
1420
|
+
*/
|
|
1421
|
+
interface VideoPlayerWiredProps extends Partial<VideoPlayerHeadlessProps> {
|
|
1422
|
+
/** Video item (required for wired version) */
|
|
1423
|
+
video: VideoItem;
|
|
1424
|
+
}
|
|
1425
|
+
/**
|
|
1426
|
+
* ActionBar wired component props
|
|
1427
|
+
* interactionState and interactionActions are injected by SDK
|
|
1428
|
+
*/
|
|
1429
|
+
interface ActionBarProps extends Omit<ActionBarHeadlessProps, 'interactionState' | 'interactionActions'> {
|
|
1430
|
+
/** Video to get interaction state for */
|
|
1431
|
+
video: VideoItem;
|
|
1432
|
+
/** Override interaction state (optional) */
|
|
1433
|
+
interactionState?: UIInteractionState;
|
|
1434
|
+
/** Override interaction actions (optional) */
|
|
1435
|
+
interactionActions?: UIInteractionActions;
|
|
1436
|
+
}
|
|
1437
|
+
/**
|
|
1438
|
+
* AuthorInfo wired component props
|
|
1439
|
+
* authorState and authorActions are injected by SDK
|
|
1440
|
+
*/
|
|
1441
|
+
interface AuthorInfoProps extends Omit<AuthorInfoHeadlessProps, 'authorState' | 'authorActions'> {
|
|
1442
|
+
/** Video to get author from */
|
|
1443
|
+
video: VideoItem;
|
|
1444
|
+
/** Override author state (optional) */
|
|
1445
|
+
authorState?: UIAuthorState;
|
|
1446
|
+
/** Override author actions (optional) */
|
|
1447
|
+
authorActions?: UIAuthorActions;
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Hook configuration for HOC factory
|
|
1451
|
+
*/
|
|
1452
|
+
interface WiredComponentConfig<THooks, THeadlessProps, TWiredProps> {
|
|
1453
|
+
/** Function that calls SDK hooks and returns hook results */
|
|
1454
|
+
useHooks: (props: TWiredProps) => THooks;
|
|
1455
|
+
/** Function that maps hook results to headless component props */
|
|
1456
|
+
mapHooksToProps: (hooks: THooks, props: TWiredProps) => Partial<THeadlessProps>;
|
|
1457
|
+
}
|
|
1458
|
+
/**
|
|
1459
|
+
* HOC factory return type
|
|
1460
|
+
*/
|
|
1461
|
+
type WiredComponent<TWiredProps> = UIComponent<TWiredProps>;
|
|
1462
|
+
/**
|
|
1463
|
+
* Icon component type
|
|
1464
|
+
*/
|
|
1465
|
+
type IconComponent = UIComponent<{
|
|
1466
|
+
className?: string;
|
|
1467
|
+
size?: number | string;
|
|
1468
|
+
}>;
|
|
1469
|
+
/**
|
|
1470
|
+
* Format count function type (1000 -> "1K")
|
|
1471
|
+
*/
|
|
1472
|
+
type FormatCountFn = (count: number) => string;
|
|
1473
|
+
|
|
1474
|
+
export type { ActionBarHeadlessProps, ActionBarProps, ActionButtonHeadlessProps, AnalyticsConfig, AnalyticsEvent, AnalyticsEventType, Author, AuthorInfoHeadlessProps, AuthorInfoProps, Comment, ErrorBoundaryProps, FeedResponse, FeedState, FormatCountFn, IAnalytics, IDataSource, IInteraction, ILogger, INetworkAdapter, IPosterLoader, ISessionStorage, IStorage, IVideoLoader, IconComponent, InternalLogger, LogEntry, LogLevel, LoggerConfig, MusicInfo, NetworkQuality, NetworkType, OptimisticAction, PlaybackState, PlayerError, PlayerState, PreloadConfig, PreloadResult, PreloadStatus, ProgressBarHeadlessProps, SDKConfig, SessionSnapshot, SkeletonProps, UIAuthorActions, UIAuthorState, UIComponent, UIFeedState, UIInteractionActions, UIInteractionState, UINode, UIPlayerControls, UIPlayerState, UIRef, UIResourceState, UISwipeState, VideoFeedHeadlessProps, VideoFeedProps, VideoItem, VideoPlayerHeadlessProps, VideoPlayerWiredProps, VideoQuality, VideoSlotHeadlessProps, VideoSlotProps, VideoSource, VideoStats, WiredComponent, WiredComponentConfig };
|