@xhub-short/adapters 0.1.0-beta.1 → 0.1.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +733 -9
  2. package/dist/index.js +1073 -26
  3. package/package.json +4 -3
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
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';
1
+ import { IDataSource, VideoItem, FeedResponse, ILogger, LogLevel, LogEntry, IStorage, ISessionStorage, SessionSnapshot, IInteraction, Comment, ICommentAdapter, MockCommentAdapterConfig, CommentListResponse, ReplyListResponse, PostCommentPayload, CommentItem, PostReplyPayload, ReplyItem, EditCommentPayload, DeleteCommentPayload, ReportCommentPayload, IAnalytics, AnalyticsEvent, INetworkAdapter, NetworkType, NetworkQuality, IVideoLoader, VideoSource, PreloadConfig, PreloadResult, PreloadStatus, IPosterLoader, ReportReason, CommentTransformers } from '@xhub-short/contracts';
2
2
 
3
3
  /**
4
4
  * MockDataAdapter - Development/Testing Data Source
@@ -446,11 +446,11 @@ declare class MockInteractionAdapter implements IInteraction {
446
446
  /**
447
447
  * Report a video
448
448
  *
449
- * @param _videoId - ID of the video to report
450
- * @param _reason - Report reason code
451
- * @param _description - Optional additional description
449
+ * @param videoId - ID of the video to report
450
+ * @param reason - Report reason code
451
+ * @param description - Optional additional description
452
452
  */
453
- report(_videoId: string, _reason: string, _description?: string): Promise<void>;
453
+ report(videoId: string, reason: string, description?: string): Promise<void>;
454
454
  /**
455
455
  * Check if video is liked (for testing)
456
456
  *
@@ -511,6 +511,48 @@ declare class MockInteractionAdapter implements IInteraction {
511
511
  private maybeThrowError;
512
512
  }
513
513
 
514
+ /**
515
+ * Mock Comment Adapter for testing and development
516
+ *
517
+ * Features:
518
+ * - Simulated network delay
519
+ * - Configurable error simulation
520
+ * - In-memory storage
521
+ */
522
+ declare class MockCommentAdapter implements ICommentAdapter {
523
+ private comments;
524
+ private replies;
525
+ private delay;
526
+ private simulateErrors;
527
+ private errorRate;
528
+ private idCounter;
529
+ constructor(config?: MockCommentAdapterConfig);
530
+ private simulateNetworkDelay;
531
+ private generateId;
532
+ getComments(videoId: string, cursor?: string | null, limit?: number): Promise<CommentListResponse>;
533
+ getReplies(commentId: string, cursor?: string | null, limit?: number): Promise<ReplyListResponse>;
534
+ postComment(payload: PostCommentPayload): Promise<CommentItem>;
535
+ postReply(payload: PostReplyPayload): Promise<ReplyItem>;
536
+ editComment(payload: EditCommentPayload): Promise<CommentItem | ReplyItem>;
537
+ deleteComment(payload: DeleteCommentPayload): Promise<void>;
538
+ likeComment(id: string): Promise<void>;
539
+ unlikeComment(id: string): Promise<void>;
540
+ reportComment(_payload: ReportCommentPayload): Promise<void>;
541
+ private updateLikeState;
542
+ /**
543
+ * Clear all stored data (for testing)
544
+ */
545
+ clearAll(): void;
546
+ /**
547
+ * Get stored comments count (for testing)
548
+ */
549
+ getCommentsCount(videoId: string): number;
550
+ /**
551
+ * Get stored replies count (for testing)
552
+ */
553
+ getRepliesCount(commentId: string): number;
554
+ }
555
+
514
556
  /**
515
557
  * Configuration options for MockAnalyticsAdapter
516
558
  */
@@ -948,15 +990,69 @@ interface RESTEndpointMap {
948
990
  deleteComment: string;
949
991
  /** POST /videos/:id/share (optional) */
950
992
  share?: string;
993
+ /** POST /content/:id/report (optional) */
994
+ report?: string;
995
+ /** GET /report-reasons (optional) */
996
+ reportReasons?: string;
997
+ /** POST /content/:id/not-interested (optional) */
998
+ notInterested?: string;
951
999
  };
952
1000
  /**
953
- * Analytics endpoints (optional)
954
- * If not provided, analytics adapter will be no-op
1001
+ * Analytics endpoints (optional) - BATCH mode
1002
+ * If provided, uses RESTAnalyticsAdapter with batch sending
955
1003
  */
956
1004
  analytics?: {
957
1005
  /** POST endpoint for batch events */
958
1006
  batch: string;
959
1007
  };
1008
+ /**
1009
+ * View tracking endpoint (optional) - PER-VIDEO mode
1010
+ * If provided, uses RESTViewTrackingAdapter with per-video tracking
1011
+ *
1012
+ * Priority: viewTracking > analytics (if both provided, viewTracking wins)
1013
+ *
1014
+ * @example
1015
+ * ```typescript
1016
+ * viewTracking: {
1017
+ * notify: '/reels/:id/notify-views',
1018
+ * }
1019
+ * ```
1020
+ */
1021
+ viewTracking?: {
1022
+ /**
1023
+ * POST endpoint for view notification
1024
+ * :id will be replaced with video ID
1025
+ */
1026
+ notify: string;
1027
+ /**
1028
+ * Path param name for video ID
1029
+ * @default 'id'
1030
+ */
1031
+ pathParam?: string;
1032
+ };
1033
+ /**
1034
+ * Comment endpoints (optional)
1035
+ * If not provided, comment adapter will not be created
1036
+ * Note: postReply uses same endpoint as post, with reply_to_comment_id in body
1037
+ */
1038
+ comment?: {
1039
+ /** GET /videos/:videoId/comments */
1040
+ list: string;
1041
+ /** GET /comments/:commentId/replies */
1042
+ replies: string;
1043
+ /** POST /videos/:videoId/comments (also for replies with reply_to_comment_id) */
1044
+ post: string;
1045
+ /** PATCH /comments/:id */
1046
+ edit: string;
1047
+ /** DELETE /comments/:id */
1048
+ delete: string;
1049
+ /** POST /comments/:id/like */
1050
+ like: string;
1051
+ /** DELETE /comments/:id/like */
1052
+ unlike: string;
1053
+ /** POST /comments/:id/report */
1054
+ report: string;
1055
+ };
960
1056
  }
961
1057
  /**
962
1058
  * HTTP request configuration
@@ -1010,6 +1106,14 @@ interface RetryConfig {
1010
1106
  */
1011
1107
  exponentialBackoff?: boolean;
1012
1108
  }
1109
+ /**
1110
+ * Report reason from API
1111
+ */
1112
+ interface ReportReasonItem {
1113
+ id: string;
1114
+ label: string;
1115
+ description?: string;
1116
+ }
1013
1117
  /**
1014
1118
  * Response transform configuration
1015
1119
  */
@@ -1028,6 +1132,41 @@ interface TransformConfig {
1028
1132
  nextCursor: string | null;
1029
1133
  hasMore: boolean;
1030
1134
  };
1135
+ /**
1136
+ * Transform report reasons response from API
1137
+ * If not provided, uses default transform
1138
+ *
1139
+ * @example
1140
+ * ```ts
1141
+ * reportReasons: (response) => {
1142
+ * const data = response.data?.reasons || [];
1143
+ * return data.map(item => ({
1144
+ * id: item.id,
1145
+ * label: item.title,
1146
+ * description: item.description,
1147
+ * }));
1148
+ * }
1149
+ * ```
1150
+ */
1151
+ reportReasons?: (apiResponse: unknown) => ReportReasonItem[];
1152
+ /**
1153
+ * Transform report request body before sending to API
1154
+ * If not provided, uses default format: { reason, description }
1155
+ *
1156
+ * @example
1157
+ * ```ts
1158
+ * reportBody: ({ contentId, reasonId, description }) => ({
1159
+ * video_id: contentId,
1160
+ * reason_id: reasonId,
1161
+ * description: description || '',
1162
+ * })
1163
+ * ```
1164
+ */
1165
+ reportBody?: (input: {
1166
+ contentId: string;
1167
+ reasonId: string;
1168
+ description?: string;
1169
+ }) => Record<string, unknown>;
1031
1170
  /**
1032
1171
  * Field mapping for default transforms
1033
1172
  * Used when API field names differ from defaults
@@ -1053,6 +1192,225 @@ interface FieldMapConfig {
1053
1192
  hasMore?: string;
1054
1193
  };
1055
1194
  }
1195
+ /**
1196
+ * Event types for batch analytics
1197
+ * Maps SDK internal types to API event types
1198
+ */
1199
+ type BatchAnalyticsEventType = 'view_start' | 'view_end' | 'scroll_pass' | 'like' | 'comment' | 'share' | 'follow_creator' | 'save' | 'report';
1200
+ /**
1201
+ * Device type for analytics context
1202
+ */
1203
+ type BatchAnalyticsDeviceType = 'ios' | 'android' | 'web';
1204
+ /**
1205
+ * Network type for analytics context
1206
+ */
1207
+ type BatchAnalyticsNetworkType = 'wifi' | '4g' | '5g' | 'other';
1208
+ /**
1209
+ * SDK internal analytics event data
1210
+ * Passed to transform function
1211
+ */
1212
+ interface BatchAnalyticsEventData {
1213
+ /** SDK event type (will be mapped to API event type) */
1214
+ sdkEventType: string;
1215
+ /** Video ID */
1216
+ videoId?: string;
1217
+ /** Event timestamp */
1218
+ timestamp: number;
1219
+ /** User ID (from setUser) */
1220
+ userId: string | null;
1221
+ /** User properties (from setUser) */
1222
+ userProperties: Record<string, unknown>;
1223
+ /** Additional event data from SDK */
1224
+ data?: Record<string, unknown>;
1225
+ }
1226
+ /**
1227
+ * Context data for analytics events
1228
+ * Provided by Host App via config
1229
+ */
1230
+ interface BatchAnalyticsContext {
1231
+ /** Get current session ID */
1232
+ getSessionId: () => string | null | Promise<string | null>;
1233
+ /** Get device type */
1234
+ getDeviceType: () => BatchAnalyticsDeviceType;
1235
+ /** Get network type */
1236
+ getNetworkType: () => BatchAnalyticsNetworkType | Promise<BatchAnalyticsNetworkType>;
1237
+ /** Get geo location (optional) */
1238
+ getGeoLocation?: () => string | null | Promise<string | null>;
1239
+ /** Get current video position index in feed */
1240
+ getPositionIndex?: () => number;
1241
+ /** Get scroll speed in ms (optional) */
1242
+ getScrollSpeed?: () => number | null;
1243
+ }
1244
+ /**
1245
+ * Single event in batch request body (after transform)
1246
+ */
1247
+ type BatchAnalyticsRequestEvent = Record<string, unknown>;
1248
+ /**
1249
+ * Full request body for batch analytics
1250
+ */
1251
+ interface BatchAnalyticsRequestBody {
1252
+ events: BatchAnalyticsRequestEvent[];
1253
+ }
1254
+ /**
1255
+ * Transform function to convert SDK event to API event format
1256
+ *
1257
+ * @example
1258
+ * ```typescript
1259
+ * const transform: BatchAnalyticsEventTransformer = async (event, context) => ({
1260
+ * event_id: crypto.randomUUID(),
1261
+ * user_id: event.userId,
1262
+ * video_id: event.videoId,
1263
+ * session_id: await context.getSessionId(),
1264
+ * event_type: mapEventType(event.sdkEventType),
1265
+ * position_index: context.getPositionIndex?.() ?? 0,
1266
+ * impression_timestamp: new Date(event.timestamp).toISOString(),
1267
+ * scroll_speed_ms: context.getScrollSpeed?.() ?? null,
1268
+ * device_type: context.getDeviceType(),
1269
+ * network_type: await context.getNetworkType(),
1270
+ * geo_location: await context.getGeoLocation?.() ?? null,
1271
+ * time_of_day: new Date(event.timestamp).getHours(),
1272
+ * day_of_week: new Date(event.timestamp).getDay(),
1273
+ * });
1274
+ * ```
1275
+ */
1276
+ type BatchAnalyticsEventTransformer = (event: BatchAnalyticsEventData, context: BatchAnalyticsContext) => BatchAnalyticsRequestEvent | Promise<BatchAnalyticsRequestEvent>;
1277
+ /**
1278
+ * Batch analytics configuration for preset
1279
+ *
1280
+ * @example
1281
+ * ```typescript
1282
+ * const batchAnalytics: BatchAnalyticsConfig = {
1283
+ * batchSize: 10,
1284
+ * flushInterval: 30000,
1285
+ * context: {
1286
+ * getSessionId: () => sessionStorage.getItem('session_id'),
1287
+ * getDeviceType: () => 'web',
1288
+ * getNetworkType: () => navigator.connection?.effectiveType === '4g' ? '4g' : 'wifi',
1289
+ * getGeoLocation: () => 'Ho Chi Minh City, Vietnam',
1290
+ * getPositionIndex: () => currentIndex,
1291
+ * },
1292
+ * transform: async (event, ctx) => ({
1293
+ * event_id: crypto.randomUUID(),
1294
+ * user_id: event.userId,
1295
+ * video_id: event.videoId,
1296
+ * session_id: await ctx.getSessionId(),
1297
+ * event_type: event.sdkEventType,
1298
+ * // ... more fields
1299
+ * }),
1300
+ * };
1301
+ * ```
1302
+ */
1303
+ interface BatchAnalyticsConfig {
1304
+ /**
1305
+ * Batch size threshold for auto-flush
1306
+ * @default 10
1307
+ */
1308
+ batchSize?: number;
1309
+ /**
1310
+ * Flush interval in milliseconds
1311
+ * @default 30000 (30 seconds)
1312
+ */
1313
+ flushInterval?: number;
1314
+ /**
1315
+ * Context providers for enriching events
1316
+ * Required if using custom transform
1317
+ */
1318
+ context?: BatchAnalyticsContext;
1319
+ /**
1320
+ * Custom transform function
1321
+ * If not provided, uses default SDK format
1322
+ */
1323
+ transform?: BatchAnalyticsEventTransformer;
1324
+ /**
1325
+ * Map SDK event types to API event types
1326
+ * Used by default transform if no custom transform provided
1327
+ *
1328
+ * @example
1329
+ * ```typescript
1330
+ * eventTypeMap: {
1331
+ * 'video_view': 'view_start',
1332
+ * 'video_complete': 'view_end',
1333
+ * 'scroll': 'scroll_pass',
1334
+ * 'like': 'like',
1335
+ * 'follow': 'follow_creator',
1336
+ * }
1337
+ * ```
1338
+ */
1339
+ eventTypeMap?: Record<string, BatchAnalyticsEventType>;
1340
+ /**
1341
+ * Use sendBeacon API for flushing events
1342
+ *
1343
+ * sendBeacon is more reliable for page unload events but shows as "ping" in Network tab.
1344
+ * Set to false to always use HTTP POST (easier for debugging).
1345
+ *
1346
+ * @default true
1347
+ */
1348
+ useSendBeacon?: boolean;
1349
+ }
1350
+ /**
1351
+ * View event data passed to transformer
1352
+ */
1353
+ interface ViewEventData {
1354
+ /** Video/Reel ID */
1355
+ videoId: string;
1356
+ /** Current playback time in seconds */
1357
+ currentTime: number;
1358
+ /** Total video duration in seconds */
1359
+ duration: number;
1360
+ /** Internal event type (for SDK logic) */
1361
+ internalEventType: 'play' | 'heartbeat' | 'complete' | 'leave';
1362
+ /** Watch time so far (for completion events) */
1363
+ watchTime?: number;
1364
+ /** Loop count (for completion events) */
1365
+ loopCount?: number;
1366
+ /** Timestamp */
1367
+ timestamp: number;
1368
+ }
1369
+ /**
1370
+ * Request body after transformation
1371
+ * Allows custom field names via fieldMap
1372
+ */
1373
+ type ViewEventRequestBody = Record<string, unknown>;
1374
+ /**
1375
+ * Transform function to convert SDK data to API request body
1376
+ */
1377
+ type ViewEventTransformer = (data: ViewEventData) => ViewEventRequestBody;
1378
+ /**
1379
+ * View tracking configuration for preset
1380
+ */
1381
+ interface ViewTrackingConfig {
1382
+ /**
1383
+ * Value for view_event field
1384
+ * @default 'seek'
1385
+ */
1386
+ viewEventValue?: string;
1387
+ /**
1388
+ * Heartbeat interval in milliseconds
1389
+ * @default 10000 (10 seconds)
1390
+ */
1391
+ heartbeatInterval?: number;
1392
+ /**
1393
+ * Minimum watch time (seconds) before sending first event
1394
+ * @default 0
1395
+ */
1396
+ minWatchTime?: number;
1397
+ /**
1398
+ * Track when user scrolls away from video
1399
+ * @default true
1400
+ */
1401
+ trackLeave?: boolean;
1402
+ /**
1403
+ * Custom request body transformer
1404
+ */
1405
+ transform?: ViewEventTransformer;
1406
+ /**
1407
+ * Field mapping for default transformer
1408
+ */
1409
+ fieldMap?: {
1410
+ currentTime?: string;
1411
+ viewEvent?: string;
1412
+ };
1413
+ }
1056
1414
  /**
1057
1415
  * REST Preset Adapter configuration
1058
1416
  */
@@ -1079,6 +1437,35 @@ interface RESTPresetConfig {
1079
1437
  * Request configuration (optional)
1080
1438
  */
1081
1439
  request?: RESTRequestConfig;
1440
+ /**
1441
+ * View tracking configuration (optional)
1442
+ * Only used if endpoints.viewTracking is configured
1443
+ */
1444
+ viewTracking?: ViewTrackingConfig;
1445
+ /**
1446
+ * Batch analytics configuration (optional)
1447
+ * Only used if endpoints.analytics is configured
1448
+ *
1449
+ * @example
1450
+ * ```typescript
1451
+ * batchAnalytics: {
1452
+ * batchSize: 10,
1453
+ * flushInterval: 30000,
1454
+ * context: {
1455
+ * getSessionId: () => sessionStorage.getItem('session_id'),
1456
+ * getDeviceType: () => 'web',
1457
+ * getNetworkType: () => 'wifi',
1458
+ * },
1459
+ * transform: (event, ctx) => ({
1460
+ * event_id: crypto.randomUUID(),
1461
+ * user_id: event.userId,
1462
+ * video_id: event.videoId,
1463
+ * // ... custom format
1464
+ * }),
1465
+ * }
1466
+ * ```
1467
+ */
1468
+ batchAnalytics?: BatchAnalyticsConfig;
1082
1469
  /**
1083
1470
  * Logger adapter for error/warning logging
1084
1471
  * If not provided, uses console in dev, silent in prod
@@ -1092,6 +1479,8 @@ interface PresetAdapters {
1092
1479
  dataSource: IDataSource;
1093
1480
  interaction: IInteraction;
1094
1481
  analytics: IAnalytics;
1482
+ /** Comment adapter (only if comment endpoints are configured) */
1483
+ comment?: ICommentAdapter;
1095
1484
  }
1096
1485
  /**
1097
1486
  * HTTP request options (internal)
@@ -1557,6 +1946,8 @@ interface FullPresetAdapters {
1557
1946
  videoLoader: IVideoLoader;
1558
1947
  /** Poster preloader (Image) */
1559
1948
  posterLoader: IPosterLoader;
1949
+ /** Comment adapter (REST API, only if comment endpoints configured) */
1950
+ comment?: ICommentAdapter;
1560
1951
  /** Logger (passed through) */
1561
1952
  logger?: ILogger;
1562
1953
  }
@@ -1649,6 +2040,10 @@ declare class HttpClient {
1649
2040
  private readonly config;
1650
2041
  private readonly retryConfig;
1651
2042
  private readonly requestConfig;
2043
+ /**
2044
+ * Get base URL (for sendBeacon which needs full URL)
2045
+ */
2046
+ getBaseUrl(): string;
1652
2047
  private isRefreshing;
1653
2048
  private refreshPromise;
1654
2049
  constructor(config: HttpClientConfig);
@@ -1813,6 +2208,14 @@ declare class RESTDataAdapter implements IDataSource {
1813
2208
  * - share (optional)
1814
2209
  */
1815
2210
 
2211
+ /**
2212
+ * Report body input from SDK
2213
+ */
2214
+ interface ReportBodyInput {
2215
+ contentId: string;
2216
+ reasonId: string;
2217
+ description?: string;
2218
+ }
1816
2219
  /**
1817
2220
  * REST Interaction Adapter configuration
1818
2221
  */
@@ -1820,6 +2223,10 @@ interface RESTInteractionAdapterConfig {
1820
2223
  httpClient: HttpClient;
1821
2224
  endpoints: RESTEndpointMap['interaction'];
1822
2225
  logger?: ILogger;
2226
+ /** Custom transform for report reasons response */
2227
+ transformReportReasons?: (apiResponse: unknown) => ReportReasonItem[];
2228
+ /** Custom transform for report request body */
2229
+ transformReportBody?: (input: ReportBodyInput) => Record<string, unknown>;
1823
2230
  }
1824
2231
  /**
1825
2232
  * REST Interaction Adapter
@@ -1828,6 +2235,8 @@ declare class RESTInteractionAdapter implements IInteraction {
1828
2235
  private readonly httpClient;
1829
2236
  private readonly endpoints;
1830
2237
  private readonly logger?;
2238
+ private readonly customTransformReportReasons?;
2239
+ private readonly customTransformReportBody?;
1831
2240
  constructor(config: RESTInteractionAdapterConfig);
1832
2241
  /**
1833
2242
  * Like a video
@@ -1865,6 +2274,38 @@ declare class RESTInteractionAdapter implements IInteraction {
1865
2274
  * Share a video (optional tracking)
1866
2275
  */
1867
2276
  share(videoId: string, platform?: string): Promise<void>;
2277
+ /**
2278
+ * Report content (video or image post)
2279
+ *
2280
+ * @param contentId - ID of the content to report
2281
+ * @param reason - Report reason code/ID
2282
+ * @param description - Optional additional description
2283
+ */
2284
+ report(contentId: string, reason: string, description?: string): Promise<void>;
2285
+ /**
2286
+ * Get available report reasons
2287
+ *
2288
+ * @returns Array of report reasons, or empty array if not configured
2289
+ */
2290
+ getReportReasons(): Promise<ReportReason[]>;
2291
+ /**
2292
+ * Mark content as "not interested"
2293
+ *
2294
+ * Used for recommendation algorithm feedback.
2295
+ * Content should be hidden from feed after this action.
2296
+ *
2297
+ * @param contentId - ID of the content (video or image post)
2298
+ */
2299
+ notInterested(contentId: string): Promise<void>;
2300
+ /**
2301
+ * Default transform for API report reasons response to ReportReason[]
2302
+ *
2303
+ * Expected format: [{ id, label, description }]
2304
+ * Or wrapped: { data: [{ id, label, description }] }
2305
+ *
2306
+ * For custom API formats, use `transforms.reportReasons` in preset config.
2307
+ */
2308
+ private transformReportReasons;
1868
2309
  /**
1869
2310
  * Transform API comment response to Comment type
1870
2311
  */
@@ -1882,6 +2323,34 @@ declare class RESTInteractionAdapter implements IInteraction {
1882
2323
  * - Events queued internally
1883
2324
  * - Flush conditions: queue > 10, video change, visibility change
1884
2325
  * - Uses sendBeacon for reliability when available
2326
+ *
2327
+ * Supports custom transform for flexible API formats:
2328
+ * - Default: SDK format { events: [...] }
2329
+ * - Custom: Any format via transform function
2330
+ *
2331
+ * @example
2332
+ * ```typescript
2333
+ * // With custom transform for specific API format
2334
+ * const adapter = new RESTAnalyticsAdapter({
2335
+ * httpClient,
2336
+ * endpoints: { batch: '/api/v1/events/track-batch' },
2337
+ * config: {
2338
+ * context: {
2339
+ * getSessionId: () => sessionStorage.getItem('session_id'),
2340
+ * getDeviceType: () => 'web',
2341
+ * getNetworkType: () => 'wifi',
2342
+ * },
2343
+ * transform: async (event, ctx) => ({
2344
+ * event_id: crypto.randomUUID(),
2345
+ * user_id: event.userId,
2346
+ * video_id: event.videoId,
2347
+ * session_id: await ctx.getSessionId(),
2348
+ * event_type: event.sdkEventType,
2349
+ * // ... more fields
2350
+ * }),
2351
+ * },
2352
+ * });
2353
+ * ```
1885
2354
  */
1886
2355
 
1887
2356
  /**
@@ -1890,22 +2359,35 @@ declare class RESTInteractionAdapter implements IInteraction {
1890
2359
  interface RESTAnalyticsAdapterConfig {
1891
2360
  httpClient: HttpClient;
1892
2361
  endpoints: NonNullable<RESTEndpointMap['analytics']>;
2362
+ /** Batch analytics config with transform and context */
2363
+ config?: BatchAnalyticsConfig;
2364
+ /** @deprecated Use config.batchSize instead */
1893
2365
  batchSize?: number;
2366
+ /** @deprecated Use config.flushInterval instead */
1894
2367
  flushInterval?: number;
1895
2368
  logger?: ILogger;
1896
2369
  }
1897
2370
  /**
1898
2371
  * REST Analytics Adapter
2372
+ *
2373
+ * Supports two modes:
2374
+ * 1. Default mode: Uses SDK format { events: [...] }
2375
+ * 2. Custom mode: Uses transform function for custom API format
1899
2376
  */
1900
2377
  declare class RESTAnalyticsAdapter implements IAnalytics {
1901
2378
  private readonly httpClient;
1902
2379
  private readonly endpoint;
1903
2380
  private readonly batchSize;
1904
2381
  private readonly logger?;
2382
+ private readonly transform?;
2383
+ private readonly context;
2384
+ private readonly eventTypeMap;
2385
+ private readonly useSendBeacon;
1905
2386
  private queue;
1906
2387
  private flushTimer;
1907
2388
  private userId;
1908
2389
  private userProperties;
2390
+ private viewedVideos;
1909
2391
  constructor(config: RESTAnalyticsAdapterConfig);
1910
2392
  /**
1911
2393
  * Track an analytics event
@@ -1917,9 +2399,23 @@ declare class RESTAnalyticsAdapter implements IAnalytics {
1917
2399
  */
1918
2400
  flush(): Promise<void>;
1919
2401
  /**
1920
- * Track video view duration (heartbeat)
2402
+ * Build request body from events
2403
+ * Uses custom transform if provided, otherwise SDK default format
2404
+ */
2405
+ private buildRequestBody;
2406
+ /**
2407
+ * Track video view duration
2408
+ *
2409
+ * Note: PlayerEngine calls this every second (heartbeat), but for batch analytics
2410
+ * we only need ONE view_start per video. This method deduplicates by videoId.
2411
+ *
2412
+ * If you need heartbeat tracking, use RESTViewTrackingAdapter instead.
1921
2413
  */
1922
2414
  trackViewDuration(videoId: string, duration: number, totalDuration: number): void;
2415
+ /**
2416
+ * Clear viewed videos cache (useful for testing or session reset)
2417
+ */
2418
+ clearViewedVideos(): void;
1923
2419
  /**
1924
2420
  * Track video completion
1925
2421
  */
@@ -1938,12 +2434,23 @@ declare class RESTAnalyticsAdapter implements IAnalytics {
1938
2434
  destroy(): void;
1939
2435
  /**
1940
2436
  * Try to send via sendBeacon (for reliability on page unload)
2437
+ * Note: sendBeacon shows as "ping" type in Network tab, not "POST"
2438
+ *
2439
+ * Set useSendBeacon: false in config to always use HTTP POST
1941
2440
  */
1942
2441
  private trySendBeacon;
1943
2442
  /**
1944
2443
  * Build full URL for sendBeacon
1945
2444
  */
1946
2445
  private buildFullUrl;
2446
+ /**
2447
+ * Get mapped API event type from SDK event type
2448
+ */
2449
+ getApiEventType(sdkEventType: string): BatchAnalyticsEventType;
2450
+ /**
2451
+ * Get current context (for debugging/testing)
2452
+ */
2453
+ getContext(): BatchAnalyticsContext;
1947
2454
  }
1948
2455
  /**
1949
2456
  * Create a no-op analytics adapter
@@ -1951,4 +2458,221 @@ declare class RESTAnalyticsAdapter implements IAnalytics {
1951
2458
  */
1952
2459
  declare function createNoOpAnalyticsAdapter(): IAnalytics;
1953
2460
 
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 };
2461
+ /**
2462
+ * REST View Tracking Adapter - Per-video view tracking via REST API
2463
+ *
2464
+ * Implements IAnalytics interface with throttled, fire-and-forget tracking.
2465
+ * Uses shared HttpClient from preset (auth, retry, timeout).
2466
+ *
2467
+ * @example
2468
+ * ```typescript
2469
+ * // Created automatically by createRESTAdapters when viewTracking is configured
2470
+ * const adapters = createRESTAdapters({
2471
+ * baseUrl: 'https://api.myapp.com/v1',
2472
+ * auth: { ... },
2473
+ * endpoints: {
2474
+ * feed: { ... },
2475
+ * interaction: { ... },
2476
+ * viewTracking: {
2477
+ * notify: '/reels/:id/notify-views',
2478
+ * },
2479
+ * },
2480
+ * });
2481
+ * ```
2482
+ */
2483
+
2484
+ /**
2485
+ * REST View Tracking Adapter configuration
2486
+ */
2487
+ interface RESTViewTrackingAdapterConfig {
2488
+ httpClient: HttpClient;
2489
+ endpoint: NonNullable<RESTEndpointMap['viewTracking']>;
2490
+ config?: ViewTrackingConfig;
2491
+ logger?: ILogger;
2492
+ }
2493
+ /**
2494
+ * RESTViewTrackingAdapter - Per-video view tracking via REST API
2495
+ *
2496
+ * Uses shared HttpClient (inherits auth, retry, timeout from preset).
2497
+ * Implements throttled, fire-and-forget tracking.
2498
+ */
2499
+ declare class RESTViewTrackingAdapter implements IAnalytics {
2500
+ private readonly httpClient;
2501
+ private readonly endpoint;
2502
+ private readonly transform;
2503
+ private readonly viewEventValue;
2504
+ private readonly heartbeatInterval;
2505
+ private readonly minWatchTime;
2506
+ private readonly trackLeaveEnabled;
2507
+ private readonly logger?;
2508
+ private lastTrackTime;
2509
+ private lastEventType;
2510
+ private _userId;
2511
+ private _userProperties;
2512
+ /** Get current user ID */
2513
+ get userId(): string | null;
2514
+ /** Get current user properties */
2515
+ get userProperties(): Record<string, unknown>;
2516
+ constructor(config: RESTViewTrackingAdapterConfig);
2517
+ /**
2518
+ * Track generic event
2519
+ * Delegates video events to specific handlers
2520
+ */
2521
+ track(event: AnalyticsEvent): void;
2522
+ /**
2523
+ * Flush - No-op since we send events immediately (fire-and-forget)
2524
+ */
2525
+ flush(): Promise<void>;
2526
+ /**
2527
+ * Track view duration (heartbeat)
2528
+ * Called periodically during video playback
2529
+ */
2530
+ trackViewDuration(videoId: string, duration: number, totalDuration: number): void;
2531
+ /**
2532
+ * Track video completion
2533
+ * Called when video ends or loops
2534
+ */
2535
+ trackCompletion(videoId: string, watchTime: number, loopCount: number): void;
2536
+ /**
2537
+ * Set user context
2538
+ */
2539
+ setUser(userId: string | null, properties?: Record<string, unknown>): void;
2540
+ /**
2541
+ * Get queue size (always 0 - no batching)
2542
+ */
2543
+ getQueueSize(): number;
2544
+ /**
2545
+ * Track when user leaves video
2546
+ * Called when user scrolls to another video
2547
+ */
2548
+ trackLeaveVideo(videoId: string, currentTime: number, duration: number): void;
2549
+ /**
2550
+ * Clear tracking state for a video
2551
+ * Call when video is removed from viewport
2552
+ */
2553
+ clearVideoState(videoId: string): void;
2554
+ /**
2555
+ * Clear all tracking state
2556
+ */
2557
+ reset(): void;
2558
+ /**
2559
+ * Send view event to API (fire-and-forget)
2560
+ */
2561
+ private sendViewEvent;
2562
+ }
2563
+
2564
+ /**
2565
+ * REST Comment Adapter - ICommentAdapter implementation for REST APIs
2566
+ *
2567
+ * Implements all comment operations:
2568
+ * - getComments / getReplies
2569
+ * - postComment / postReply
2570
+ * - editComment / deleteComment
2571
+ * - likeComment / unlikeComment
2572
+ * - reportComment
2573
+ */
2574
+
2575
+ /**
2576
+ * Comment endpoints configuration (required, not optional)
2577
+ */
2578
+ interface CommentEndpointsConfig {
2579
+ /** GET /videos/:videoId/comments */
2580
+ list: string;
2581
+ /** GET /comments/:commentId/replies */
2582
+ replies: string;
2583
+ /** POST /videos/:videoId/comments */
2584
+ post: string;
2585
+ /** PATCH /comments/:id */
2586
+ edit: string;
2587
+ /** DELETE /comments/:id */
2588
+ delete: string;
2589
+ /** POST /comments/:id/like */
2590
+ like: string;
2591
+ /** DELETE /comments/:id/like */
2592
+ unlike: string;
2593
+ /** POST /comments/:id/report */
2594
+ report: string;
2595
+ }
2596
+ /**
2597
+ * REST Comment Adapter configuration
2598
+ */
2599
+ interface RESTCommentAdapterConfig {
2600
+ httpClient: HttpClient;
2601
+ endpoints: CommentEndpointsConfig;
2602
+ transformers?: CommentTransformers;
2603
+ logger?: ILogger;
2604
+ }
2605
+ /**
2606
+ * REST Comment Adapter
2607
+ */
2608
+ declare class RESTCommentAdapter implements ICommentAdapter {
2609
+ private readonly httpClient;
2610
+ private readonly endpoints;
2611
+ private readonly transformers?;
2612
+ private readonly logger?;
2613
+ constructor(config: RESTCommentAdapterConfig);
2614
+ /**
2615
+ * Get comments for a video
2616
+ */
2617
+ getComments(videoId: string, cursor?: string | null, limit?: number): Promise<CommentListResponse>;
2618
+ /**
2619
+ * Get replies for a comment
2620
+ */
2621
+ getReplies(commentId: string, cursor?: string | null, limit?: number): Promise<ReplyListResponse>;
2622
+ /**
2623
+ * Post a new comment
2624
+ */
2625
+ postComment(payload: PostCommentPayload): Promise<CommentItem>;
2626
+ /**
2627
+ * Post a reply to a comment
2628
+ * Uses same endpoint as postComment but with reply_to_comment_id
2629
+ */
2630
+ postReply(payload: PostReplyPayload): Promise<ReplyItem>;
2631
+ /**
2632
+ * Edit a comment or reply
2633
+ */
2634
+ editComment(payload: EditCommentPayload): Promise<CommentItem | ReplyItem>;
2635
+ /**
2636
+ * Delete a comment or reply
2637
+ * DELETE /reels/:id/comments/:commentId
2638
+ */
2639
+ deleteComment(payload: DeleteCommentPayload): Promise<void>;
2640
+ /**
2641
+ * Like a comment or reply
2642
+ */
2643
+ likeComment(id: string): Promise<void>;
2644
+ /**
2645
+ * Unlike a comment or reply
2646
+ */
2647
+ unlikeComment(id: string): Promise<void>;
2648
+ /**
2649
+ * Report a comment or reply
2650
+ */
2651
+ reportComment(payload: ReportCommentPayload): Promise<void>;
2652
+ /**
2653
+ * Transform API response to CommentListResponse
2654
+ */
2655
+ private transformCommentListResponse;
2656
+ /**
2657
+ * Transform API response to ReplyListResponse
2658
+ */
2659
+ private transformReplyListResponse;
2660
+ /**
2661
+ * Transform API response to CommentItem
2662
+ */
2663
+ private transformComment;
2664
+ /**
2665
+ * Transform API response to ReplyItem
2666
+ */
2667
+ private transformReply;
2668
+ /**
2669
+ * Extract author from various API formats
2670
+ */
2671
+ private extractAuthor;
2672
+ /**
2673
+ * Unwrap nested response data
2674
+ */
2675
+ private unwrapResponse;
2676
+ }
2677
+
2678
+ export { type AuthConfig, type AuthError, type BatchAnalyticsConfig, type BatchAnalyticsContext, type BatchAnalyticsDeviceType, type BatchAnalyticsEventData, type BatchAnalyticsEventTransformer, type BatchAnalyticsEventType, type BatchAnalyticsNetworkType, type BatchAnalyticsRequestBody, type BatchAnalyticsRequestEvent, 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, MockCommentAdapter, 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, type RESTAnalyticsAdapterConfig, RESTCommentAdapter, type RESTCommentAdapterConfig, RESTDataAdapter, type RESTEndpointMap, RESTInteractionAdapter, type RESTPresetConfig, type RESTRequestConfig, RESTViewTrackingAdapter, type RESTViewTrackingAdapterConfig, type ResolvedTransforms, type RetryConfig, type TransformConfig, type ViewEventData, type ViewEventRequestBody, type ViewEventTransformer, type ViewTrackingConfig, WebNetworkAdapter, type WebNetworkConfig, createBrowserAdapters, createBrowserPosterLoader, createBrowserVideoLoader, createLocalStorageAdapter, createNoOpAnalyticsAdapter, createRESTAdapters, createSessionStorageAdapter, createTransforms, createWebNetworkAdapter, defaultFeedResponseTransform, defaultVideoItemTransform };