@xhub-short/adapters 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/README.md +1 -0
- package/dist/index.d.ts +1954 -0
- package/dist/index.js +3238 -0
- package/package.json +43 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1954 @@
|
|
|
1
|
+
import { IDataSource, VideoItem, FeedResponse, ILogger, LogLevel, LogEntry, IStorage, ISessionStorage, SessionSnapshot, IInteraction, Comment, IAnalytics, AnalyticsEvent, INetworkAdapter, NetworkType, NetworkQuality, IVideoLoader, VideoSource, PreloadConfig, PreloadResult, PreloadStatus, IPosterLoader } from '@xhub-short/contracts';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MockDataAdapter - Development/Testing Data Source
|
|
5
|
+
*
|
|
6
|
+
* Provides mock video data for development without requiring a backend API.
|
|
7
|
+
* Simulates pagination and network delay for realistic testing.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const mockAdapter = new MockDataAdapter({ delay: 500 });
|
|
12
|
+
* const feed = await mockAdapter.fetchFeed();
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
declare class MockDataAdapter implements IDataSource {
|
|
16
|
+
private readonly videos;
|
|
17
|
+
private readonly pageSize;
|
|
18
|
+
private readonly delay;
|
|
19
|
+
constructor(options?: MockDataAdapterOptions);
|
|
20
|
+
/**
|
|
21
|
+
* Fetch a page of mock videos
|
|
22
|
+
*/
|
|
23
|
+
fetchFeed(cursor?: string): Promise<FeedResponse>;
|
|
24
|
+
/**
|
|
25
|
+
* Get a single video by ID
|
|
26
|
+
*/
|
|
27
|
+
getVideoDetail(id: string): Promise<VideoItem>;
|
|
28
|
+
/**
|
|
29
|
+
* Prefetch videos (no-op for mock)
|
|
30
|
+
*/
|
|
31
|
+
prefetch(_ids: string[]): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Simulate network delay
|
|
34
|
+
*/
|
|
35
|
+
private simulateDelay;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Configuration options for MockDataAdapter
|
|
39
|
+
*/
|
|
40
|
+
interface MockDataAdapterOptions {
|
|
41
|
+
/** Custom mock video data */
|
|
42
|
+
videos?: VideoItem[];
|
|
43
|
+
/** Number of items per page (default: 3) */
|
|
44
|
+
pageSize?: number;
|
|
45
|
+
/** Simulated network delay in ms (default: 300) */
|
|
46
|
+
delay?: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Configuration options for MockLoggerAdapter
|
|
51
|
+
*/
|
|
52
|
+
interface MockLoggerAdapterOptions {
|
|
53
|
+
/** Maximum entries in circular buffer (default: 50 per ADD spec) */
|
|
54
|
+
bufferSize?: number;
|
|
55
|
+
/** Minimum log level to record (default: 'debug') */
|
|
56
|
+
minLevel?: LogLevel;
|
|
57
|
+
/** Whether to also output to console (default: false) */
|
|
58
|
+
consoleOutput?: boolean;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* MockLoggerAdapter - In-memory ILogger implementation for testing
|
|
62
|
+
*
|
|
63
|
+
* Implements Circular Buffer pattern as specified in ADD.md Section 7.3:
|
|
64
|
+
* - Maintains buffer of last N log entries in RAM
|
|
65
|
+
* - Normal operation: Only write to buffer (silent)
|
|
66
|
+
* - On error: Flush buffer for context
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```typescript
|
|
70
|
+
* const logger = new MockLoggerAdapter({ bufferSize: 50 });
|
|
71
|
+
* logger.info('User action', { videoId: '123' });
|
|
72
|
+
* logger.error('Failed to load', new Error('Network error'));
|
|
73
|
+
*
|
|
74
|
+
* // Get buffer for debugging
|
|
75
|
+
* const entries = logger.getBuffer();
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* @see ILogger - Interface definition in @xhub-short/contracts
|
|
79
|
+
*/
|
|
80
|
+
declare class MockLoggerAdapter implements ILogger {
|
|
81
|
+
private buffer;
|
|
82
|
+
private readonly maxSize;
|
|
83
|
+
private readonly minLevel;
|
|
84
|
+
private readonly consoleOutput;
|
|
85
|
+
constructor(options?: MockLoggerAdapterOptions);
|
|
86
|
+
/**
|
|
87
|
+
* Log a single entry to the circular buffer
|
|
88
|
+
*
|
|
89
|
+
* @param entry - Log entry to record
|
|
90
|
+
*/
|
|
91
|
+
log(entry: LogEntry): void;
|
|
92
|
+
/**
|
|
93
|
+
* Flush log entries (called on error for context)
|
|
94
|
+
*
|
|
95
|
+
* @param entries - Array of log entries to flush
|
|
96
|
+
*/
|
|
97
|
+
flush(entries: LogEntry[]): void;
|
|
98
|
+
/**
|
|
99
|
+
* Log debug message
|
|
100
|
+
*
|
|
101
|
+
* @param message - Debug message
|
|
102
|
+
* @param context - Optional context data
|
|
103
|
+
*/
|
|
104
|
+
debug(message: string, context?: Record<string, unknown>): void;
|
|
105
|
+
/**
|
|
106
|
+
* Log info message
|
|
107
|
+
*
|
|
108
|
+
* @param message - Info message
|
|
109
|
+
* @param context - Optional context data
|
|
110
|
+
*/
|
|
111
|
+
info(message: string, context?: Record<string, unknown>): void;
|
|
112
|
+
/**
|
|
113
|
+
* Log warning message
|
|
114
|
+
*
|
|
115
|
+
* @param message - Warning message
|
|
116
|
+
* @param context - Optional context data
|
|
117
|
+
*/
|
|
118
|
+
warn(message: string, context?: Record<string, unknown>): void;
|
|
119
|
+
/**
|
|
120
|
+
* Log error message
|
|
121
|
+
*
|
|
122
|
+
* @param message - Error message
|
|
123
|
+
* @param error - Optional Error object
|
|
124
|
+
* @param context - Optional context data
|
|
125
|
+
*/
|
|
126
|
+
error(message: string, error?: Error, context?: Record<string, unknown>): void;
|
|
127
|
+
/**
|
|
128
|
+
* Get current buffer contents (for testing)
|
|
129
|
+
*
|
|
130
|
+
* @returns Copy of current log buffer
|
|
131
|
+
*/
|
|
132
|
+
getBuffer(): LogEntry[];
|
|
133
|
+
/**
|
|
134
|
+
* Get buffer size (for testing)
|
|
135
|
+
*
|
|
136
|
+
* @returns Number of entries in buffer
|
|
137
|
+
*/
|
|
138
|
+
getBufferSize(): number;
|
|
139
|
+
/**
|
|
140
|
+
* Clear the buffer (for testing)
|
|
141
|
+
*/
|
|
142
|
+
clearBuffer(): void;
|
|
143
|
+
/**
|
|
144
|
+
* Get entries by level (for testing)
|
|
145
|
+
*
|
|
146
|
+
* @param level - Log level to filter by
|
|
147
|
+
* @returns Entries matching the level
|
|
148
|
+
*/
|
|
149
|
+
getEntriesByLevel(level: LogLevel): LogEntry[];
|
|
150
|
+
/**
|
|
151
|
+
* Force flush and return buffer contents
|
|
152
|
+
*
|
|
153
|
+
* @returns All entries in buffer before clearing
|
|
154
|
+
*/
|
|
155
|
+
forceFlush(): LogEntry[];
|
|
156
|
+
/**
|
|
157
|
+
* Output entry to console (when consoleOutput is enabled)
|
|
158
|
+
*/
|
|
159
|
+
private outputToConsole;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Configuration options for MockStorageAdapter
|
|
164
|
+
*/
|
|
165
|
+
interface MockStorageAdapterOptions {
|
|
166
|
+
/** Initial data to populate storage */
|
|
167
|
+
initialData?: Record<string, unknown>;
|
|
168
|
+
/** Simulated delay in ms (default: 0) */
|
|
169
|
+
delay?: number;
|
|
170
|
+
/** Error rate for random failures (0-1) */
|
|
171
|
+
errorRate?: number;
|
|
172
|
+
/** Simulate QuotaExceededError on set operations */
|
|
173
|
+
simulateQuotaError?: boolean;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* MockStorageAdapter - In-memory IStorage implementation for testing
|
|
177
|
+
*
|
|
178
|
+
* Simulates localStorage/sessionStorage behavior without persisting to disk.
|
|
179
|
+
* Supports configurable delays and error simulation for testing edge cases.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```typescript
|
|
183
|
+
* const storage = new MockStorageAdapter({ delay: 100 });
|
|
184
|
+
* await storage.set('key', { foo: 'bar' });
|
|
185
|
+
* const value = await storage.get('key');
|
|
186
|
+
* ```
|
|
187
|
+
*
|
|
188
|
+
* @see IStorage - Interface definition in @xhub-short/contracts
|
|
189
|
+
*/
|
|
190
|
+
declare class MockStorageAdapter implements IStorage {
|
|
191
|
+
protected store: Map<string, string>;
|
|
192
|
+
protected readonly delay: number;
|
|
193
|
+
protected readonly errorRate: number;
|
|
194
|
+
protected readonly simulateQuotaError: boolean;
|
|
195
|
+
constructor(options?: MockStorageAdapterOptions);
|
|
196
|
+
/**
|
|
197
|
+
* Get a value from storage
|
|
198
|
+
*
|
|
199
|
+
* @param key - Storage key
|
|
200
|
+
* @returns Promise resolving to the stored value or null if not found
|
|
201
|
+
*/
|
|
202
|
+
get<T>(key: string): Promise<T | null>;
|
|
203
|
+
/**
|
|
204
|
+
* Set a value in storage
|
|
205
|
+
*
|
|
206
|
+
* @param key - Storage key
|
|
207
|
+
* @param value - Value to store (will be serialized)
|
|
208
|
+
* @throws DOMException if simulateQuotaError is enabled
|
|
209
|
+
*/
|
|
210
|
+
set<T>(key: string, value: T): Promise<void>;
|
|
211
|
+
/**
|
|
212
|
+
* Remove a value from storage
|
|
213
|
+
*
|
|
214
|
+
* @param key - Storage key to remove
|
|
215
|
+
*/
|
|
216
|
+
remove(key: string): Promise<void>;
|
|
217
|
+
/**
|
|
218
|
+
* Clear all SDK-related storage
|
|
219
|
+
*/
|
|
220
|
+
clear(): Promise<void>;
|
|
221
|
+
/**
|
|
222
|
+
* Get all keys in storage
|
|
223
|
+
*
|
|
224
|
+
* @returns Promise resolving to array of storage keys
|
|
225
|
+
*/
|
|
226
|
+
keys(): Promise<string[]>;
|
|
227
|
+
/**
|
|
228
|
+
* Get storage size (for testing)
|
|
229
|
+
*
|
|
230
|
+
* @returns Number of entries in storage
|
|
231
|
+
*/
|
|
232
|
+
getSize(): number;
|
|
233
|
+
/**
|
|
234
|
+
* Check if key exists (for testing)
|
|
235
|
+
*
|
|
236
|
+
* @param key - Storage key to check
|
|
237
|
+
* @returns Whether key exists
|
|
238
|
+
*/
|
|
239
|
+
has(key: string): boolean;
|
|
240
|
+
/**
|
|
241
|
+
* Get raw storage Map (for testing)
|
|
242
|
+
*
|
|
243
|
+
* @returns Copy of internal storage map
|
|
244
|
+
*/
|
|
245
|
+
getRawStore(): Map<string, string>;
|
|
246
|
+
/**
|
|
247
|
+
* Simulate network/processing delay
|
|
248
|
+
*/
|
|
249
|
+
protected simulateDelay(): Promise<void>;
|
|
250
|
+
/**
|
|
251
|
+
* Maybe throw error based on error rate
|
|
252
|
+
*/
|
|
253
|
+
protected maybeThrowError(): void;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Configuration options for MockSessionStorageAdapter
|
|
257
|
+
*/
|
|
258
|
+
interface MockSessionStorageAdapterOptions extends MockStorageAdapterOptions {
|
|
259
|
+
/** Maximum age of snapshot in ms before considered stale (default: 24h) */
|
|
260
|
+
maxSnapshotAge?: number;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* MockSessionStorageAdapter - ISessionStorage implementation for testing
|
|
264
|
+
*
|
|
265
|
+
* Extends MockStorageAdapter with session snapshot functionality.
|
|
266
|
+
* Used by LifecycleManager for state restoration.
|
|
267
|
+
*
|
|
268
|
+
* @example
|
|
269
|
+
* ```typescript
|
|
270
|
+
* const sessionStorage = new MockSessionStorageAdapter();
|
|
271
|
+
*
|
|
272
|
+
* // Save snapshot when user navigates away
|
|
273
|
+
* await sessionStorage.saveSnapshot({
|
|
274
|
+
* currentVideoId: 'video-123',
|
|
275
|
+
* timestamp: 30.5,
|
|
276
|
+
* cursor: 'abc123',
|
|
277
|
+
* currentIndex: 5,
|
|
278
|
+
* savedAt: Date.now(),
|
|
279
|
+
* });
|
|
280
|
+
*
|
|
281
|
+
* // Restore snapshot when user returns
|
|
282
|
+
* const snapshot = await sessionStorage.loadSnapshot();
|
|
283
|
+
* ```
|
|
284
|
+
*
|
|
285
|
+
* @see ISessionStorage - Interface definition in @xhub-short/contracts
|
|
286
|
+
*/
|
|
287
|
+
declare class MockSessionStorageAdapter extends MockStorageAdapter implements ISessionStorage {
|
|
288
|
+
private readonly maxSnapshotAge;
|
|
289
|
+
constructor(options?: MockSessionStorageAdapterOptions);
|
|
290
|
+
/**
|
|
291
|
+
* Save session snapshot for state restoration
|
|
292
|
+
*
|
|
293
|
+
* @param snapshot - Session state to persist
|
|
294
|
+
*/
|
|
295
|
+
saveSnapshot(snapshot: SessionSnapshot): Promise<void>;
|
|
296
|
+
/**
|
|
297
|
+
* Load the most recent session snapshot
|
|
298
|
+
*
|
|
299
|
+
* @returns Promise resolving to snapshot or null if none exists
|
|
300
|
+
*/
|
|
301
|
+
loadSnapshot(): Promise<SessionSnapshot | null>;
|
|
302
|
+
/**
|
|
303
|
+
* Clear session snapshot
|
|
304
|
+
*/
|
|
305
|
+
clearSnapshot(): Promise<void>;
|
|
306
|
+
/**
|
|
307
|
+
* Check if snapshot exists (for testing)
|
|
308
|
+
*
|
|
309
|
+
* @returns Whether snapshot exists
|
|
310
|
+
*/
|
|
311
|
+
hasSnapshot(): boolean;
|
|
312
|
+
/**
|
|
313
|
+
* Get snapshot age in ms (for testing)
|
|
314
|
+
*
|
|
315
|
+
* @returns Age in ms or null if no snapshot
|
|
316
|
+
*/
|
|
317
|
+
getSnapshotAge(): Promise<number | null>;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Configuration options for MockInteractionAdapter
|
|
322
|
+
*/
|
|
323
|
+
interface MockInteractionAdapterOptions {
|
|
324
|
+
/** Simulated delay in ms (default: 0) */
|
|
325
|
+
delay?: number;
|
|
326
|
+
/** Error rate for random failures (0-1) */
|
|
327
|
+
errorRate?: number;
|
|
328
|
+
/** Force like operations to fail (for rollback testing) */
|
|
329
|
+
failLike?: boolean;
|
|
330
|
+
/** Force unlike operations to fail */
|
|
331
|
+
failUnlike?: boolean;
|
|
332
|
+
/** Force follow operations to fail */
|
|
333
|
+
failFollow?: boolean;
|
|
334
|
+
/** Force unfollow operations to fail */
|
|
335
|
+
failUnfollow?: boolean;
|
|
336
|
+
/** Force comment operations to fail */
|
|
337
|
+
failComment?: boolean;
|
|
338
|
+
/** Force deleteComment operations to fail */
|
|
339
|
+
failDeleteComment?: boolean;
|
|
340
|
+
/** Initial liked video IDs */
|
|
341
|
+
initialLikedVideos?: string[];
|
|
342
|
+
/** Initial followed author IDs */
|
|
343
|
+
initialFollowedAuthors?: string[];
|
|
344
|
+
/** Initial liked comment IDs */
|
|
345
|
+
initialLikedComments?: string[];
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* MockInteractionAdapter - In-memory IInteraction implementation for testing
|
|
349
|
+
*
|
|
350
|
+
* Provides mock implementations for all user interaction methods.
|
|
351
|
+
* Tracks state changes in-memory for verification in tests.
|
|
352
|
+
* Supports error simulation for testing optimistic UI rollback.
|
|
353
|
+
*
|
|
354
|
+
* @example
|
|
355
|
+
* ```typescript
|
|
356
|
+
* const interaction = new MockInteractionAdapter({ delay: 100 });
|
|
357
|
+
*
|
|
358
|
+
* // Simulate like action
|
|
359
|
+
* await interaction.like('video-123');
|
|
360
|
+
* console.log(interaction.isVideoLiked('video-123')); // true
|
|
361
|
+
*
|
|
362
|
+
* // Test rollback scenario
|
|
363
|
+
* const failingInteraction = new MockInteractionAdapter({ failLike: true });
|
|
364
|
+
* await failingInteraction.like('video-123'); // throws Error
|
|
365
|
+
* ```
|
|
366
|
+
*
|
|
367
|
+
* @see IInteraction - Interface definition in @xhub-short/contracts
|
|
368
|
+
*/
|
|
369
|
+
declare class MockInteractionAdapter implements IInteraction {
|
|
370
|
+
private readonly likedVideos;
|
|
371
|
+
private readonly followedAuthors;
|
|
372
|
+
private readonly likedComments;
|
|
373
|
+
private readonly comments;
|
|
374
|
+
private readonly delay;
|
|
375
|
+
private readonly errorRate;
|
|
376
|
+
private readonly failLike;
|
|
377
|
+
private readonly failUnlike;
|
|
378
|
+
private readonly failFollow;
|
|
379
|
+
private readonly failUnfollow;
|
|
380
|
+
private readonly failComment;
|
|
381
|
+
private readonly failDeleteComment;
|
|
382
|
+
constructor(options?: MockInteractionAdapterOptions);
|
|
383
|
+
/**
|
|
384
|
+
* Like a video
|
|
385
|
+
*
|
|
386
|
+
* @param videoId - ID of the video to like
|
|
387
|
+
* @throws Error if failLike is enabled or random error occurs
|
|
388
|
+
*/
|
|
389
|
+
like(videoId: string): Promise<void>;
|
|
390
|
+
/**
|
|
391
|
+
* Remove like from a video
|
|
392
|
+
*
|
|
393
|
+
* @param videoId - ID of the video to unlike
|
|
394
|
+
* @throws Error if failUnlike is enabled or random error occurs
|
|
395
|
+
*/
|
|
396
|
+
unlike(videoId: string): Promise<void>;
|
|
397
|
+
/**
|
|
398
|
+
* Follow a video author
|
|
399
|
+
*
|
|
400
|
+
* @param authorId - ID of the author to follow
|
|
401
|
+
* @throws Error if failFollow is enabled or random error occurs
|
|
402
|
+
*/
|
|
403
|
+
follow(authorId: string): Promise<void>;
|
|
404
|
+
/**
|
|
405
|
+
* Unfollow a video author
|
|
406
|
+
*
|
|
407
|
+
* @param authorId - ID of the author to unfollow
|
|
408
|
+
* @throws Error if failUnfollow is enabled or random error occurs
|
|
409
|
+
*/
|
|
410
|
+
unfollow(authorId: string): Promise<void>;
|
|
411
|
+
/**
|
|
412
|
+
* Post a comment on a video
|
|
413
|
+
*
|
|
414
|
+
* @param _videoId - ID of the video to comment on (unused in mock)
|
|
415
|
+
* @param text - Comment text content
|
|
416
|
+
* @returns Promise resolving to the created comment
|
|
417
|
+
* @throws Error if failComment is enabled or random error occurs
|
|
418
|
+
*/
|
|
419
|
+
comment(_videoId: string, text: string): Promise<Comment>;
|
|
420
|
+
/**
|
|
421
|
+
* Delete a comment
|
|
422
|
+
*
|
|
423
|
+
* @param commentId - ID of the comment to delete
|
|
424
|
+
* @throws Error if comment not found, failDeleteComment is enabled, or random error occurs
|
|
425
|
+
*/
|
|
426
|
+
deleteComment(commentId: string): Promise<void>;
|
|
427
|
+
/**
|
|
428
|
+
* Like a comment
|
|
429
|
+
*
|
|
430
|
+
* @param commentId - ID of the comment to like
|
|
431
|
+
*/
|
|
432
|
+
likeComment(commentId: string): Promise<void>;
|
|
433
|
+
/**
|
|
434
|
+
* Unlike a comment
|
|
435
|
+
*
|
|
436
|
+
* @param commentId - ID of the comment to unlike
|
|
437
|
+
*/
|
|
438
|
+
unlikeComment(commentId: string): Promise<void>;
|
|
439
|
+
/**
|
|
440
|
+
* Share a video (tracking only)
|
|
441
|
+
*
|
|
442
|
+
* @param _videoId - ID of the video being shared
|
|
443
|
+
* @param _platform - Optional platform identifier
|
|
444
|
+
*/
|
|
445
|
+
share(_videoId: string, _platform?: string): Promise<void>;
|
|
446
|
+
/**
|
|
447
|
+
* Report a video
|
|
448
|
+
*
|
|
449
|
+
* @param _videoId - ID of the video to report
|
|
450
|
+
* @param _reason - Report reason code
|
|
451
|
+
* @param _description - Optional additional description
|
|
452
|
+
*/
|
|
453
|
+
report(_videoId: string, _reason: string, _description?: string): Promise<void>;
|
|
454
|
+
/**
|
|
455
|
+
* Check if video is liked (for testing)
|
|
456
|
+
*
|
|
457
|
+
* @param videoId - Video ID to check
|
|
458
|
+
* @returns Whether video is liked
|
|
459
|
+
*/
|
|
460
|
+
isVideoLiked(videoId: string): boolean;
|
|
461
|
+
/**
|
|
462
|
+
* Check if author is followed (for testing)
|
|
463
|
+
*
|
|
464
|
+
* @param authorId - Author ID to check
|
|
465
|
+
* @returns Whether author is followed
|
|
466
|
+
*/
|
|
467
|
+
isAuthorFollowed(authorId: string): boolean;
|
|
468
|
+
/**
|
|
469
|
+
* Check if comment is liked (for testing)
|
|
470
|
+
*
|
|
471
|
+
* @param commentId - Comment ID to check
|
|
472
|
+
* @returns Whether comment is liked
|
|
473
|
+
*/
|
|
474
|
+
isCommentLiked(commentId: string): boolean;
|
|
475
|
+
/**
|
|
476
|
+
* Get all liked video IDs (for testing)
|
|
477
|
+
*
|
|
478
|
+
* @returns Array of liked video IDs
|
|
479
|
+
*/
|
|
480
|
+
getLikedVideos(): string[];
|
|
481
|
+
/**
|
|
482
|
+
* Get all followed author IDs (for testing)
|
|
483
|
+
*
|
|
484
|
+
* @returns Array of followed author IDs
|
|
485
|
+
*/
|
|
486
|
+
getFollowedAuthors(): string[];
|
|
487
|
+
/**
|
|
488
|
+
* Get all comments (for testing)
|
|
489
|
+
*
|
|
490
|
+
* @returns Map of comment ID to Comment
|
|
491
|
+
*/
|
|
492
|
+
getComments(): Map<string, Comment>;
|
|
493
|
+
/**
|
|
494
|
+
* Get a specific comment (for testing)
|
|
495
|
+
*
|
|
496
|
+
* @param commentId - Comment ID
|
|
497
|
+
* @returns Comment or undefined
|
|
498
|
+
*/
|
|
499
|
+
getComment(commentId: string): Comment | undefined;
|
|
500
|
+
/**
|
|
501
|
+
* Reset all state (for testing)
|
|
502
|
+
*/
|
|
503
|
+
reset(): void;
|
|
504
|
+
/**
|
|
505
|
+
* Simulate network/processing delay
|
|
506
|
+
*/
|
|
507
|
+
private simulateDelay;
|
|
508
|
+
/**
|
|
509
|
+
* Maybe throw error based on error rate
|
|
510
|
+
*/
|
|
511
|
+
private maybeThrowError;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Configuration options for MockAnalyticsAdapter
|
|
516
|
+
*/
|
|
517
|
+
interface MockAnalyticsAdapterOptions {
|
|
518
|
+
/** Batch size threshold for auto-flush (default: 10) */
|
|
519
|
+
batchSize?: number;
|
|
520
|
+
/** Whether to auto-flush when batch size is reached (default: false) */
|
|
521
|
+
autoFlush?: boolean;
|
|
522
|
+
/** Simulated delay for flush in ms (default: 0) */
|
|
523
|
+
flushDelay?: number;
|
|
524
|
+
/** Error rate for flush failures (0-1) */
|
|
525
|
+
errorRate?: number;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* MockAnalyticsAdapter - In-memory IAnalytics implementation for testing
|
|
529
|
+
*
|
|
530
|
+
* Provides mock implementations for analytics tracking with event queue batching.
|
|
531
|
+
* Events are stored in-memory for verification in tests.
|
|
532
|
+
*
|
|
533
|
+
* Batching Strategy (per ADD spec):
|
|
534
|
+
* 1. Events are pushed to internal queue via track()
|
|
535
|
+
* 2. Auto-flush conditions (when autoFlush enabled):
|
|
536
|
+
* - Queue >= batchSize items
|
|
537
|
+
* 3. Manual flush via flush() method
|
|
538
|
+
*
|
|
539
|
+
* @example
|
|
540
|
+
* ```typescript
|
|
541
|
+
* const analytics = new MockAnalyticsAdapter({ batchSize: 10, autoFlush: true });
|
|
542
|
+
*
|
|
543
|
+
* // Track events
|
|
544
|
+
* analytics.track({ type: 'video_view', videoId: '123', timestamp: Date.now() });
|
|
545
|
+
*
|
|
546
|
+
* // Get tracked events for verification
|
|
547
|
+
* const events = analytics.getTrackedEvents();
|
|
548
|
+
*
|
|
549
|
+
* // Manual flush
|
|
550
|
+
* await analytics.flush();
|
|
551
|
+
* ```
|
|
552
|
+
*
|
|
553
|
+
* @see IAnalytics - Interface definition in @xhub-short/contracts
|
|
554
|
+
*/
|
|
555
|
+
declare class MockAnalyticsAdapter implements IAnalytics {
|
|
556
|
+
private eventQueue;
|
|
557
|
+
private flushedEvents;
|
|
558
|
+
private userId;
|
|
559
|
+
private userProperties;
|
|
560
|
+
private readonly batchSize;
|
|
561
|
+
private readonly autoFlush;
|
|
562
|
+
private readonly flushDelay;
|
|
563
|
+
private readonly errorRate;
|
|
564
|
+
constructor(options?: MockAnalyticsAdapterOptions);
|
|
565
|
+
/**
|
|
566
|
+
* Track an analytics event
|
|
567
|
+
*
|
|
568
|
+
* @param event - Event to track
|
|
569
|
+
*/
|
|
570
|
+
track(event: AnalyticsEvent): void;
|
|
571
|
+
/**
|
|
572
|
+
* Flush queued events immediately
|
|
573
|
+
*
|
|
574
|
+
* @returns Promise that resolves when flush is complete
|
|
575
|
+
*/
|
|
576
|
+
flush(): Promise<void>;
|
|
577
|
+
/**
|
|
578
|
+
* Track video view duration
|
|
579
|
+
*
|
|
580
|
+
* @param videoId - ID of the video
|
|
581
|
+
* @param duration - Watch duration in seconds
|
|
582
|
+
* @param totalDuration - Total video duration in seconds
|
|
583
|
+
*/
|
|
584
|
+
trackViewDuration(videoId: string, duration: number, totalDuration: number): void;
|
|
585
|
+
/**
|
|
586
|
+
* Track video completion
|
|
587
|
+
*
|
|
588
|
+
* @param videoId - ID of the video
|
|
589
|
+
* @param watchTime - Total time watched in seconds
|
|
590
|
+
* @param loops - Number of times video looped
|
|
591
|
+
*/
|
|
592
|
+
trackCompletion(videoId: string, watchTime: number, loops: number): void;
|
|
593
|
+
/**
|
|
594
|
+
* Set user context for analytics
|
|
595
|
+
*
|
|
596
|
+
* @param userId - Optional user ID (null for anonymous)
|
|
597
|
+
* @param properties - Optional user properties
|
|
598
|
+
*/
|
|
599
|
+
setUser(userId: string | null, properties?: Record<string, unknown>): void;
|
|
600
|
+
/**
|
|
601
|
+
* Get current queue size
|
|
602
|
+
*
|
|
603
|
+
* @returns Number of events in queue
|
|
604
|
+
*/
|
|
605
|
+
getQueueSize(): number;
|
|
606
|
+
/**
|
|
607
|
+
* Get all tracked events (current queue)
|
|
608
|
+
*
|
|
609
|
+
* @returns Copy of current event queue
|
|
610
|
+
*/
|
|
611
|
+
getTrackedEvents(): AnalyticsEvent[];
|
|
612
|
+
/**
|
|
613
|
+
* Get all flushed event batches
|
|
614
|
+
*
|
|
615
|
+
* @returns Array of flushed event batches
|
|
616
|
+
*/
|
|
617
|
+
getFlushedBatches(): AnalyticsEvent[][];
|
|
618
|
+
/**
|
|
619
|
+
* Get total flushed events count
|
|
620
|
+
*
|
|
621
|
+
* @returns Total number of flushed events
|
|
622
|
+
*/
|
|
623
|
+
getTotalFlushedCount(): number;
|
|
624
|
+
/**
|
|
625
|
+
* Get events by type (from current queue)
|
|
626
|
+
*
|
|
627
|
+
* @param type - Event type to filter by
|
|
628
|
+
* @returns Events matching the type
|
|
629
|
+
*/
|
|
630
|
+
getEventsByType(type: AnalyticsEvent['type']): AnalyticsEvent[];
|
|
631
|
+
/**
|
|
632
|
+
* Get events for a specific video (from current queue)
|
|
633
|
+
*
|
|
634
|
+
* @param videoId - Video ID to filter by
|
|
635
|
+
* @returns Events for the video
|
|
636
|
+
*/
|
|
637
|
+
getEventsByVideoId(videoId: string): AnalyticsEvent[];
|
|
638
|
+
/**
|
|
639
|
+
* Get current user context
|
|
640
|
+
*
|
|
641
|
+
* @returns User ID and properties
|
|
642
|
+
*/
|
|
643
|
+
getUserContext(): {
|
|
644
|
+
userId: string | null;
|
|
645
|
+
properties: Record<string, unknown>;
|
|
646
|
+
};
|
|
647
|
+
/**
|
|
648
|
+
* Check if specific event type was tracked
|
|
649
|
+
*
|
|
650
|
+
* @param type - Event type to check
|
|
651
|
+
* @returns Whether event type was tracked
|
|
652
|
+
*/
|
|
653
|
+
hasTracked(type: AnalyticsEvent['type']): boolean;
|
|
654
|
+
/**
|
|
655
|
+
* Get last tracked event
|
|
656
|
+
*
|
|
657
|
+
* @returns Last event or undefined
|
|
658
|
+
*/
|
|
659
|
+
getLastEvent(): AnalyticsEvent | undefined;
|
|
660
|
+
/**
|
|
661
|
+
* Reset all state
|
|
662
|
+
*/
|
|
663
|
+
reset(): void;
|
|
664
|
+
/**
|
|
665
|
+
* Clear only the event queue (keep flushed history)
|
|
666
|
+
*/
|
|
667
|
+
clearQueue(): void;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Mock Network Adapter
|
|
672
|
+
*
|
|
673
|
+
* Simulates network detection for testing and development.
|
|
674
|
+
* Can be configured with initial network type and simulate network changes.
|
|
675
|
+
*/
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Configuration options for MockNetworkAdapter
|
|
679
|
+
*/
|
|
680
|
+
interface MockNetworkAdapterOptions {
|
|
681
|
+
/** Initial network type (default: 'wifi') */
|
|
682
|
+
initialType?: NetworkType;
|
|
683
|
+
/** Initial downlink speed in Mbps (default: 10) */
|
|
684
|
+
initialDownlink?: number;
|
|
685
|
+
/** Initial RTT in ms (default: 50) */
|
|
686
|
+
initialRtt?: number;
|
|
687
|
+
/** Whether network is metered (default: false) */
|
|
688
|
+
isMetered?: boolean;
|
|
689
|
+
/** Whether initially online (default: true) */
|
|
690
|
+
isOnline?: boolean;
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Mock implementation of INetworkAdapter
|
|
694
|
+
*
|
|
695
|
+
* Useful for:
|
|
696
|
+
* - Unit testing network-dependent logic
|
|
697
|
+
* - Development without real network APIs
|
|
698
|
+
* - Simulating various network conditions
|
|
699
|
+
*
|
|
700
|
+
* @example
|
|
701
|
+
* ```typescript
|
|
702
|
+
* const adapter = new MockNetworkAdapter({ initialType: 'cellular' });
|
|
703
|
+
*
|
|
704
|
+
* // Simulate network change
|
|
705
|
+
* adapter.setNetworkType('wifi');
|
|
706
|
+
*
|
|
707
|
+
* // Test slow network
|
|
708
|
+
* adapter.setNetworkType('slow');
|
|
709
|
+
* adapter.setDownlink(0.5);
|
|
710
|
+
* ```
|
|
711
|
+
*/
|
|
712
|
+
declare class MockNetworkAdapter implements INetworkAdapter {
|
|
713
|
+
private currentType;
|
|
714
|
+
private downlink;
|
|
715
|
+
private rtt;
|
|
716
|
+
private metered;
|
|
717
|
+
private online;
|
|
718
|
+
private listeners;
|
|
719
|
+
constructor(options?: MockNetworkAdapterOptions);
|
|
720
|
+
getNetworkType(): Promise<NetworkType>;
|
|
721
|
+
getNetworkQuality(): Promise<NetworkQuality>;
|
|
722
|
+
onNetworkChange(callback: (type: NetworkType) => void): () => void;
|
|
723
|
+
isOnline(): Promise<boolean>;
|
|
724
|
+
/**
|
|
725
|
+
* Set network type and notify listeners
|
|
726
|
+
*/
|
|
727
|
+
setNetworkType(type: NetworkType): void;
|
|
728
|
+
/**
|
|
729
|
+
* Set downlink speed in Mbps
|
|
730
|
+
*/
|
|
731
|
+
setDownlink(mbps: number): void;
|
|
732
|
+
/**
|
|
733
|
+
* Set RTT in milliseconds
|
|
734
|
+
*/
|
|
735
|
+
setRtt(ms: number): void;
|
|
736
|
+
/**
|
|
737
|
+
* Set metered status (e.g., cellular data)
|
|
738
|
+
*/
|
|
739
|
+
setMetered(metered: boolean): void;
|
|
740
|
+
/**
|
|
741
|
+
* Set online/offline status
|
|
742
|
+
*/
|
|
743
|
+
setOnline(online: boolean): void;
|
|
744
|
+
/**
|
|
745
|
+
* Simulate network change sequence for testing
|
|
746
|
+
*/
|
|
747
|
+
simulateNetworkChanges(sequence: NetworkType[], delayMs?: number): Promise<void>;
|
|
748
|
+
/**
|
|
749
|
+
* Reset to default state
|
|
750
|
+
*/
|
|
751
|
+
reset(): void;
|
|
752
|
+
private notifyListeners;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Mock Video Loader
|
|
757
|
+
*
|
|
758
|
+
* Simulates video preloading for testing and development.
|
|
759
|
+
* Does not actually load video data, just tracks preload state.
|
|
760
|
+
*/
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* Configuration options for MockVideoLoader
|
|
764
|
+
*/
|
|
765
|
+
interface MockVideoLoaderOptions {
|
|
766
|
+
/** Simulated preload delay in ms (default: 100) */
|
|
767
|
+
preloadDelayMs?: number;
|
|
768
|
+
/** Whether preloads should succeed (default: true) */
|
|
769
|
+
shouldSucceed?: boolean;
|
|
770
|
+
/** Error message for failed preloads */
|
|
771
|
+
errorMessage?: string;
|
|
772
|
+
/** Simulated bytes per preload (default: 500000 = 500KB) */
|
|
773
|
+
bytesPerPreload?: number;
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Tracked preload state
|
|
777
|
+
*/
|
|
778
|
+
interface PreloadState {
|
|
779
|
+
status: PreloadStatus;
|
|
780
|
+
loadedBytes: number;
|
|
781
|
+
error?: Error;
|
|
782
|
+
abortController?: AbortController;
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Mock implementation of IVideoLoader
|
|
786
|
+
*
|
|
787
|
+
* Useful for:
|
|
788
|
+
* - Unit testing ResourceGovernor logic
|
|
789
|
+
* - Development without real video loading
|
|
790
|
+
* - Simulating various preload scenarios
|
|
791
|
+
*
|
|
792
|
+
* @example
|
|
793
|
+
* ```typescript
|
|
794
|
+
* const loader = new MockVideoLoader({ preloadDelayMs: 50 });
|
|
795
|
+
*
|
|
796
|
+
* await loader.preload('video-1', { url: '...', type: 'mp4' });
|
|
797
|
+
* expect(loader.isPreloaded('video-1')).toBe(true);
|
|
798
|
+
*
|
|
799
|
+
* // Simulate failure
|
|
800
|
+
* loader.setShouldSucceed(false);
|
|
801
|
+
* ```
|
|
802
|
+
*/
|
|
803
|
+
declare class MockVideoLoader implements IVideoLoader {
|
|
804
|
+
private preloads;
|
|
805
|
+
private preloadDelayMs;
|
|
806
|
+
private shouldSucceed;
|
|
807
|
+
private errorMessage;
|
|
808
|
+
private bytesPerPreload;
|
|
809
|
+
constructor(options?: MockVideoLoaderOptions);
|
|
810
|
+
preload(_videoId: string, _source: VideoSource, _config?: PreloadConfig): Promise<PreloadResult>;
|
|
811
|
+
cancelPreload(videoId: string): void;
|
|
812
|
+
isPreloaded(videoId: string): boolean;
|
|
813
|
+
getPreloadStatus(videoId: string): PreloadStatus;
|
|
814
|
+
clearPreload(videoId: string): void;
|
|
815
|
+
clearAll(): void;
|
|
816
|
+
getTotalPreloadedBytes(): number;
|
|
817
|
+
/**
|
|
818
|
+
* Set whether preloads should succeed
|
|
819
|
+
*/
|
|
820
|
+
setShouldSucceed(shouldSucceed: boolean): void;
|
|
821
|
+
/**
|
|
822
|
+
* Set preload delay in milliseconds
|
|
823
|
+
*/
|
|
824
|
+
setPreloadDelay(ms: number): void;
|
|
825
|
+
/**
|
|
826
|
+
* Set error message for failed preloads
|
|
827
|
+
*/
|
|
828
|
+
setErrorMessage(message: string): void;
|
|
829
|
+
/**
|
|
830
|
+
* Get all tracked preloads (for testing assertions)
|
|
831
|
+
*/
|
|
832
|
+
getPreloads(): Map<string, PreloadState>;
|
|
833
|
+
/**
|
|
834
|
+
* Reset all state
|
|
835
|
+
*/
|
|
836
|
+
reset(): void;
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Mock implementation of IPosterLoader
|
|
840
|
+
*
|
|
841
|
+
* Separate class for poster preloading to match interface contracts
|
|
842
|
+
*/
|
|
843
|
+
declare class MockPosterLoader implements IPosterLoader {
|
|
844
|
+
private posterCache;
|
|
845
|
+
private preloadDelayMs;
|
|
846
|
+
constructor(preloadDelayMs?: number);
|
|
847
|
+
preload(url: string): Promise<void>;
|
|
848
|
+
isCached(url: string): boolean;
|
|
849
|
+
clearCache(): void;
|
|
850
|
+
/**
|
|
851
|
+
* Set preload delay in milliseconds
|
|
852
|
+
*/
|
|
853
|
+
setPreloadDelay(ms: number): void;
|
|
854
|
+
/**
|
|
855
|
+
* Reset all state
|
|
856
|
+
*/
|
|
857
|
+
reset(): void;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* Preset Adapters - Type Definitions
|
|
862
|
+
*
|
|
863
|
+
* Types cho REST Preset Adapter factory
|
|
864
|
+
* Cho phép Host App chỉ cần config thay vì viết adapter từ đầu
|
|
865
|
+
*/
|
|
866
|
+
|
|
867
|
+
/**
|
|
868
|
+
* Authentication configuration
|
|
869
|
+
*/
|
|
870
|
+
interface AuthConfig {
|
|
871
|
+
/**
|
|
872
|
+
* Get current access token
|
|
873
|
+
* Return null if not logged in
|
|
874
|
+
*/
|
|
875
|
+
getAccessToken: () => string | null | Promise<string | null>;
|
|
876
|
+
/**
|
|
877
|
+
* Refresh token when access token expires
|
|
878
|
+
* Return new access token
|
|
879
|
+
* Throw error if refresh fails
|
|
880
|
+
*/
|
|
881
|
+
refreshToken?: () => Promise<string>;
|
|
882
|
+
/**
|
|
883
|
+
* Callback when auth fails completely (after refresh also fails)
|
|
884
|
+
* Usually used to redirect to login page
|
|
885
|
+
*/
|
|
886
|
+
onAuthError?: (error: AuthError) => void;
|
|
887
|
+
/**
|
|
888
|
+
* Custom headers for all requests
|
|
889
|
+
* Example: { 'X-App-Version': '1.0.0' }
|
|
890
|
+
*/
|
|
891
|
+
headers?: Record<string, string>;
|
|
892
|
+
/**
|
|
893
|
+
* Token header name
|
|
894
|
+
* @default 'Authorization'
|
|
895
|
+
*/
|
|
896
|
+
tokenHeader?: string;
|
|
897
|
+
/**
|
|
898
|
+
* Token prefix in header
|
|
899
|
+
* @default 'Bearer'
|
|
900
|
+
* Set '' if no prefix needed
|
|
901
|
+
*/
|
|
902
|
+
tokenPrefix?: string;
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Auth error information
|
|
906
|
+
*/
|
|
907
|
+
interface AuthError {
|
|
908
|
+
status: number;
|
|
909
|
+
message: string;
|
|
910
|
+
originalError?: Error;
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* REST endpoint mapping
|
|
914
|
+
*/
|
|
915
|
+
interface RESTEndpointMap {
|
|
916
|
+
/**
|
|
917
|
+
* Feed-related endpoints
|
|
918
|
+
*/
|
|
919
|
+
feed: {
|
|
920
|
+
/**
|
|
921
|
+
* GET endpoint for listing videos
|
|
922
|
+
* Query params auto-added: cursor, limit
|
|
923
|
+
* Example: '/videos', '/reels', '/shorts'
|
|
924
|
+
*/
|
|
925
|
+
list: string;
|
|
926
|
+
/**
|
|
927
|
+
* GET endpoint for video detail
|
|
928
|
+
* :id will be replaced with video ID
|
|
929
|
+
* Example: '/videos/:id', '/reels/:id'
|
|
930
|
+
*/
|
|
931
|
+
detail: string;
|
|
932
|
+
};
|
|
933
|
+
/**
|
|
934
|
+
* Interaction endpoints
|
|
935
|
+
*/
|
|
936
|
+
interaction: {
|
|
937
|
+
/** POST /videos/:id/like */
|
|
938
|
+
like: string;
|
|
939
|
+
/** DELETE /videos/:id/like */
|
|
940
|
+
unlike: string;
|
|
941
|
+
/** POST /users/:id/follow */
|
|
942
|
+
follow: string;
|
|
943
|
+
/** DELETE /users/:id/follow */
|
|
944
|
+
unfollow: string;
|
|
945
|
+
/** POST /videos/:id/comments */
|
|
946
|
+
comment: string;
|
|
947
|
+
/** DELETE /comments/:id */
|
|
948
|
+
deleteComment: string;
|
|
949
|
+
/** POST /videos/:id/share (optional) */
|
|
950
|
+
share?: string;
|
|
951
|
+
};
|
|
952
|
+
/**
|
|
953
|
+
* Analytics endpoints (optional)
|
|
954
|
+
* If not provided, analytics adapter will be no-op
|
|
955
|
+
*/
|
|
956
|
+
analytics?: {
|
|
957
|
+
/** POST endpoint for batch events */
|
|
958
|
+
batch: string;
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* HTTP request configuration
|
|
963
|
+
*/
|
|
964
|
+
interface RESTRequestConfig {
|
|
965
|
+
/**
|
|
966
|
+
* Default query params for all requests
|
|
967
|
+
* Example: { app_id: 'my-app', version: '2' }
|
|
968
|
+
*/
|
|
969
|
+
defaultParams?: Record<string, string>;
|
|
970
|
+
/**
|
|
971
|
+
* Request timeout in milliseconds
|
|
972
|
+
* @default 10000 (10s)
|
|
973
|
+
*/
|
|
974
|
+
timeout?: number;
|
|
975
|
+
/**
|
|
976
|
+
* Retry configuration
|
|
977
|
+
*/
|
|
978
|
+
retry?: RetryConfig;
|
|
979
|
+
/**
|
|
980
|
+
* Pagination param names
|
|
981
|
+
* @default { cursor: 'cursor', limit: 'limit' }
|
|
982
|
+
*/
|
|
983
|
+
pagination?: {
|
|
984
|
+
cursor: string;
|
|
985
|
+
limit: string;
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Retry configuration
|
|
990
|
+
*/
|
|
991
|
+
interface RetryConfig {
|
|
992
|
+
/**
|
|
993
|
+
* Max retry attempts
|
|
994
|
+
* @default 3
|
|
995
|
+
*/
|
|
996
|
+
maxRetries: number;
|
|
997
|
+
/**
|
|
998
|
+
* Delay between retries (ms)
|
|
999
|
+
* @default 1000
|
|
1000
|
+
*/
|
|
1001
|
+
retryDelay: number;
|
|
1002
|
+
/**
|
|
1003
|
+
* HTTP status codes to retry on
|
|
1004
|
+
* @default [408, 429, 500, 502, 503, 504]
|
|
1005
|
+
*/
|
|
1006
|
+
retryOn: number[];
|
|
1007
|
+
/**
|
|
1008
|
+
* Use exponential backoff
|
|
1009
|
+
* @default true
|
|
1010
|
+
*/
|
|
1011
|
+
exponentialBackoff?: boolean;
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Response transform configuration
|
|
1015
|
+
*/
|
|
1016
|
+
interface TransformConfig {
|
|
1017
|
+
/**
|
|
1018
|
+
* Transform single video item from API response
|
|
1019
|
+
* If not provided, uses default transform
|
|
1020
|
+
*/
|
|
1021
|
+
videoItem?: (apiResponse: unknown) => VideoItem;
|
|
1022
|
+
/**
|
|
1023
|
+
* Transform feed response from API
|
|
1024
|
+
* If not provided, uses default transform
|
|
1025
|
+
*/
|
|
1026
|
+
feedResponse?: (apiResponse: unknown) => {
|
|
1027
|
+
items: unknown[];
|
|
1028
|
+
nextCursor: string | null;
|
|
1029
|
+
hasMore: boolean;
|
|
1030
|
+
};
|
|
1031
|
+
/**
|
|
1032
|
+
* Field mapping for default transforms
|
|
1033
|
+
* Used when API field names differ from defaults
|
|
1034
|
+
*/
|
|
1035
|
+
fieldMap?: FieldMapConfig;
|
|
1036
|
+
}
|
|
1037
|
+
/**
|
|
1038
|
+
* Field mapping configuration for auto-transform
|
|
1039
|
+
*/
|
|
1040
|
+
interface FieldMapConfig {
|
|
1041
|
+
/**
|
|
1042
|
+
* VideoItem field mapping
|
|
1043
|
+
* Key: SDK field path, Value: API field path (dot notation)
|
|
1044
|
+
* Example: { 'author.name': 'creator.display_name' }
|
|
1045
|
+
*/
|
|
1046
|
+
video?: Record<string, string>;
|
|
1047
|
+
/**
|
|
1048
|
+
* FeedResponse field mapping
|
|
1049
|
+
*/
|
|
1050
|
+
feed?: {
|
|
1051
|
+
items?: string;
|
|
1052
|
+
nextCursor?: string;
|
|
1053
|
+
hasMore?: string;
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* REST Preset Adapter configuration
|
|
1058
|
+
*/
|
|
1059
|
+
interface RESTPresetConfig {
|
|
1060
|
+
/**
|
|
1061
|
+
* Base URL for all API requests
|
|
1062
|
+
* Example: 'https://api.myapp.com/v1'
|
|
1063
|
+
*/
|
|
1064
|
+
baseUrl: string;
|
|
1065
|
+
/**
|
|
1066
|
+
* Auth configuration
|
|
1067
|
+
*/
|
|
1068
|
+
auth: AuthConfig;
|
|
1069
|
+
/**
|
|
1070
|
+
* Endpoint mapping
|
|
1071
|
+
*/
|
|
1072
|
+
endpoints: RESTEndpointMap;
|
|
1073
|
+
/**
|
|
1074
|
+
* Response transforms (optional)
|
|
1075
|
+
* If not provided, uses default transforms
|
|
1076
|
+
*/
|
|
1077
|
+
transforms?: TransformConfig;
|
|
1078
|
+
/**
|
|
1079
|
+
* Request configuration (optional)
|
|
1080
|
+
*/
|
|
1081
|
+
request?: RESTRequestConfig;
|
|
1082
|
+
/**
|
|
1083
|
+
* Logger adapter for error/warning logging
|
|
1084
|
+
* If not provided, uses console in dev, silent in prod
|
|
1085
|
+
*/
|
|
1086
|
+
logger?: ILogger;
|
|
1087
|
+
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Factory output - all preset adapters
|
|
1090
|
+
*/
|
|
1091
|
+
interface PresetAdapters {
|
|
1092
|
+
dataSource: IDataSource;
|
|
1093
|
+
interaction: IInteraction;
|
|
1094
|
+
analytics: IAnalytics;
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* HTTP request options (internal)
|
|
1098
|
+
*/
|
|
1099
|
+
interface HttpRequestOptions {
|
|
1100
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
1101
|
+
path: string;
|
|
1102
|
+
params?: Record<string, string | number | null | undefined>;
|
|
1103
|
+
body?: unknown;
|
|
1104
|
+
pathParams?: Record<string, string>;
|
|
1105
|
+
}
|
|
1106
|
+
/**
|
|
1107
|
+
* Default retry config
|
|
1108
|
+
*/
|
|
1109
|
+
declare const DEFAULT_RETRY_CONFIG: Required<RetryConfig>;
|
|
1110
|
+
/**
|
|
1111
|
+
* Default request config
|
|
1112
|
+
*/
|
|
1113
|
+
declare const DEFAULT_REQUEST_CONFIG: Required<RESTRequestConfig>;
|
|
1114
|
+
|
|
1115
|
+
/**
|
|
1116
|
+
* createRESTAdapters - Factory function for REST preset adapters
|
|
1117
|
+
*
|
|
1118
|
+
* Tạo bộ adapters hoàn chỉnh từ config đơn giản.
|
|
1119
|
+
* Host App chỉ cần cung cấp baseUrl, auth, và endpoint mapping.
|
|
1120
|
+
*
|
|
1121
|
+
* @example
|
|
1122
|
+
* ```typescript
|
|
1123
|
+
* import { createRESTAdapters } from '@xhub-short/adapters/preset';
|
|
1124
|
+
*
|
|
1125
|
+
* const adapters = createRESTAdapters({
|
|
1126
|
+
* baseUrl: 'https://api.myapp.com/v1',
|
|
1127
|
+
* auth: {
|
|
1128
|
+
* getAccessToken: () => localStorage.getItem('token'),
|
|
1129
|
+
* refreshToken: async () => {
|
|
1130
|
+
* const res = await fetch('/auth/refresh', { method: 'POST' });
|
|
1131
|
+
* const { token } = await res.json();
|
|
1132
|
+
* return token;
|
|
1133
|
+
* },
|
|
1134
|
+
* },
|
|
1135
|
+
* endpoints: {
|
|
1136
|
+
* feed: { list: '/videos', detail: '/videos/:id' },
|
|
1137
|
+
* interaction: {
|
|
1138
|
+
* like: '/videos/:id/like',
|
|
1139
|
+
* unlike: '/videos/:id/like',
|
|
1140
|
+
* follow: '/users/:id/follow',
|
|
1141
|
+
* unfollow: '/users/:id/follow',
|
|
1142
|
+
* comment: '/videos/:id/comments',
|
|
1143
|
+
* deleteComment: '/comments/:id',
|
|
1144
|
+
* },
|
|
1145
|
+
* },
|
|
1146
|
+
* });
|
|
1147
|
+
*
|
|
1148
|
+
* const sdk = createSDK({ adapters });
|
|
1149
|
+
* ```
|
|
1150
|
+
*/
|
|
1151
|
+
|
|
1152
|
+
/**
|
|
1153
|
+
* Create REST preset adapters from config
|
|
1154
|
+
*
|
|
1155
|
+
* @param config - REST preset configuration
|
|
1156
|
+
* @returns Object containing dataSource, interaction, and analytics adapters
|
|
1157
|
+
*/
|
|
1158
|
+
declare function createRESTAdapters(config: RESTPresetConfig): PresetAdapters;
|
|
1159
|
+
|
|
1160
|
+
/**
|
|
1161
|
+
* Browser Video Loader - Real video preloading implementation
|
|
1162
|
+
*
|
|
1163
|
+
* Preloads video data using appropriate strategy:
|
|
1164
|
+
* - MP4: Fetch with Range header (first N bytes)
|
|
1165
|
+
* - HLS: Load manifest + first segment (if hls.js available)
|
|
1166
|
+
*
|
|
1167
|
+
* Uses browser's Cache API for storage when available.
|
|
1168
|
+
*/
|
|
1169
|
+
|
|
1170
|
+
/**
|
|
1171
|
+
* Configuration for BrowserVideoLoader
|
|
1172
|
+
*/
|
|
1173
|
+
interface BrowserVideoLoaderConfig {
|
|
1174
|
+
/** Logger for debugging */
|
|
1175
|
+
logger?: ILogger;
|
|
1176
|
+
/** Default max bytes to preload for MP4 (default: 512KB) */
|
|
1177
|
+
defaultMaxBytes?: number;
|
|
1178
|
+
/** Default timeout in ms (default: 10000) */
|
|
1179
|
+
defaultTimeout?: number;
|
|
1180
|
+
/** Use Cache API when available (default: true) */
|
|
1181
|
+
useCache?: boolean;
|
|
1182
|
+
/** Cache name for stored video chunks (default: 'sv-video-cache') */
|
|
1183
|
+
cacheName?: string;
|
|
1184
|
+
}
|
|
1185
|
+
/**
|
|
1186
|
+
* BrowserVideoLoader - Real browser video preloading
|
|
1187
|
+
*
|
|
1188
|
+
* Strategies:
|
|
1189
|
+
* - **MP4:** Fetch first chunk (500KB-1MB) via Range request
|
|
1190
|
+
* - **HLS:** Not implemented (requires hls.js injection)
|
|
1191
|
+
* - **Native HLS (Safari):** Falls back to standard preload
|
|
1192
|
+
*
|
|
1193
|
+
* @example
|
|
1194
|
+
* ```typescript
|
|
1195
|
+
* const loader = new BrowserVideoLoader();
|
|
1196
|
+
*
|
|
1197
|
+
* // Preload next video
|
|
1198
|
+
* const result = await loader.preload('video-123', {
|
|
1199
|
+
* url: 'https://example.com/video.mp4',
|
|
1200
|
+
* type: 'mp4',
|
|
1201
|
+
* });
|
|
1202
|
+
*
|
|
1203
|
+
* console.log(result.status); // 'ready'
|
|
1204
|
+
* console.log(result.loadedBytes); // 524288 (512KB)
|
|
1205
|
+
* ```
|
|
1206
|
+
*/
|
|
1207
|
+
declare class BrowserVideoLoader implements IVideoLoader {
|
|
1208
|
+
private readonly logger?;
|
|
1209
|
+
private readonly config;
|
|
1210
|
+
private readonly preloads;
|
|
1211
|
+
private cache;
|
|
1212
|
+
constructor(config?: BrowserVideoLoaderConfig);
|
|
1213
|
+
/**
|
|
1214
|
+
* Initialize Cache API
|
|
1215
|
+
*/
|
|
1216
|
+
private initCache;
|
|
1217
|
+
/**
|
|
1218
|
+
* Preload video source
|
|
1219
|
+
*/
|
|
1220
|
+
preload(videoId: string, source: VideoSource, config?: PreloadConfig): Promise<PreloadResult>;
|
|
1221
|
+
/**
|
|
1222
|
+
* Preload MP4 video using Range request
|
|
1223
|
+
*/
|
|
1224
|
+
private preloadMP4;
|
|
1225
|
+
/**
|
|
1226
|
+
* Preload HLS - fetch manifest only
|
|
1227
|
+
* Full segment preload requires hls.js which is optional
|
|
1228
|
+
*/
|
|
1229
|
+
private preloadHLS;
|
|
1230
|
+
/**
|
|
1231
|
+
* Cancel preload
|
|
1232
|
+
*/
|
|
1233
|
+
cancelPreload(videoId: string): void;
|
|
1234
|
+
/**
|
|
1235
|
+
* Check if video is preloaded
|
|
1236
|
+
*/
|
|
1237
|
+
isPreloaded(videoId: string): boolean;
|
|
1238
|
+
/**
|
|
1239
|
+
* Get preload status
|
|
1240
|
+
*/
|
|
1241
|
+
getPreloadStatus(videoId: string): PreloadStatus;
|
|
1242
|
+
/**
|
|
1243
|
+
* Clear preloaded data for a video
|
|
1244
|
+
*/
|
|
1245
|
+
clearPreload(videoId: string): void;
|
|
1246
|
+
/**
|
|
1247
|
+
* Clear all preloaded data
|
|
1248
|
+
*/
|
|
1249
|
+
clearAll(): void;
|
|
1250
|
+
/**
|
|
1251
|
+
* Get total preloaded bytes
|
|
1252
|
+
*/
|
|
1253
|
+
getTotalPreloadedBytes(): number;
|
|
1254
|
+
}
|
|
1255
|
+
/**
|
|
1256
|
+
* BrowserPosterLoader - Browser image preloading
|
|
1257
|
+
*
|
|
1258
|
+
* Uses Image object for preloading poster/thumbnail images.
|
|
1259
|
+
* Simple and effective for warming browser cache.
|
|
1260
|
+
*
|
|
1261
|
+
* @example
|
|
1262
|
+
* ```typescript
|
|
1263
|
+
* const posterLoader = new BrowserPosterLoader();
|
|
1264
|
+
*
|
|
1265
|
+
* await posterLoader.preload('https://example.com/thumb.jpg');
|
|
1266
|
+
* console.log(posterLoader.isCached('https://example.com/thumb.jpg')); // true
|
|
1267
|
+
* ```
|
|
1268
|
+
*/
|
|
1269
|
+
declare class BrowserPosterLoader implements IPosterLoader {
|
|
1270
|
+
private readonly logger?;
|
|
1271
|
+
private readonly cache;
|
|
1272
|
+
private readonly pending;
|
|
1273
|
+
private readonly timeout;
|
|
1274
|
+
constructor(config?: {
|
|
1275
|
+
logger?: ILogger;
|
|
1276
|
+
timeout?: number;
|
|
1277
|
+
});
|
|
1278
|
+
/**
|
|
1279
|
+
* Preload a poster image
|
|
1280
|
+
*/
|
|
1281
|
+
preload(url: string): Promise<void>;
|
|
1282
|
+
/**
|
|
1283
|
+
* Check if poster is cached
|
|
1284
|
+
*/
|
|
1285
|
+
isCached(url: string): boolean;
|
|
1286
|
+
/**
|
|
1287
|
+
* Clear poster cache
|
|
1288
|
+
*/
|
|
1289
|
+
clearCache(): void;
|
|
1290
|
+
}
|
|
1291
|
+
/**
|
|
1292
|
+
* Create browser video loader with default config
|
|
1293
|
+
*/
|
|
1294
|
+
declare function createBrowserVideoLoader(config?: BrowserVideoLoaderConfig): BrowserVideoLoader;
|
|
1295
|
+
/**
|
|
1296
|
+
* Create browser poster loader with default config
|
|
1297
|
+
*/
|
|
1298
|
+
declare function createBrowserPosterLoader(config?: {
|
|
1299
|
+
logger?: ILogger;
|
|
1300
|
+
timeout?: number;
|
|
1301
|
+
}): BrowserPosterLoader;
|
|
1302
|
+
|
|
1303
|
+
/**
|
|
1304
|
+
* LocalStorage Adapter - Browser localStorage implementation
|
|
1305
|
+
*
|
|
1306
|
+
* Real implementation of ISessionStorage using browser localStorage.
|
|
1307
|
+
* Automatically handles JSON serialization and SDK namespacing.
|
|
1308
|
+
*/
|
|
1309
|
+
|
|
1310
|
+
/**
|
|
1311
|
+
* Configuration for LocalStorageAdapter
|
|
1312
|
+
*/
|
|
1313
|
+
interface LocalStorageConfig {
|
|
1314
|
+
/** Storage key prefix for namespacing (default: 'sv-') */
|
|
1315
|
+
prefix?: string;
|
|
1316
|
+
/** Maximum age of snapshot in ms (default: 24h) */
|
|
1317
|
+
maxSnapshotAge?: number;
|
|
1318
|
+
/** Logger for debugging */
|
|
1319
|
+
logger?: ILogger;
|
|
1320
|
+
}
|
|
1321
|
+
/**
|
|
1322
|
+
* LocalStorageAdapter - Real browser localStorage implementation
|
|
1323
|
+
*
|
|
1324
|
+
* Features:
|
|
1325
|
+
* - Auto JSON serialization/deserialization
|
|
1326
|
+
* - SDK key namespacing (avoids conflicts with host app)
|
|
1327
|
+
* - Graceful error handling (returns null instead of throwing)
|
|
1328
|
+
* - Session snapshot with automatic staleness checking
|
|
1329
|
+
*
|
|
1330
|
+
* @example
|
|
1331
|
+
* ```typescript
|
|
1332
|
+
* const storage = new LocalStorageAdapter({ prefix: 'myapp-' });
|
|
1333
|
+
*
|
|
1334
|
+
* await storage.set('user', { id: '123', name: 'John' });
|
|
1335
|
+
* const user = await storage.get('user');
|
|
1336
|
+
* ```
|
|
1337
|
+
*/
|
|
1338
|
+
declare class LocalStorageAdapter implements IStorage {
|
|
1339
|
+
protected readonly prefix: string;
|
|
1340
|
+
protected readonly logger?: ILogger;
|
|
1341
|
+
constructor(config?: LocalStorageConfig);
|
|
1342
|
+
/**
|
|
1343
|
+
* Build namespaced key
|
|
1344
|
+
*/
|
|
1345
|
+
protected buildKey(key: string): string;
|
|
1346
|
+
/**
|
|
1347
|
+
* Get a value from localStorage
|
|
1348
|
+
*/
|
|
1349
|
+
get<T>(key: string): Promise<T | null>;
|
|
1350
|
+
/**
|
|
1351
|
+
* Set a value in localStorage
|
|
1352
|
+
*/
|
|
1353
|
+
set<T>(key: string, value: T): Promise<void>;
|
|
1354
|
+
/**
|
|
1355
|
+
* Remove a value from localStorage
|
|
1356
|
+
*/
|
|
1357
|
+
remove(key: string): Promise<void>;
|
|
1358
|
+
/**
|
|
1359
|
+
* Clear all SDK-namespaced keys
|
|
1360
|
+
*/
|
|
1361
|
+
clear(): Promise<void>;
|
|
1362
|
+
/**
|
|
1363
|
+
* Get all SDK-namespaced keys
|
|
1364
|
+
*/
|
|
1365
|
+
keys(): Promise<string[]>;
|
|
1366
|
+
/**
|
|
1367
|
+
* Clear old entries when quota exceeded (simple LRU-like behavior)
|
|
1368
|
+
*/
|
|
1369
|
+
private clearOldEntries;
|
|
1370
|
+
}
|
|
1371
|
+
/**
|
|
1372
|
+
* LocalSessionStorageAdapter - ISessionStorage implementation
|
|
1373
|
+
*
|
|
1374
|
+
* Extends LocalStorageAdapter with session snapshot methods.
|
|
1375
|
+
* Used by LifecycleManager for state restoration.
|
|
1376
|
+
*
|
|
1377
|
+
* @example
|
|
1378
|
+
* ```typescript
|
|
1379
|
+
* const sessionStorage = new LocalSessionStorageAdapter();
|
|
1380
|
+
*
|
|
1381
|
+
* // Save snapshot when user leaves
|
|
1382
|
+
* await sessionStorage.saveSnapshot({
|
|
1383
|
+
* currentIndex: 5,
|
|
1384
|
+
* cursor: 'abc123',
|
|
1385
|
+
* savedAt: Date.now(),
|
|
1386
|
+
* });
|
|
1387
|
+
*
|
|
1388
|
+
* // Restore when user returns
|
|
1389
|
+
* const snapshot = await sessionStorage.loadSnapshot();
|
|
1390
|
+
* if (snapshot) {
|
|
1391
|
+
* // Restore state
|
|
1392
|
+
* }
|
|
1393
|
+
* ```
|
|
1394
|
+
*/
|
|
1395
|
+
declare class LocalSessionStorageAdapter extends LocalStorageAdapter implements ISessionStorage {
|
|
1396
|
+
private readonly maxSnapshotAge;
|
|
1397
|
+
constructor(config?: LocalStorageConfig);
|
|
1398
|
+
/**
|
|
1399
|
+
* Save session snapshot
|
|
1400
|
+
*/
|
|
1401
|
+
saveSnapshot(snapshot: SessionSnapshot): Promise<void>;
|
|
1402
|
+
/**
|
|
1403
|
+
* Load session snapshot (returns null if stale)
|
|
1404
|
+
*/
|
|
1405
|
+
loadSnapshot(): Promise<SessionSnapshot | null>;
|
|
1406
|
+
/**
|
|
1407
|
+
* Clear session snapshot
|
|
1408
|
+
*/
|
|
1409
|
+
clearSnapshot(): Promise<void>;
|
|
1410
|
+
}
|
|
1411
|
+
/**
|
|
1412
|
+
* Create localStorage adapter with default config
|
|
1413
|
+
*/
|
|
1414
|
+
declare function createLocalStorageAdapter(config?: LocalStorageConfig): LocalStorageAdapter;
|
|
1415
|
+
/**
|
|
1416
|
+
* Create session storage adapter with default config
|
|
1417
|
+
*/
|
|
1418
|
+
declare function createSessionStorageAdapter(config?: LocalStorageConfig): LocalSessionStorageAdapter;
|
|
1419
|
+
|
|
1420
|
+
/**
|
|
1421
|
+
* Web Network Adapter - Browser Navigator.connection implementation
|
|
1422
|
+
*
|
|
1423
|
+
* Real implementation of INetworkAdapter using Web APIs.
|
|
1424
|
+
* Provides network detection with fallbacks for browsers without Network Information API.
|
|
1425
|
+
*/
|
|
1426
|
+
|
|
1427
|
+
/**
|
|
1428
|
+
* Configuration for WebNetworkAdapter
|
|
1429
|
+
*/
|
|
1430
|
+
interface WebNetworkConfig {
|
|
1431
|
+
/** Logger for debugging */
|
|
1432
|
+
logger?: ILogger;
|
|
1433
|
+
/** Fallback network type when API not available (default: 'wifi') */
|
|
1434
|
+
fallbackType?: NetworkType;
|
|
1435
|
+
/** Fallback downlink in Mbps (default: 10) */
|
|
1436
|
+
fallbackDownlink?: number;
|
|
1437
|
+
}
|
|
1438
|
+
/**
|
|
1439
|
+
* WebNetworkAdapter - Browser Network Information API implementation
|
|
1440
|
+
*
|
|
1441
|
+
* Features:
|
|
1442
|
+
* - Uses Navigator.connection when available
|
|
1443
|
+
* - Graceful fallback for unsupported browsers
|
|
1444
|
+
* - Online/offline detection via navigator.onLine
|
|
1445
|
+
* - Network change listeners via connection.onchange
|
|
1446
|
+
*
|
|
1447
|
+
* @example
|
|
1448
|
+
* ```typescript
|
|
1449
|
+
* const network = new WebNetworkAdapter();
|
|
1450
|
+
*
|
|
1451
|
+
* const type = await network.getNetworkType();
|
|
1452
|
+
* console.log('Network:', type); // 'wifi', '4g', 'offline', etc.
|
|
1453
|
+
*
|
|
1454
|
+
* // Listen for changes
|
|
1455
|
+
* const unsubscribe = network.onNetworkChange((type) => {
|
|
1456
|
+
* console.log('Network changed to:', type);
|
|
1457
|
+
* });
|
|
1458
|
+
* ```
|
|
1459
|
+
*/
|
|
1460
|
+
declare class WebNetworkAdapter implements INetworkAdapter {
|
|
1461
|
+
private readonly logger?;
|
|
1462
|
+
private readonly fallbackType;
|
|
1463
|
+
private readonly fallbackDownlink;
|
|
1464
|
+
private readonly listeners;
|
|
1465
|
+
private connection;
|
|
1466
|
+
private boundOnlineHandler;
|
|
1467
|
+
private boundOfflineHandler;
|
|
1468
|
+
private boundChangeHandler;
|
|
1469
|
+
constructor(config?: WebNetworkConfig);
|
|
1470
|
+
/**
|
|
1471
|
+
* Get current network type
|
|
1472
|
+
*/
|
|
1473
|
+
getNetworkType(): Promise<NetworkType>;
|
|
1474
|
+
/**
|
|
1475
|
+
* Get detailed network quality
|
|
1476
|
+
*/
|
|
1477
|
+
getNetworkQuality(): Promise<NetworkQuality>;
|
|
1478
|
+
/**
|
|
1479
|
+
* Subscribe to network changes
|
|
1480
|
+
*/
|
|
1481
|
+
onNetworkChange(callback: (type: NetworkType) => void): () => void;
|
|
1482
|
+
/**
|
|
1483
|
+
* Check if currently online
|
|
1484
|
+
*/
|
|
1485
|
+
isOnline(): Promise<boolean>;
|
|
1486
|
+
/**
|
|
1487
|
+
* Cleanup listeners
|
|
1488
|
+
*/
|
|
1489
|
+
destroy(): void;
|
|
1490
|
+
private setupListeners;
|
|
1491
|
+
private handleOnline;
|
|
1492
|
+
private handleOffline;
|
|
1493
|
+
private handleConnectionChange;
|
|
1494
|
+
private notifyListeners;
|
|
1495
|
+
}
|
|
1496
|
+
/**
|
|
1497
|
+
* Create web network adapter with default config
|
|
1498
|
+
*/
|
|
1499
|
+
declare function createWebNetworkAdapter(config?: WebNetworkConfig): WebNetworkAdapter;
|
|
1500
|
+
|
|
1501
|
+
/**
|
|
1502
|
+
* createBrowserAdapters - Full preset factory for browser environment
|
|
1503
|
+
*
|
|
1504
|
+
* Creates all adapters needed for SDK with a single config object.
|
|
1505
|
+
* Combines REST API adapters with browser-specific adapters.
|
|
1506
|
+
*
|
|
1507
|
+
* @example
|
|
1508
|
+
* ```typescript
|
|
1509
|
+
* import { createBrowserAdapters } from '@xhub-short/adapters';
|
|
1510
|
+
*
|
|
1511
|
+
* const adapters = createBrowserAdapters({
|
|
1512
|
+
* baseUrl: 'https://api.myapp.com/v1',
|
|
1513
|
+
* auth: { getAccessToken: () => token },
|
|
1514
|
+
* endpoints: { ... },
|
|
1515
|
+
* });
|
|
1516
|
+
*
|
|
1517
|
+
* const sdk = createSDK({
|
|
1518
|
+
* dataSource: adapters.dataSource,
|
|
1519
|
+
* interaction: adapters.interaction,
|
|
1520
|
+
* analytics: adapters.analytics,
|
|
1521
|
+
* storage: adapters.storage,
|
|
1522
|
+
* network: adapters.network,
|
|
1523
|
+
* videoLoader: adapters.videoLoader,
|
|
1524
|
+
* posterLoader: adapters.posterLoader,
|
|
1525
|
+
* });
|
|
1526
|
+
* ```
|
|
1527
|
+
*/
|
|
1528
|
+
|
|
1529
|
+
/**
|
|
1530
|
+
* Configuration for createBrowserAdapters
|
|
1531
|
+
*/
|
|
1532
|
+
interface BrowserAdaptersConfig extends RESTPresetConfig {
|
|
1533
|
+
/** Storage configuration */
|
|
1534
|
+
storage?: LocalStorageConfig;
|
|
1535
|
+
/** Network configuration */
|
|
1536
|
+
network?: WebNetworkConfig;
|
|
1537
|
+
/** Video loader configuration */
|
|
1538
|
+
videoLoader?: BrowserVideoLoaderConfig;
|
|
1539
|
+
/** Poster loader timeout */
|
|
1540
|
+
posterTimeout?: number;
|
|
1541
|
+
}
|
|
1542
|
+
/**
|
|
1543
|
+
* Full adapter set returned by createBrowserAdapters
|
|
1544
|
+
*/
|
|
1545
|
+
interface FullPresetAdapters {
|
|
1546
|
+
/** Data source adapter (REST API) */
|
|
1547
|
+
dataSource: IDataSource;
|
|
1548
|
+
/** Interaction adapter (REST API) */
|
|
1549
|
+
interaction: IInteraction;
|
|
1550
|
+
/** Analytics adapter (REST API or no-op) */
|
|
1551
|
+
analytics: IAnalytics;
|
|
1552
|
+
/** Session storage adapter (localStorage) */
|
|
1553
|
+
storage: ISessionStorage;
|
|
1554
|
+
/** Network adapter (Navigator.connection) */
|
|
1555
|
+
network: INetworkAdapter;
|
|
1556
|
+
/** Video preloader (fetch + Cache API) */
|
|
1557
|
+
videoLoader: IVideoLoader;
|
|
1558
|
+
/** Poster preloader (Image) */
|
|
1559
|
+
posterLoader: IPosterLoader;
|
|
1560
|
+
/** Logger (passed through) */
|
|
1561
|
+
logger?: ILogger;
|
|
1562
|
+
}
|
|
1563
|
+
/**
|
|
1564
|
+
* Create full browser adapter set from config
|
|
1565
|
+
*
|
|
1566
|
+
* This is the recommended way to integrate SDK for web applications.
|
|
1567
|
+
* Creates all adapters with sensible defaults.
|
|
1568
|
+
*
|
|
1569
|
+
* @param config - Combined configuration
|
|
1570
|
+
* @returns Full adapter set for SDK
|
|
1571
|
+
*
|
|
1572
|
+
* @example
|
|
1573
|
+
* ```typescript
|
|
1574
|
+
* // Minimal config
|
|
1575
|
+
* const adapters = createBrowserAdapters({
|
|
1576
|
+
* baseUrl: 'https://api.myapp.com',
|
|
1577
|
+
* auth: { getAccessToken: () => localStorage.getItem('token') },
|
|
1578
|
+
* endpoints: {
|
|
1579
|
+
* feed: { list: '/videos', detail: '/videos/:id' },
|
|
1580
|
+
* interaction: {
|
|
1581
|
+
* like: '/videos/:id/like',
|
|
1582
|
+
* unlike: '/videos/:id/like',
|
|
1583
|
+
* follow: '/users/:id/follow',
|
|
1584
|
+
* unfollow: '/users/:id/follow',
|
|
1585
|
+
* comment: '/videos/:id/comments',
|
|
1586
|
+
* deleteComment: '/comments/:id',
|
|
1587
|
+
* },
|
|
1588
|
+
* },
|
|
1589
|
+
* });
|
|
1590
|
+
*
|
|
1591
|
+
* // Full config with all options
|
|
1592
|
+
* const adapters = createBrowserAdapters({
|
|
1593
|
+
* baseUrl: 'https://api.myapp.com',
|
|
1594
|
+
* auth: {
|
|
1595
|
+
* getAccessToken: () => localStorage.getItem('token'),
|
|
1596
|
+
* refreshToken: () => refreshService.refresh(),
|
|
1597
|
+
* onAuthError: () => router.push('/login'),
|
|
1598
|
+
* },
|
|
1599
|
+
* endpoints: { ... },
|
|
1600
|
+
* request: {
|
|
1601
|
+
* timeout: 15000,
|
|
1602
|
+
* retry: { maxRetries: 3 },
|
|
1603
|
+
* },
|
|
1604
|
+
* storage: {
|
|
1605
|
+
* prefix: 'myapp-',
|
|
1606
|
+
* maxSnapshotAge: 12 * 60 * 60 * 1000, // 12 hours
|
|
1607
|
+
* },
|
|
1608
|
+
* videoLoader: {
|
|
1609
|
+
* defaultMaxBytes: 1024 * 1024, // 1MB
|
|
1610
|
+
* },
|
|
1611
|
+
* logger: myLogger,
|
|
1612
|
+
* });
|
|
1613
|
+
* ```
|
|
1614
|
+
*/
|
|
1615
|
+
declare function createBrowserAdapters(config: BrowserAdaptersConfig): FullPresetAdapters;
|
|
1616
|
+
|
|
1617
|
+
/**
|
|
1618
|
+
* HTTP Client - Shared HTTP client với auth/retry
|
|
1619
|
+
*
|
|
1620
|
+
* Features:
|
|
1621
|
+
* - Auto auth token injection
|
|
1622
|
+
* - Token refresh on 401
|
|
1623
|
+
* - Retry with exponential backoff
|
|
1624
|
+
* - Request timeout
|
|
1625
|
+
* - Error normalization
|
|
1626
|
+
*/
|
|
1627
|
+
|
|
1628
|
+
/**
|
|
1629
|
+
* HTTP Client configuration
|
|
1630
|
+
*/
|
|
1631
|
+
interface HttpClientConfig {
|
|
1632
|
+
baseUrl: string;
|
|
1633
|
+
auth: AuthConfig;
|
|
1634
|
+
request?: RESTRequestConfig;
|
|
1635
|
+
logger?: ILogger;
|
|
1636
|
+
}
|
|
1637
|
+
/**
|
|
1638
|
+
* HTTP error with status code
|
|
1639
|
+
*/
|
|
1640
|
+
declare class HttpError extends Error {
|
|
1641
|
+
readonly status: number;
|
|
1642
|
+
readonly body?: unknown | undefined;
|
|
1643
|
+
constructor(status: number, message: string, body?: unknown | undefined);
|
|
1644
|
+
}
|
|
1645
|
+
/**
|
|
1646
|
+
* HTTP Client class
|
|
1647
|
+
*/
|
|
1648
|
+
declare class HttpClient {
|
|
1649
|
+
private readonly config;
|
|
1650
|
+
private readonly retryConfig;
|
|
1651
|
+
private readonly requestConfig;
|
|
1652
|
+
private isRefreshing;
|
|
1653
|
+
private refreshPromise;
|
|
1654
|
+
constructor(config: HttpClientConfig);
|
|
1655
|
+
/**
|
|
1656
|
+
* Make HTTP request with auth and retry
|
|
1657
|
+
*/
|
|
1658
|
+
request<T>(options: HttpRequestOptions): Promise<T>;
|
|
1659
|
+
/**
|
|
1660
|
+
* Build full URL from path and path params
|
|
1661
|
+
*/
|
|
1662
|
+
private buildUrl;
|
|
1663
|
+
/**
|
|
1664
|
+
* Append query params to URL
|
|
1665
|
+
*/
|
|
1666
|
+
private appendQueryParams;
|
|
1667
|
+
/**
|
|
1668
|
+
* Build headers with auth token
|
|
1669
|
+
*/
|
|
1670
|
+
private buildHeaders;
|
|
1671
|
+
/**
|
|
1672
|
+
* Execute request with retry logic
|
|
1673
|
+
*/
|
|
1674
|
+
private executeWithRetry;
|
|
1675
|
+
/**
|
|
1676
|
+
* Handle response status and parse result
|
|
1677
|
+
*/
|
|
1678
|
+
private handleResponse;
|
|
1679
|
+
/**
|
|
1680
|
+
* Handle 401 unauthorized - attempt token refresh
|
|
1681
|
+
*/
|
|
1682
|
+
private handleUnauthorized;
|
|
1683
|
+
/**
|
|
1684
|
+
* Update auth header with new token
|
|
1685
|
+
*/
|
|
1686
|
+
private updateAuthHeader;
|
|
1687
|
+
/**
|
|
1688
|
+
* Check if error should trigger retry
|
|
1689
|
+
*/
|
|
1690
|
+
private shouldRetry;
|
|
1691
|
+
/**
|
|
1692
|
+
* Wait before retry with backoff
|
|
1693
|
+
*/
|
|
1694
|
+
private waitBeforeRetry;
|
|
1695
|
+
/**
|
|
1696
|
+
* Execute fetch with timeout
|
|
1697
|
+
*/
|
|
1698
|
+
private doFetch;
|
|
1699
|
+
/**
|
|
1700
|
+
* Parse response JSON
|
|
1701
|
+
*/
|
|
1702
|
+
private parseResponse;
|
|
1703
|
+
/**
|
|
1704
|
+
* Safely parse JSON from response
|
|
1705
|
+
*/
|
|
1706
|
+
private safeParseJson;
|
|
1707
|
+
/**
|
|
1708
|
+
* Refresh token with deduplication
|
|
1709
|
+
*/
|
|
1710
|
+
private refreshTokenIfNeeded;
|
|
1711
|
+
/**
|
|
1712
|
+
* Sleep utility
|
|
1713
|
+
*/
|
|
1714
|
+
private sleep;
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
/**
|
|
1718
|
+
* Default Transforms - Auto-transform API responses to SDK format
|
|
1719
|
+
*
|
|
1720
|
+
* Default convention: snake_case API → camelCase SDK
|
|
1721
|
+
* Tries multiple common field names for flexibility
|
|
1722
|
+
*/
|
|
1723
|
+
|
|
1724
|
+
/**
|
|
1725
|
+
* Default video item transform
|
|
1726
|
+
*
|
|
1727
|
+
* Handles common API response formats:
|
|
1728
|
+
* - snake_case (Laravel, Django, Rails)
|
|
1729
|
+
* - camelCase
|
|
1730
|
+
* - Nested structures
|
|
1731
|
+
*/
|
|
1732
|
+
declare function defaultVideoItemTransform(apiResponse: unknown, fieldMap?: FieldMapConfig['video'], logger?: ILogger): VideoItem;
|
|
1733
|
+
/**
|
|
1734
|
+
* Default feed response transform
|
|
1735
|
+
*/
|
|
1736
|
+
declare function defaultFeedResponseTransform(apiResponse: unknown, fieldMap?: FieldMapConfig['feed'], logger?: ILogger): {
|
|
1737
|
+
items: unknown[];
|
|
1738
|
+
nextCursor: string | null;
|
|
1739
|
+
hasMore: boolean;
|
|
1740
|
+
};
|
|
1741
|
+
/**
|
|
1742
|
+
* Create transforms with custom overrides
|
|
1743
|
+
*/
|
|
1744
|
+
interface ResolvedTransforms {
|
|
1745
|
+
videoItem: (data: unknown) => VideoItem;
|
|
1746
|
+
feedResponse: (data: unknown) => {
|
|
1747
|
+
items: unknown[];
|
|
1748
|
+
nextCursor: string | null;
|
|
1749
|
+
hasMore: boolean;
|
|
1750
|
+
};
|
|
1751
|
+
}
|
|
1752
|
+
declare function createTransforms(config?: TransformConfig, logger?: ILogger): ResolvedTransforms;
|
|
1753
|
+
|
|
1754
|
+
/**
|
|
1755
|
+
* REST Data Adapter - IDataSource implementation for REST APIs
|
|
1756
|
+
*
|
|
1757
|
+
* Implements:
|
|
1758
|
+
* - fetchFeed: GET /videos with pagination
|
|
1759
|
+
* - getVideoDetail: GET /videos/:id
|
|
1760
|
+
* - prefetch: Optional prefetch support
|
|
1761
|
+
*/
|
|
1762
|
+
|
|
1763
|
+
/**
|
|
1764
|
+
* REST Data Adapter configuration
|
|
1765
|
+
*/
|
|
1766
|
+
interface RESTDataAdapterConfig {
|
|
1767
|
+
httpClient: HttpClient;
|
|
1768
|
+
endpoints: RESTEndpointMap['feed'];
|
|
1769
|
+
transforms: ResolvedTransforms;
|
|
1770
|
+
pagination?: RESTRequestConfig['pagination'];
|
|
1771
|
+
logger?: ILogger;
|
|
1772
|
+
}
|
|
1773
|
+
/**
|
|
1774
|
+
* REST Data Adapter
|
|
1775
|
+
*/
|
|
1776
|
+
declare class RESTDataAdapter implements IDataSource {
|
|
1777
|
+
private readonly httpClient;
|
|
1778
|
+
private readonly endpoints;
|
|
1779
|
+
private readonly transforms;
|
|
1780
|
+
private readonly pagination;
|
|
1781
|
+
private readonly logger?;
|
|
1782
|
+
constructor(config: RESTDataAdapterConfig);
|
|
1783
|
+
/**
|
|
1784
|
+
* Fetch feed with pagination
|
|
1785
|
+
*/
|
|
1786
|
+
fetchFeed(cursor?: string): Promise<FeedResponse>;
|
|
1787
|
+
/**
|
|
1788
|
+
* Get video detail by ID
|
|
1789
|
+
*/
|
|
1790
|
+
getVideoDetail(id: string): Promise<VideoItem>;
|
|
1791
|
+
/**
|
|
1792
|
+
* Optional: Prefetch videos
|
|
1793
|
+
* This is a no-op by default, can be overridden if API supports batch fetch
|
|
1794
|
+
*/
|
|
1795
|
+
prefetch(ids: string[]): Promise<void>;
|
|
1796
|
+
/**
|
|
1797
|
+
* Unwrap response if wrapped in data/result field
|
|
1798
|
+
*/
|
|
1799
|
+
private unwrapResponse;
|
|
1800
|
+
/**
|
|
1801
|
+
* Create fallback video item when transform fails
|
|
1802
|
+
*/
|
|
1803
|
+
private createFallbackVideoItem;
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
/**
|
|
1807
|
+
* REST Interaction Adapter - IInteraction implementation for REST APIs
|
|
1808
|
+
*
|
|
1809
|
+
* Implements all user interaction methods:
|
|
1810
|
+
* - like/unlike
|
|
1811
|
+
* - follow/unfollow
|
|
1812
|
+
* - comment/deleteComment
|
|
1813
|
+
* - share (optional)
|
|
1814
|
+
*/
|
|
1815
|
+
|
|
1816
|
+
/**
|
|
1817
|
+
* REST Interaction Adapter configuration
|
|
1818
|
+
*/
|
|
1819
|
+
interface RESTInteractionAdapterConfig {
|
|
1820
|
+
httpClient: HttpClient;
|
|
1821
|
+
endpoints: RESTEndpointMap['interaction'];
|
|
1822
|
+
logger?: ILogger;
|
|
1823
|
+
}
|
|
1824
|
+
/**
|
|
1825
|
+
* REST Interaction Adapter
|
|
1826
|
+
*/
|
|
1827
|
+
declare class RESTInteractionAdapter implements IInteraction {
|
|
1828
|
+
private readonly httpClient;
|
|
1829
|
+
private readonly endpoints;
|
|
1830
|
+
private readonly logger?;
|
|
1831
|
+
constructor(config: RESTInteractionAdapterConfig);
|
|
1832
|
+
/**
|
|
1833
|
+
* Like a video
|
|
1834
|
+
*/
|
|
1835
|
+
like(videoId: string): Promise<void>;
|
|
1836
|
+
/**
|
|
1837
|
+
* Unlike a video
|
|
1838
|
+
*/
|
|
1839
|
+
unlike(videoId: string): Promise<void>;
|
|
1840
|
+
/**
|
|
1841
|
+
* Follow an author
|
|
1842
|
+
*/
|
|
1843
|
+
follow(authorId: string): Promise<void>;
|
|
1844
|
+
/**
|
|
1845
|
+
* Unfollow an author
|
|
1846
|
+
*/
|
|
1847
|
+
unfollow(authorId: string): Promise<void>;
|
|
1848
|
+
/**
|
|
1849
|
+
* Post a comment
|
|
1850
|
+
*/
|
|
1851
|
+
comment(videoId: string, text: string): Promise<Comment>;
|
|
1852
|
+
/**
|
|
1853
|
+
* Delete a comment
|
|
1854
|
+
*/
|
|
1855
|
+
deleteComment(commentId: string): Promise<void>;
|
|
1856
|
+
/**
|
|
1857
|
+
* Like a comment
|
|
1858
|
+
*/
|
|
1859
|
+
likeComment(commentId: string): Promise<void>;
|
|
1860
|
+
/**
|
|
1861
|
+
* Unlike a comment
|
|
1862
|
+
*/
|
|
1863
|
+
unlikeComment(commentId: string): Promise<void>;
|
|
1864
|
+
/**
|
|
1865
|
+
* Share a video (optional tracking)
|
|
1866
|
+
*/
|
|
1867
|
+
share(videoId: string, platform?: string): Promise<void>;
|
|
1868
|
+
/**
|
|
1869
|
+
* Transform API comment response to Comment type
|
|
1870
|
+
*/
|
|
1871
|
+
private transformComment;
|
|
1872
|
+
/**
|
|
1873
|
+
* Unwrap response if wrapped
|
|
1874
|
+
*/
|
|
1875
|
+
private unwrapResponse;
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
/**
|
|
1879
|
+
* REST Analytics Adapter - IAnalytics implementation for REST APIs
|
|
1880
|
+
*
|
|
1881
|
+
* Implements batching strategy per ADD.md:
|
|
1882
|
+
* - Events queued internally
|
|
1883
|
+
* - Flush conditions: queue > 10, video change, visibility change
|
|
1884
|
+
* - Uses sendBeacon for reliability when available
|
|
1885
|
+
*/
|
|
1886
|
+
|
|
1887
|
+
/**
|
|
1888
|
+
* REST Analytics Adapter configuration
|
|
1889
|
+
*/
|
|
1890
|
+
interface RESTAnalyticsAdapterConfig {
|
|
1891
|
+
httpClient: HttpClient;
|
|
1892
|
+
endpoints: NonNullable<RESTEndpointMap['analytics']>;
|
|
1893
|
+
batchSize?: number;
|
|
1894
|
+
flushInterval?: number;
|
|
1895
|
+
logger?: ILogger;
|
|
1896
|
+
}
|
|
1897
|
+
/**
|
|
1898
|
+
* REST Analytics Adapter
|
|
1899
|
+
*/
|
|
1900
|
+
declare class RESTAnalyticsAdapter implements IAnalytics {
|
|
1901
|
+
private readonly httpClient;
|
|
1902
|
+
private readonly endpoint;
|
|
1903
|
+
private readonly batchSize;
|
|
1904
|
+
private readonly logger?;
|
|
1905
|
+
private queue;
|
|
1906
|
+
private flushTimer;
|
|
1907
|
+
private userId;
|
|
1908
|
+
private userProperties;
|
|
1909
|
+
constructor(config: RESTAnalyticsAdapterConfig);
|
|
1910
|
+
/**
|
|
1911
|
+
* Track an analytics event
|
|
1912
|
+
* Fire-and-forget - doesn't throw
|
|
1913
|
+
*/
|
|
1914
|
+
track(event: AnalyticsEvent): void;
|
|
1915
|
+
/**
|
|
1916
|
+
* Flush queued events
|
|
1917
|
+
*/
|
|
1918
|
+
flush(): Promise<void>;
|
|
1919
|
+
/**
|
|
1920
|
+
* Track video view duration (heartbeat)
|
|
1921
|
+
*/
|
|
1922
|
+
trackViewDuration(videoId: string, duration: number, totalDuration: number): void;
|
|
1923
|
+
/**
|
|
1924
|
+
* Track video completion
|
|
1925
|
+
*/
|
|
1926
|
+
trackCompletion(videoId: string, watchTime: number, loops: number): void;
|
|
1927
|
+
/**
|
|
1928
|
+
* Set user context
|
|
1929
|
+
*/
|
|
1930
|
+
setUser(userId: string | null, properties?: Record<string, unknown>): void;
|
|
1931
|
+
/**
|
|
1932
|
+
* Get current queue size (for debugging)
|
|
1933
|
+
*/
|
|
1934
|
+
getQueueSize(): number;
|
|
1935
|
+
/**
|
|
1936
|
+
* Cleanup - stop flush interval
|
|
1937
|
+
*/
|
|
1938
|
+
destroy(): void;
|
|
1939
|
+
/**
|
|
1940
|
+
* Try to send via sendBeacon (for reliability on page unload)
|
|
1941
|
+
*/
|
|
1942
|
+
private trySendBeacon;
|
|
1943
|
+
/**
|
|
1944
|
+
* Build full URL for sendBeacon
|
|
1945
|
+
*/
|
|
1946
|
+
private buildFullUrl;
|
|
1947
|
+
}
|
|
1948
|
+
/**
|
|
1949
|
+
* Create a no-op analytics adapter
|
|
1950
|
+
* Used when analytics endpoint is not configured
|
|
1951
|
+
*/
|
|
1952
|
+
declare function createNoOpAnalyticsAdapter(): IAnalytics;
|
|
1953
|
+
|
|
1954
|
+
export { type AuthConfig, type AuthError, type BrowserAdaptersConfig, BrowserPosterLoader, BrowserVideoLoader, type BrowserVideoLoaderConfig, DEFAULT_REQUEST_CONFIG, DEFAULT_RETRY_CONFIG, type FieldMapConfig, type FullPresetAdapters, HttpClient, HttpError, LocalSessionStorageAdapter, LocalStorageAdapter, type LocalStorageConfig, MockAnalyticsAdapter, type MockAnalyticsAdapterOptions, MockDataAdapter, type MockDataAdapterOptions, MockInteractionAdapter, type MockInteractionAdapterOptions, MockLoggerAdapter, type MockLoggerAdapterOptions, MockNetworkAdapter, type MockNetworkAdapterOptions, MockPosterLoader, MockSessionStorageAdapter, type MockSessionStorageAdapterOptions, MockStorageAdapter, type MockStorageAdapterOptions, MockVideoLoader, type MockVideoLoaderOptions, type PresetAdapters, RESTAnalyticsAdapter, RESTDataAdapter, type RESTEndpointMap, RESTInteractionAdapter, type RESTPresetConfig, type RESTRequestConfig, type ResolvedTransforms, type RetryConfig, type TransformConfig, WebNetworkAdapter, type WebNetworkConfig, createBrowserAdapters, createBrowserPosterLoader, createBrowserVideoLoader, createLocalStorageAdapter, createNoOpAnalyticsAdapter, createRESTAdapters, createSessionStorageAdapter, createTransforms, createWebNetworkAdapter, defaultFeedResponseTransform, defaultVideoItemTransform };
|