@boldvideo/bold-js 1.15.2 → 1.17.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/CHANGELOG.md CHANGED
@@ -1,5 +1,51 @@
1
1
  # @boldvideo/bold-js
2
2
 
3
+ ## 1.17.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 563730d: Add full options support to `videos.list()` for tag, collectionId, viewerId, and page filtering
8
+
9
+ **New features:**
10
+
11
+ - Filter videos by `tag` and `collectionId` on both endpoints
12
+ - Include watch progress with `viewerId` parameter
13
+ - Access paginated `/videos` endpoint using `page` parameter
14
+
15
+ **Usage:**
16
+
17
+ ```typescript
18
+ // Latest videos with filters
19
+ await bold.videos.list({ limit: 20, tag: "sales", collectionId: "col_123" });
20
+
21
+ // Include viewer watch progress
22
+ await bold.videos.list({ limit: 20, viewerId: "viewer_123" });
23
+
24
+ // Paginated index (uses /videos endpoint)
25
+ await bold.videos.list({ page: 2, tag: "sales" });
26
+ ```
27
+
28
+ **Breaking:** None. Existing `bold.videos.list()` and `bold.videos.list(12)` calls work unchanged.
29
+
30
+ ## 1.16.0
31
+
32
+ ### Minor Changes
33
+
34
+ - 06c8abc: Add Viewers API for managing external users and tracking video progress
35
+
36
+ New methods on the `viewers` namespace:
37
+
38
+ - `viewers.list()` - List all viewers
39
+ - `viewers.get(id)` - Get a viewer by ID
40
+ - `viewers.lookup({ externalId } | { email })` - Find viewer by external ID or email
41
+ - `viewers.create(data)` - Create a new viewer
42
+ - `viewers.update(id, data)` - Update a viewer
43
+ - `viewers.listProgress(viewerId, options?)` - List progress for a viewer
44
+ - `viewers.getProgress(viewerId, videoId)` - Get progress for a video
45
+ - `viewers.saveProgress(viewerId, videoId, data)` - Save/update progress
46
+
47
+ Also fixes `camelizeKeys` to preserve user-defined trait keys (e.g., `company_name` stays as-is instead of becoming `companyName`).
48
+
3
49
  ## 1.15.2
4
50
 
5
51
  ### Patch Changes
package/README.md CHANGED
@@ -59,9 +59,27 @@ console.log(recs.guidance);
59
59
  ### Videos
60
60
 
61
61
  ```typescript
62
- // List latest videos
62
+ // List latest videos (default: 12)
63
63
  const videos = await bold.videos.list();
64
64
 
65
+ // With limit (backwards compatible)
66
+ const videos = await bold.videos.list(20);
67
+
68
+ // With filters
69
+ const videos = await bold.videos.list({
70
+ limit: 20,
71
+ tag: 'sales',
72
+ collectionId: 'col_123',
73
+ viewerId: 'viewer_123' // Include watch progress
74
+ });
75
+
76
+ // Paginated index (uses /videos endpoint)
77
+ const videos = await bold.videos.list({
78
+ page: 2,
79
+ tag: 'sales',
80
+ collectionId: 'col_123'
81
+ });
82
+
65
83
  // Get a single video by ID or slug
66
84
  const video = await bold.videos.get('video-id');
67
85
  const videoBySlug = await bold.videos.get('my-video-slug');
@@ -101,6 +119,65 @@ settings.menuItems.forEach(item => {
101
119
 
102
120
  ---
103
121
 
122
+ ## Viewers API
123
+
124
+ Manage external users and track their video watch progress. Ideal for course platforms integrating with Bold Video.
125
+
126
+ ### Viewer Management
127
+
128
+ ```typescript
129
+ // Create a viewer (e.g., when user signs up)
130
+ const { data: viewer } = await bold.viewers.create({
131
+ name: 'John Doe',
132
+ externalId: 'user_123', // Your platform's user ID
133
+ email: 'john@example.com',
134
+ traits: { plan: 'pro', company_name: 'Acme Inc' }
135
+ });
136
+
137
+ // Find viewer by external ID (common for syncing users)
138
+ const { data: viewer } = await bold.viewers.lookup({ externalId: 'user_123' });
139
+
140
+ // Or find by email
141
+ const { data: viewer } = await bold.viewers.lookup({ email: 'john@example.com' });
142
+
143
+ // Update viewer
144
+ await bold.viewers.update(viewer.id, {
145
+ traits: { plan: 'enterprise' } // Note: traits are replaced, not merged
146
+ });
147
+
148
+ // List all viewers
149
+ const { data: viewers } = await bold.viewers.list();
150
+ ```
151
+
152
+ ### Progress Tracking
153
+
154
+ ```typescript
155
+ // Save progress as video plays (call every 5-10 seconds)
156
+ await bold.viewers.saveProgress(viewerId, videoId, {
157
+ currentTime: 120, // seconds
158
+ duration: 600 // total video duration
159
+ });
160
+
161
+ // Mark video complete by setting currentTime = duration
162
+ await bold.viewers.saveProgress(viewerId, videoId, {
163
+ currentTime: 600,
164
+ duration: 600
165
+ });
166
+
167
+ // Get progress for a specific video
168
+ const { data: progress } = await bold.viewers.getProgress(viewerId, videoId);
169
+ console.log(`${progress.percentage}% complete`);
170
+
171
+ // List all progress for a viewer (e.g., for a course dashboard)
172
+ const { data: progress, meta } = await bold.viewers.listProgress(viewerId, {
173
+ collectionId: 'course-collection-id', // Filter to a course
174
+ completed: false // Only in-progress videos
175
+ });
176
+ console.log(`Completed ${meta.completed} of ${meta.total} videos`);
177
+ ```
178
+
179
+ ---
180
+
104
181
  ## AI Methods
105
182
 
106
183
  All AI methods support both streaming (default) and non-streaming modes.
@@ -294,7 +371,14 @@ import type {
294
371
  Recommendation,
295
372
  Conversation,
296
373
  ConversationMessage,
297
- Source
374
+ Source,
375
+ Viewer,
376
+ ViewerProgress,
377
+ ViewerLookupParams,
378
+ ListProgressOptions,
379
+ ListVideosOptions,
380
+ ListVideosLatestOptions,
381
+ ListVideosIndexOptions
298
382
  } from '@boldvideo/bold-js';
299
383
  ```
300
384
 
package/dist/index.cjs CHANGED
@@ -32,19 +32,21 @@ var src_exports = {};
32
32
  __export(src_exports, {
33
33
  DEFAULT_API_BASE_URL: () => DEFAULT_API_BASE_URL,
34
34
  DEFAULT_INTERNAL_API_BASE_URL: () => DEFAULT_INTERNAL_API_BASE_URL,
35
+ ViewerAPIError: () => ViewerAPIError,
35
36
  createClient: () => createClient
36
37
  });
37
38
  module.exports = __toCommonJS(src_exports);
38
39
 
39
40
  // src/lib/client.ts
40
- var import_axios = __toESM(require("axios"), 1);
41
+ var import_axios2 = __toESM(require("axios"), 1);
41
42
 
42
43
  // src/util/camelize.ts
43
44
  var isPlainObject = (value) => value !== null && typeof value === "object" && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
44
45
  var snakeToCamel = (key) => key.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase());
45
- function camelizeKeys(input) {
46
+ function camelizeKeys(input, options = {}) {
47
+ const preserve = new Set(options.preserveKeys ?? []);
46
48
  if (Array.isArray(input)) {
47
- return input.map((item) => camelizeKeys(item));
49
+ return input.map((item) => camelizeKeys(item, options));
48
50
  }
49
51
  if (!isPlainObject(input)) {
50
52
  return input;
@@ -52,12 +54,25 @@ function camelizeKeys(input) {
52
54
  const out = {};
53
55
  for (const [rawKey, value] of Object.entries(input)) {
54
56
  const key = snakeToCamel(rawKey);
55
- out[key] = camelizeKeys(value);
57
+ if (preserve.has(rawKey) || preserve.has(key)) {
58
+ out[key] = value;
59
+ continue;
60
+ }
61
+ out[key] = camelizeKeys(value, options);
56
62
  }
57
63
  return out;
58
64
  }
59
65
 
60
66
  // src/lib/fetchers.ts
67
+ function toQuery(params) {
68
+ const qs = new URLSearchParams();
69
+ for (const [k, v] of Object.entries(params)) {
70
+ if (v !== void 0 && v !== null)
71
+ qs.set(k, String(v));
72
+ }
73
+ const s = qs.toString();
74
+ return s ? `?${s}` : "";
75
+ }
61
76
  async function get(client, url) {
62
77
  try {
63
78
  const res = await client.get(url);
@@ -84,14 +99,44 @@ function fetchSettings(client) {
84
99
  };
85
100
  }
86
101
  function fetchVideos(client) {
87
- return async (videoLimit = 12) => {
102
+ return async (arg = 12) => {
88
103
  try {
104
+ if (typeof arg === "number") {
105
+ return await get(
106
+ client,
107
+ `videos/latest${toQuery({ limit: arg })}`
108
+ );
109
+ }
110
+ const opts = arg;
111
+ const hasPage = "page" in opts && opts.page !== void 0;
112
+ if (hasPage && ("limit" in opts || "viewerId" in opts)) {
113
+ throw new Error(
114
+ "videos.list(): cannot use `page` with `limit` or `viewerId` (these belong to different endpoints)"
115
+ );
116
+ }
117
+ if (hasPage) {
118
+ const { page, tag: tag2, collectionId: collectionId2 } = opts;
119
+ return await get(
120
+ client,
121
+ `videos${toQuery({
122
+ page,
123
+ tag: tag2,
124
+ collection_id: collectionId2
125
+ })}`
126
+ );
127
+ }
128
+ const { limit, tag, collectionId, viewerId } = opts;
89
129
  return await get(
90
130
  client,
91
- `videos/latest?limit=${videoLimit}`
131
+ `videos/latest${toQuery({
132
+ limit: limit ?? 12,
133
+ tag,
134
+ collection_id: collectionId,
135
+ viewer_id: viewerId
136
+ })}`
92
137
  );
93
138
  } catch (error) {
94
- console.error(`Error fetching videos with limit: ${videoLimit}`, error);
139
+ console.error(`Error fetching videos`, error);
95
140
  throw error;
96
141
  }
97
142
  };
@@ -137,6 +182,154 @@ function fetchPlaylist(client) {
137
182
  };
138
183
  }
139
184
 
185
+ // src/lib/viewers.ts
186
+ var import_axios = require("axios");
187
+ var VIEWER_CAMELIZE_OPTIONS = { preserveKeys: ["traits"] };
188
+ var ViewerAPIError = class extends Error {
189
+ constructor(method, url, error) {
190
+ var __super = (...args) => {
191
+ super(...args);
192
+ };
193
+ if (error instanceof import_axios.AxiosError) {
194
+ const status = error.response?.status;
195
+ const message = error.response?.data?.error || error.message;
196
+ __super(`${method} ${url} failed (${status}): ${message}`);
197
+ this.status = status;
198
+ this.originalError = error;
199
+ } else if (error instanceof Error) {
200
+ __super(`${method} ${url} failed: ${error.message}`);
201
+ this.originalError = error;
202
+ } else {
203
+ __super(`${method} ${url} failed: ${String(error)}`);
204
+ }
205
+ this.name = "ViewerAPIError";
206
+ }
207
+ };
208
+ async function get2(client, url, options) {
209
+ try {
210
+ const res = await client.get(url);
211
+ return camelizeKeys(res.data, options);
212
+ } catch (error) {
213
+ throw new ViewerAPIError("GET", url, error);
214
+ }
215
+ }
216
+ async function post(client, url, data, options) {
217
+ try {
218
+ const res = await client.post(url, data);
219
+ return camelizeKeys(res.data, options);
220
+ } catch (error) {
221
+ throw new ViewerAPIError("POST", url, error);
222
+ }
223
+ }
224
+ async function patch(client, url, data, options) {
225
+ try {
226
+ const res = await client.patch(url, data);
227
+ return camelizeKeys(res.data, options);
228
+ } catch (error) {
229
+ throw new ViewerAPIError("PATCH", url, error);
230
+ }
231
+ }
232
+ function fetchViewers(client) {
233
+ return async () => {
234
+ return get2(client, "viewers", VIEWER_CAMELIZE_OPTIONS);
235
+ };
236
+ }
237
+ function fetchViewer(client) {
238
+ return async (id) => {
239
+ if (!id)
240
+ throw new Error("Viewer ID is required");
241
+ return get2(client, `viewers/${id}`, VIEWER_CAMELIZE_OPTIONS);
242
+ };
243
+ }
244
+ function lookupViewer(client) {
245
+ return async (params) => {
246
+ const qs = new URLSearchParams();
247
+ if ("externalId" in params && params.externalId) {
248
+ qs.set("external_id", params.externalId);
249
+ }
250
+ if ("email" in params && params.email) {
251
+ qs.set("email", params.email);
252
+ }
253
+ if (!qs.toString()) {
254
+ throw new Error("Either externalId or email is required");
255
+ }
256
+ return get2(client, `viewers/lookup?${qs.toString()}`, VIEWER_CAMELIZE_OPTIONS);
257
+ };
258
+ }
259
+ function createViewer(client) {
260
+ return async (data) => {
261
+ if (!data.name)
262
+ throw new Error("Viewer name is required");
263
+ return post(client, "viewers", {
264
+ viewer: {
265
+ name: data.name,
266
+ email: data.email,
267
+ external_id: data.externalId,
268
+ traits: data.traits
269
+ }
270
+ }, VIEWER_CAMELIZE_OPTIONS);
271
+ };
272
+ }
273
+ function updateViewer(client) {
274
+ return async (id, data) => {
275
+ if (!id)
276
+ throw new Error("Viewer ID is required");
277
+ const body = {};
278
+ if (data.name !== void 0)
279
+ body.name = data.name;
280
+ if (data.email !== void 0)
281
+ body.email = data.email;
282
+ if (data.externalId !== void 0)
283
+ body.external_id = data.externalId;
284
+ if (data.traits !== void 0)
285
+ body.traits = data.traits;
286
+ return patch(client, `viewers/${id}`, { viewer: body }, VIEWER_CAMELIZE_OPTIONS);
287
+ };
288
+ }
289
+ function fetchViewerProgress(client) {
290
+ return async (viewerId, options) => {
291
+ if (!viewerId)
292
+ throw new Error("Viewer ID is required");
293
+ const params = new URLSearchParams();
294
+ if (options?.completed !== void 0)
295
+ params.set("completed", String(options.completed));
296
+ if (options?.collectionId)
297
+ params.set("collection_id", options.collectionId);
298
+ const query = params.toString();
299
+ const url = query ? `viewers/${viewerId}/progress?${query}` : `viewers/${viewerId}/progress`;
300
+ return get2(client, url);
301
+ };
302
+ }
303
+ function fetchProgress(client) {
304
+ return async (viewerId, videoId) => {
305
+ if (!viewerId)
306
+ throw new Error("Viewer ID is required");
307
+ if (!videoId)
308
+ throw new Error("Video ID is required");
309
+ return get2(client, `viewers/${viewerId}/progress/${videoId}`);
310
+ };
311
+ }
312
+ function saveProgress(client) {
313
+ return async (viewerId, videoId, data) => {
314
+ if (!viewerId)
315
+ throw new Error("Viewer ID is required");
316
+ if (!videoId)
317
+ throw new Error("Video ID is required");
318
+ if (!Number.isFinite(data.currentTime) || data.currentTime < 0) {
319
+ throw new Error("currentTime must be a non-negative number");
320
+ }
321
+ if (!Number.isFinite(data.duration) || data.duration <= 0) {
322
+ throw new Error("duration must be a positive number");
323
+ }
324
+ return post(client, `viewers/${viewerId}/progress/${videoId}`, {
325
+ progress: {
326
+ current_time: data.currentTime,
327
+ duration: data.duration
328
+ }
329
+ });
330
+ };
331
+ }
332
+
140
333
  // src/util/throttle.ts
141
334
  var throttle = (fn, delay) => {
142
335
  let wait = false;
@@ -438,7 +631,7 @@ function createClient(apiKey, options = {}) {
438
631
  };
439
632
  let apiClient;
440
633
  try {
441
- apiClient = import_axios.default.create(apiClientOptions);
634
+ apiClient = import_axios2.default.create(apiClientOptions);
442
635
  } catch (error) {
443
636
  console.error("Error creating API client", error);
444
637
  throw error;
@@ -459,6 +652,16 @@ function createClient(apiKey, options = {}) {
459
652
  list: fetchPlaylists(apiClient),
460
653
  get: fetchPlaylist(apiClient)
461
654
  },
655
+ viewers: {
656
+ list: fetchViewers(apiClient),
657
+ get: fetchViewer(apiClient),
658
+ lookup: lookupViewer(apiClient),
659
+ create: createViewer(apiClient),
660
+ update: updateViewer(apiClient),
661
+ listProgress: fetchViewerProgress(apiClient),
662
+ getProgress: fetchProgress(apiClient),
663
+ saveProgress: saveProgress(apiClient)
664
+ },
462
665
  ai: createAI(aiConfig),
463
666
  trackEvent: trackEvent(apiClient, userId, { debug }),
464
667
  trackPageView: trackPageView(apiClient, userId, { debug })
@@ -468,5 +671,6 @@ function createClient(apiKey, options = {}) {
468
671
  0 && (module.exports = {
469
672
  DEFAULT_API_BASE_URL,
470
673
  DEFAULT_INTERNAL_API_BASE_URL,
674
+ ViewerAPIError,
471
675
  createClient
472
676
  });
package/dist/index.d.ts CHANGED
@@ -390,6 +390,127 @@ interface Conversation {
390
390
  createdAt: string;
391
391
  updatedAt: string;
392
392
  }
393
+ /**
394
+ * Viewer represents an external user from a course platform
395
+ */
396
+ type Viewer = {
397
+ id: string;
398
+ name: string;
399
+ email?: string;
400
+ externalId?: string;
401
+ /** Key-value metadata. Keys must start with letter/underscore, contain only alphanumeric/underscore */
402
+ traits?: Record<string, unknown>;
403
+ insertedAt: string;
404
+ updatedAt: string;
405
+ };
406
+ /**
407
+ * Progress record for a viewer-video pair
408
+ */
409
+ type ViewerProgress = {
410
+ id: string;
411
+ viewerId: string;
412
+ videoId: string;
413
+ /** Current playback position in seconds */
414
+ currentTime: number;
415
+ /** Total video duration in seconds */
416
+ duration: number;
417
+ /** Calculated: (currentTime / duration) * 100 */
418
+ percentage: number;
419
+ completed: boolean;
420
+ completedAt?: string;
421
+ insertedAt: string;
422
+ updatedAt: string;
423
+ };
424
+ /**
425
+ * Options for listing viewer progress
426
+ */
427
+ type ListProgressOptions = {
428
+ /** Filter by completion status */
429
+ completed?: boolean;
430
+ /** Filter to videos in a specific collection */
431
+ collectionId?: string;
432
+ };
433
+ /**
434
+ * Data for creating a new viewer
435
+ */
436
+ type CreateViewerData = {
437
+ /** Display name (required) */
438
+ name: string;
439
+ /** Email address for lookup */
440
+ email?: string;
441
+ /** Your platform's user ID for lookup */
442
+ externalId?: string;
443
+ /** Key-value metadata. Keys must start with letter/underscore, contain only alphanumeric/underscore */
444
+ traits?: Record<string, unknown>;
445
+ };
446
+ /**
447
+ * Data for updating an existing viewer
448
+ */
449
+ type UpdateViewerData = {
450
+ name?: string;
451
+ email?: string;
452
+ externalId?: string;
453
+ /** Note: traits are replaced entirely, not merged */
454
+ traits?: Record<string, unknown>;
455
+ };
456
+ /**
457
+ * Data for saving video progress
458
+ */
459
+ type SaveProgressData = {
460
+ /** Current playback position in seconds (must be non-negative) */
461
+ currentTime: number;
462
+ /** Total video duration in seconds (must be positive) */
463
+ duration: number;
464
+ };
465
+ /**
466
+ * Metadata returned with progress list
467
+ */
468
+ type ProgressListMeta = {
469
+ /** Total number of progress records */
470
+ total: number;
471
+ /** Number of completed videos */
472
+ completed: number;
473
+ /** Number of in-progress videos */
474
+ inProgress: number;
475
+ };
476
+ /**
477
+ * Options for listing videos from /videos/latest endpoint
478
+ */
479
+ type ListVideosLatestOptions = {
480
+ /** Max videos to return (default: 12) */
481
+ limit?: number;
482
+ /** Filter by tag */
483
+ tag?: string;
484
+ /** Filter to videos in a specific collection */
485
+ collectionId?: string;
486
+ /** Viewer UUID for watch progress */
487
+ viewerId?: string;
488
+ };
489
+ /**
490
+ * Options for listing videos from /videos (index) endpoint with pagination
491
+ */
492
+ type ListVideosIndexOptions = {
493
+ /** Page number for pagination */
494
+ page?: number;
495
+ /** Filter by tag */
496
+ tag?: string;
497
+ /** Filter to videos in a specific collection */
498
+ collectionId?: string;
499
+ };
500
+ /**
501
+ * Combined options for bold.videos.list()
502
+ *
503
+ * If `page` is provided, uses /videos (index) endpoint.
504
+ * Otherwise, uses /videos/latest endpoint.
505
+ *
506
+ * Note: `page` and `limit`/`viewerId` are mutually exclusive.
507
+ */
508
+ type ListVideosOptions = (ListVideosLatestOptions & {
509
+ page?: never;
510
+ }) | (ListVideosIndexOptions & {
511
+ limit?: never;
512
+ viewerId?: never;
513
+ });
393
514
 
394
515
  /**
395
516
  * AI client interface for type-safe method overloading
@@ -503,6 +624,19 @@ interface AIClient {
503
624
  getConversation(conversationId: string): Promise<Conversation>;
504
625
  }
505
626
 
627
+ declare class ViewerAPIError extends Error {
628
+ readonly status?: number;
629
+ readonly originalError?: Error;
630
+ constructor(method: string, url: string, error: unknown);
631
+ }
632
+ type ViewerLookupParams = {
633
+ externalId: string;
634
+ email?: never;
635
+ } | {
636
+ email: string;
637
+ externalId?: never;
638
+ };
639
+
506
640
  type ClientOptions = {
507
641
  baseURL?: string;
508
642
  debug?: boolean;
@@ -513,7 +647,7 @@ declare function createClient(apiKey: string, options?: ClientOptions): {
513
647
  data: Settings;
514
648
  }>;
515
649
  videos: {
516
- list: (videoLimit?: number) => Promise<{
650
+ list: (arg?: number | ListVideosOptions) => Promise<{
517
651
  data: Video[];
518
652
  }>;
519
653
  get: (id: string) => Promise<{
@@ -531,6 +665,33 @@ declare function createClient(apiKey: string, options?: ClientOptions): {
531
665
  data: Playlist;
532
666
  }>;
533
667
  };
668
+ viewers: {
669
+ list: () => Promise<{
670
+ data: Viewer[];
671
+ }>;
672
+ get: (id: string) => Promise<{
673
+ data: Viewer;
674
+ }>;
675
+ lookup: (params: ViewerLookupParams) => Promise<{
676
+ data: Viewer;
677
+ }>;
678
+ create: (data: CreateViewerData) => Promise<{
679
+ data: Viewer;
680
+ }>;
681
+ update: (id: string, data: UpdateViewerData) => Promise<{
682
+ data: Viewer;
683
+ }>;
684
+ listProgress: (viewerId: string, options?: ListProgressOptions | undefined) => Promise<{
685
+ data: ViewerProgress[];
686
+ meta: ProgressListMeta;
687
+ }>;
688
+ getProgress: (viewerId: string, videoId: string) => Promise<{
689
+ data: ViewerProgress;
690
+ }>;
691
+ saveProgress: (viewerId: string, videoId: string, data: SaveProgressData) => Promise<{
692
+ data: ViewerProgress;
693
+ }>;
694
+ };
534
695
  ai: AIClient;
535
696
  trackEvent: (video: any, event: Event) => void;
536
697
  trackPageView: (title: string) => void;
@@ -545,4 +706,4 @@ declare const DEFAULT_API_BASE_URL = "https://app.boldvideo.io/api/v1/";
545
706
  */
546
707
  declare const DEFAULT_INTERNAL_API_BASE_URL = "https://app.boldvideo.io/i/v1/";
547
708
 
548
- export { AIContextMessage, AIEvent, AIResponse, AIUsage, Account, AccountAI, AnalyticsProvider, AskOptions, AssistantConfig, ChatOptions, Citation, ClientOptions, Conversation, ConversationMessage, ConversationMetadata, CustomRedirect, DEFAULT_API_BASE_URL, DEFAULT_INTERNAL_API_BASE_URL, MenuItem, Playlist, Portal, PortalDisplay, PortalHero, PortalLayout, PortalNavigation, PortalTheme, RecommendOptions, RecommendResponse, Recommendation, RecommendationVideo, RecommendationsOptions, RecommendationsResponse, SearchOptions, Segment, Settings, Source, ThemeColors, ThemeConfig, Video, VideoAttachment, VideoDownloadUrls, VideoMetadata, VideoSubtitles, VideoTranscript, createClient };
709
+ export { AIContextMessage, AIEvent, AIResponse, AIUsage, Account, AccountAI, AnalyticsProvider, AskOptions, AssistantConfig, ChatOptions, Citation, ClientOptions, Conversation, ConversationMessage, ConversationMetadata, CreateViewerData, CustomRedirect, DEFAULT_API_BASE_URL, DEFAULT_INTERNAL_API_BASE_URL, ListProgressOptions, ListVideosIndexOptions, ListVideosLatestOptions, ListVideosOptions, MenuItem, Playlist, Portal, PortalDisplay, PortalHero, PortalLayout, PortalNavigation, PortalTheme, ProgressListMeta, RecommendOptions, RecommendResponse, Recommendation, RecommendationVideo, RecommendationsOptions, RecommendationsResponse, SaveProgressData, SearchOptions, Segment, Settings, Source, ThemeColors, ThemeConfig, UpdateViewerData, Video, VideoAttachment, VideoDownloadUrls, VideoMetadata, VideoSubtitles, VideoTranscript, Viewer, ViewerAPIError, ViewerLookupParams, ViewerProgress, createClient };