@boldvideo/bold-js 1.17.0 → 1.19.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 +62 -0
- package/README.md +73 -1
- package/dist/index.cjs +201 -2
- package/dist/index.d.ts +204 -1
- package/dist/index.js +198 -0
- package/llms.txt +123 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,67 @@
|
|
|
1
1
|
# @boldvideo/bold-js
|
|
2
2
|
|
|
3
|
+
## 1.19.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- d423437: Redesign Community API types to eliminate N+1 queries
|
|
8
|
+
|
|
9
|
+
**Breaking changes to Post type:**
|
|
10
|
+
|
|
11
|
+
- `viewer` → `author` (creator of the post)
|
|
12
|
+
- `reactionsCount` → `reactions.count`
|
|
13
|
+
- `commentsCount` → `comments.count`
|
|
14
|
+
- `viewerReacted` → `reactions.viewer_has_reacted`
|
|
15
|
+
- `createdAt` → `created_at`
|
|
16
|
+
|
|
17
|
+
**New types:**
|
|
18
|
+
|
|
19
|
+
- `UserSummary` - minimal profile with `id`, `name`, `avatar_url`
|
|
20
|
+
- `ReactionSummary` - `{ count, reacted_by[], viewer_has_reacted? }`
|
|
21
|
+
- `CommentSummary` - `{ count, commented_by[], items? }`
|
|
22
|
+
- `CommentThread` and `Reply` - structured comment threading
|
|
23
|
+
- `PaginationMeta` and `PaginatedResponse<T>` - page-based pagination
|
|
24
|
+
|
|
25
|
+
**Pagination changes:**
|
|
26
|
+
|
|
27
|
+
- `posts.list()` now uses `page`/`pageSize` instead of `limit`/`offset`
|
|
28
|
+
- Returns `PaginatedResponse<Post>` with `data[]` and `meta`
|
|
29
|
+
|
|
30
|
+
**Benefits:**
|
|
31
|
+
|
|
32
|
+
- List endpoint now includes `reacted_by` and `commented_by` arrays (up to 10 users)
|
|
33
|
+
- Enables avatar display without fetching each post individually
|
|
34
|
+
- Consistent object shapes between list and detail views
|
|
35
|
+
|
|
36
|
+
## 1.18.0
|
|
37
|
+
|
|
38
|
+
### Minor Changes
|
|
39
|
+
|
|
40
|
+
- 48d8b40: Add Community API for posts, comments, and reactions
|
|
41
|
+
|
|
42
|
+
New `bold.community` namespace with full CRUD operations:
|
|
43
|
+
|
|
44
|
+
**Posts:**
|
|
45
|
+
|
|
46
|
+
- `bold.community.posts.list(opts?)` - List posts with category/limit/offset filters
|
|
47
|
+
- `bold.community.posts.get(id, viewerId?)` - Get single post with comments
|
|
48
|
+
- `bold.community.posts.create(viewerId, data)` - Create a post (markdown supported)
|
|
49
|
+
- `bold.community.posts.update(viewerId, id, data)` - Update post (owner/admin only)
|
|
50
|
+
- `bold.community.posts.delete(viewerId, id)` - Delete post (owner/admin only)
|
|
51
|
+
- `bold.community.posts.react(viewerId, id)` - Toggle reaction (like/unlike)
|
|
52
|
+
|
|
53
|
+
**Comments:**
|
|
54
|
+
|
|
55
|
+
- `bold.community.comments.create(viewerId, postId, data)` - Create comment with optional `parentId` for nested replies
|
|
56
|
+
- `bold.community.comments.delete(viewerId, id)` - Delete comment (owner/admin only)
|
|
57
|
+
- `bold.community.comments.react(viewerId, id)` - Toggle reaction
|
|
58
|
+
|
|
59
|
+
**New Types:**
|
|
60
|
+
|
|
61
|
+
- `Post`, `PostAuthor`, `Comment`, `ReactionResponse`
|
|
62
|
+
- `ListPostsOptions`, `CreatePostData`, `UpdatePostData`, `CreateCommentData`
|
|
63
|
+
- `CommunityAPIError` - Error class for Community API operations
|
|
64
|
+
|
|
3
65
|
## 1.17.0
|
|
4
66
|
|
|
5
67
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -178,6 +178,69 @@ console.log(`Completed ${meta.completed} of ${meta.total} videos`);
|
|
|
178
178
|
|
|
179
179
|
---
|
|
180
180
|
|
|
181
|
+
## Community API
|
|
182
|
+
|
|
183
|
+
Build community features with posts, comments, and reactions. All write operations require a `viewerId` (the viewer performing the action).
|
|
184
|
+
|
|
185
|
+
### Posts
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
// List posts (optionally filter by category)
|
|
189
|
+
const { data: posts } = await bold.community.posts.list({
|
|
190
|
+
category: 'announcements',
|
|
191
|
+
limit: 20,
|
|
192
|
+
offset: 0,
|
|
193
|
+
viewerId: 'viewer-uuid' // Include viewerReacted in response
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Get a single post with comments
|
|
197
|
+
const { data: post } = await bold.community.posts.get('post-id', 'viewer-uuid');
|
|
198
|
+
|
|
199
|
+
// Create a post (requires viewerId)
|
|
200
|
+
const { data: newPost } = await bold.community.posts.create('viewer-uuid', {
|
|
201
|
+
content: 'Hello community! **Markdown** supported.',
|
|
202
|
+
category: 'general'
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Update a post (owner or admin only)
|
|
206
|
+
await bold.community.posts.update('viewer-uuid', 'post-id', {
|
|
207
|
+
content: 'Updated content'
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Delete a post (owner or admin only)
|
|
211
|
+
await bold.community.posts.delete('viewer-uuid', 'post-id');
|
|
212
|
+
|
|
213
|
+
// React to a post (toggle like/unlike)
|
|
214
|
+
const reaction = await bold.community.posts.react('viewer-uuid', 'post-id');
|
|
215
|
+
console.log(reaction.reacted, reaction.reactionsCount);
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Comments
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
// Create a comment on a post
|
|
222
|
+
const { data: comment } = await bold.community.comments.create(
|
|
223
|
+
'viewer-uuid',
|
|
224
|
+
'post-id',
|
|
225
|
+
{ content: 'Great post!' }
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
// Reply to a comment (nested)
|
|
229
|
+
const { data: reply } = await bold.community.comments.create(
|
|
230
|
+
'viewer-uuid',
|
|
231
|
+
'post-id',
|
|
232
|
+
{ content: 'I agree!', parentId: 'parent-comment-id' }
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
// Delete a comment (owner or admin only)
|
|
236
|
+
await bold.community.comments.delete('viewer-uuid', 'comment-id');
|
|
237
|
+
|
|
238
|
+
// React to a comment (toggle)
|
|
239
|
+
const reaction = await bold.community.comments.react('viewer-uuid', 'comment-id');
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
181
244
|
## AI Methods
|
|
182
245
|
|
|
183
246
|
All AI methods support both streaming (default) and non-streaming modes.
|
|
@@ -378,7 +441,16 @@ import type {
|
|
|
378
441
|
ListProgressOptions,
|
|
379
442
|
ListVideosOptions,
|
|
380
443
|
ListVideosLatestOptions,
|
|
381
|
-
ListVideosIndexOptions
|
|
444
|
+
ListVideosIndexOptions,
|
|
445
|
+
// Community API
|
|
446
|
+
Post,
|
|
447
|
+
PostAuthor,
|
|
448
|
+
Comment,
|
|
449
|
+
ReactionResponse,
|
|
450
|
+
ListPostsOptions,
|
|
451
|
+
CreatePostData,
|
|
452
|
+
UpdatePostData,
|
|
453
|
+
CreateCommentData
|
|
382
454
|
} from '@boldvideo/bold-js';
|
|
383
455
|
```
|
|
384
456
|
|
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
|
+
CommunityAPIError: () => CommunityAPIError,
|
|
33
34
|
DEFAULT_API_BASE_URL: () => DEFAULT_API_BASE_URL,
|
|
34
35
|
DEFAULT_INTERNAL_API_BASE_URL: () => DEFAULT_INTERNAL_API_BASE_URL,
|
|
35
36
|
ViewerAPIError: () => ViewerAPIError,
|
|
@@ -38,7 +39,7 @@ __export(src_exports, {
|
|
|
38
39
|
module.exports = __toCommonJS(src_exports);
|
|
39
40
|
|
|
40
41
|
// src/lib/client.ts
|
|
41
|
-
var
|
|
42
|
+
var import_axios3 = __toESM(require("axios"), 1);
|
|
42
43
|
|
|
43
44
|
// src/util/camelize.ts
|
|
44
45
|
var isPlainObject = (value) => value !== null && typeof value === "object" && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
|
|
@@ -330,6 +331,188 @@ function saveProgress(client) {
|
|
|
330
331
|
};
|
|
331
332
|
}
|
|
332
333
|
|
|
334
|
+
// src/lib/community.ts
|
|
335
|
+
var import_axios2 = require("axios");
|
|
336
|
+
var CommunityAPIError = class extends Error {
|
|
337
|
+
constructor(method, url, error) {
|
|
338
|
+
var __super = (...args) => {
|
|
339
|
+
super(...args);
|
|
340
|
+
};
|
|
341
|
+
if (error instanceof import_axios2.AxiosError) {
|
|
342
|
+
const status = error.response?.status;
|
|
343
|
+
const message = error.response?.data?.error || error.message;
|
|
344
|
+
__super(`${method} ${url} failed (${status}): ${message}`);
|
|
345
|
+
this.status = status;
|
|
346
|
+
this.originalError = error;
|
|
347
|
+
} else if (error instanceof Error) {
|
|
348
|
+
__super(`${method} ${url} failed: ${error.message}`);
|
|
349
|
+
this.originalError = error;
|
|
350
|
+
} else {
|
|
351
|
+
__super(`${method} ${url} failed: ${String(error)}`);
|
|
352
|
+
}
|
|
353
|
+
this.name = "CommunityAPIError";
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
function requireViewerId(viewerId) {
|
|
357
|
+
if (!viewerId)
|
|
358
|
+
throw new Error("Viewer ID is required (X-Viewer-ID)");
|
|
359
|
+
}
|
|
360
|
+
function viewerHeaders(viewerId) {
|
|
361
|
+
return viewerId ? { "X-Viewer-ID": viewerId } : void 0;
|
|
362
|
+
}
|
|
363
|
+
function toQuery2(params) {
|
|
364
|
+
const qs = new URLSearchParams();
|
|
365
|
+
for (const [k, v] of Object.entries(params)) {
|
|
366
|
+
if (v !== void 0 && v !== null)
|
|
367
|
+
qs.set(k, String(v));
|
|
368
|
+
}
|
|
369
|
+
const s = qs.toString();
|
|
370
|
+
return s ? `?${s}` : "";
|
|
371
|
+
}
|
|
372
|
+
async function get3(client, url, viewerId) {
|
|
373
|
+
try {
|
|
374
|
+
const res = await client.get(url, { headers: viewerHeaders(viewerId) });
|
|
375
|
+
return camelizeKeys(res.data);
|
|
376
|
+
} catch (error) {
|
|
377
|
+
throw new CommunityAPIError("GET", url, error);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
async function post2(client, url, data, viewerId) {
|
|
381
|
+
try {
|
|
382
|
+
const res = await client.post(url, data, { headers: viewerHeaders(viewerId) });
|
|
383
|
+
return camelizeKeys(res.data);
|
|
384
|
+
} catch (error) {
|
|
385
|
+
throw new CommunityAPIError("POST", url, error);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
async function put(client, url, data, viewerId) {
|
|
389
|
+
try {
|
|
390
|
+
const res = await client.put(url, data, { headers: viewerHeaders(viewerId) });
|
|
391
|
+
return camelizeKeys(res.data);
|
|
392
|
+
} catch (error) {
|
|
393
|
+
throw new CommunityAPIError("PUT", url, error);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
async function del(client, url, viewerId) {
|
|
397
|
+
try {
|
|
398
|
+
const res = await client.delete(url, { headers: viewerHeaders(viewerId) });
|
|
399
|
+
return camelizeKeys(res.data);
|
|
400
|
+
} catch (error) {
|
|
401
|
+
throw new CommunityAPIError("DELETE", url, error);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
function listPosts(client) {
|
|
405
|
+
return async (opts = {}) => {
|
|
406
|
+
return get3(
|
|
407
|
+
client,
|
|
408
|
+
`community/posts${toQuery2({
|
|
409
|
+
category: opts.category,
|
|
410
|
+
page: opts.page,
|
|
411
|
+
page_size: opts.pageSize
|
|
412
|
+
})}`,
|
|
413
|
+
opts.viewerId
|
|
414
|
+
);
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
function getPost(client) {
|
|
418
|
+
return async (id, viewerId) => {
|
|
419
|
+
if (!id)
|
|
420
|
+
throw new Error("Post ID is required");
|
|
421
|
+
return get3(client, `community/posts/${id}`, viewerId);
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
function createPost(client) {
|
|
425
|
+
return async (viewerId, data) => {
|
|
426
|
+
requireViewerId(viewerId);
|
|
427
|
+
if (!data?.content)
|
|
428
|
+
throw new Error("Post content is required");
|
|
429
|
+
return post2(
|
|
430
|
+
client,
|
|
431
|
+
"community/posts",
|
|
432
|
+
{ post: { content: data.content, category: data.category } },
|
|
433
|
+
viewerId
|
|
434
|
+
);
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
function updatePost(client) {
|
|
438
|
+
return async (viewerId, id, data) => {
|
|
439
|
+
requireViewerId(viewerId);
|
|
440
|
+
if (!id)
|
|
441
|
+
throw new Error("Post ID is required");
|
|
442
|
+
const body = {};
|
|
443
|
+
if (data.content !== void 0)
|
|
444
|
+
body.content = data.content;
|
|
445
|
+
if (data.category !== void 0)
|
|
446
|
+
body.category = data.category;
|
|
447
|
+
return put(
|
|
448
|
+
client,
|
|
449
|
+
`community/posts/${id}`,
|
|
450
|
+
{ post: body },
|
|
451
|
+
viewerId
|
|
452
|
+
);
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
function deletePost(client) {
|
|
456
|
+
return async (viewerId, id) => {
|
|
457
|
+
requireViewerId(viewerId);
|
|
458
|
+
if (!id)
|
|
459
|
+
throw new Error("Post ID is required");
|
|
460
|
+
return del(client, `community/posts/${id}`, viewerId);
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
function reactToPost(client) {
|
|
464
|
+
return async (viewerId, id) => {
|
|
465
|
+
requireViewerId(viewerId);
|
|
466
|
+
if (!id)
|
|
467
|
+
throw new Error("Post ID is required");
|
|
468
|
+
return post2(
|
|
469
|
+
client,
|
|
470
|
+
`community/posts/${id}/react`,
|
|
471
|
+
void 0,
|
|
472
|
+
viewerId
|
|
473
|
+
);
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
function createComment(client) {
|
|
477
|
+
return async (viewerId, postId, data) => {
|
|
478
|
+
requireViewerId(viewerId);
|
|
479
|
+
if (!postId)
|
|
480
|
+
throw new Error("Post ID is required");
|
|
481
|
+
if (!data?.content)
|
|
482
|
+
throw new Error("Comment content is required");
|
|
483
|
+
const body = { content: data.content };
|
|
484
|
+
if (data.parentId)
|
|
485
|
+
body.parent_id = data.parentId;
|
|
486
|
+
return post2(
|
|
487
|
+
client,
|
|
488
|
+
`community/posts/${postId}/comments`,
|
|
489
|
+
{ comment: body },
|
|
490
|
+
viewerId
|
|
491
|
+
);
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
function deleteComment(client) {
|
|
495
|
+
return async (viewerId, id) => {
|
|
496
|
+
requireViewerId(viewerId);
|
|
497
|
+
if (!id)
|
|
498
|
+
throw new Error("Comment ID is required");
|
|
499
|
+
return del(client, `community/comments/${id}`, viewerId);
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
function reactToComment(client) {
|
|
503
|
+
return async (viewerId, id) => {
|
|
504
|
+
requireViewerId(viewerId);
|
|
505
|
+
if (!id)
|
|
506
|
+
throw new Error("Comment ID is required");
|
|
507
|
+
return post2(
|
|
508
|
+
client,
|
|
509
|
+
`community/comments/${id}/react`,
|
|
510
|
+
void 0,
|
|
511
|
+
viewerId
|
|
512
|
+
);
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
|
|
333
516
|
// src/util/throttle.ts
|
|
334
517
|
var throttle = (fn, delay) => {
|
|
335
518
|
let wait = false;
|
|
@@ -631,7 +814,7 @@ function createClient(apiKey, options = {}) {
|
|
|
631
814
|
};
|
|
632
815
|
let apiClient;
|
|
633
816
|
try {
|
|
634
|
-
apiClient =
|
|
817
|
+
apiClient = import_axios3.default.create(apiClientOptions);
|
|
635
818
|
} catch (error) {
|
|
636
819
|
console.error("Error creating API client", error);
|
|
637
820
|
throw error;
|
|
@@ -663,12 +846,28 @@ function createClient(apiKey, options = {}) {
|
|
|
663
846
|
saveProgress: saveProgress(apiClient)
|
|
664
847
|
},
|
|
665
848
|
ai: createAI(aiConfig),
|
|
849
|
+
community: {
|
|
850
|
+
posts: {
|
|
851
|
+
list: listPosts(apiClient),
|
|
852
|
+
get: getPost(apiClient),
|
|
853
|
+
create: createPost(apiClient),
|
|
854
|
+
update: updatePost(apiClient),
|
|
855
|
+
delete: deletePost(apiClient),
|
|
856
|
+
react: reactToPost(apiClient)
|
|
857
|
+
},
|
|
858
|
+
comments: {
|
|
859
|
+
create: createComment(apiClient),
|
|
860
|
+
delete: deleteComment(apiClient),
|
|
861
|
+
react: reactToComment(apiClient)
|
|
862
|
+
}
|
|
863
|
+
},
|
|
666
864
|
trackEvent: trackEvent(apiClient, userId, { debug }),
|
|
667
865
|
trackPageView: trackPageView(apiClient, userId, { debug })
|
|
668
866
|
};
|
|
669
867
|
}
|
|
670
868
|
// Annotate the CommonJS export names for ESM import in node:
|
|
671
869
|
0 && (module.exports = {
|
|
870
|
+
CommunityAPIError,
|
|
672
871
|
DEFAULT_API_BASE_URL,
|
|
673
872
|
DEFAULT_INTERNAL_API_BASE_URL,
|
|
674
873
|
ViewerAPIError,
|
package/dist/index.d.ts
CHANGED
|
@@ -511,6 +511,173 @@ type ListVideosOptions = (ListVideosLatestOptions & {
|
|
|
511
511
|
limit?: never;
|
|
512
512
|
viewerId?: never;
|
|
513
513
|
});
|
|
514
|
+
/**
|
|
515
|
+
* Minimal profile object for list contexts
|
|
516
|
+
*/
|
|
517
|
+
type UserSummary = {
|
|
518
|
+
id: string;
|
|
519
|
+
name: string;
|
|
520
|
+
avatar_url: string | null;
|
|
521
|
+
};
|
|
522
|
+
/**
|
|
523
|
+
* Author information for posts and comments (extended for admin context)
|
|
524
|
+
*/
|
|
525
|
+
type PostAuthor = UserSummary & {
|
|
526
|
+
email?: string;
|
|
527
|
+
isAdmin?: boolean;
|
|
528
|
+
};
|
|
529
|
+
/**
|
|
530
|
+
* Reaction summary with users who reacted
|
|
531
|
+
*/
|
|
532
|
+
type ReactionSummary = {
|
|
533
|
+
count: number;
|
|
534
|
+
/** Users who reacted (capped to 10 from API) */
|
|
535
|
+
reacted_by: UserSummary[];
|
|
536
|
+
/** Present when viewer context exists */
|
|
537
|
+
viewer_has_reacted?: boolean;
|
|
538
|
+
};
|
|
539
|
+
/**
|
|
540
|
+
* Comment summary with users who commented
|
|
541
|
+
*/
|
|
542
|
+
type CommentSummary = {
|
|
543
|
+
count: number;
|
|
544
|
+
/** Users who commented (capped to 10 from API) */
|
|
545
|
+
commented_by: UserSummary[];
|
|
546
|
+
/** Full comment threads (present in detail only) */
|
|
547
|
+
items?: CommentThread[];
|
|
548
|
+
};
|
|
549
|
+
/**
|
|
550
|
+
* A reply to a comment
|
|
551
|
+
*/
|
|
552
|
+
type Reply = {
|
|
553
|
+
id: string;
|
|
554
|
+
content: string;
|
|
555
|
+
created_at: string;
|
|
556
|
+
author: UserSummary;
|
|
557
|
+
parent_comment_id: string;
|
|
558
|
+
};
|
|
559
|
+
/**
|
|
560
|
+
* A top-level comment with replies
|
|
561
|
+
*/
|
|
562
|
+
type CommentThread = {
|
|
563
|
+
id: string;
|
|
564
|
+
content: string;
|
|
565
|
+
created_at: string;
|
|
566
|
+
author: UserSummary;
|
|
567
|
+
replies: Reply[];
|
|
568
|
+
};
|
|
569
|
+
/**
|
|
570
|
+
* @deprecated Use CommentThread instead
|
|
571
|
+
* A comment on a community post (legacy format)
|
|
572
|
+
*/
|
|
573
|
+
type Comment = {
|
|
574
|
+
id: string;
|
|
575
|
+
content: string;
|
|
576
|
+
/** Nesting depth (0 = top-level) */
|
|
577
|
+
depth: number;
|
|
578
|
+
reactionsCount: number;
|
|
579
|
+
/** Whether current viewer has reacted (only present when X-Viewer-ID header sent) */
|
|
580
|
+
viewerReacted?: boolean;
|
|
581
|
+
/** @deprecated Use author instead */
|
|
582
|
+
viewer: PostAuthor;
|
|
583
|
+
author: PostAuthor;
|
|
584
|
+
/** Nested replies */
|
|
585
|
+
replies: Comment[];
|
|
586
|
+
createdAt: string;
|
|
587
|
+
updatedAt: string;
|
|
588
|
+
};
|
|
589
|
+
/**
|
|
590
|
+
* A community post (list view)
|
|
591
|
+
*/
|
|
592
|
+
type Post = {
|
|
593
|
+
id: string;
|
|
594
|
+
content: string;
|
|
595
|
+
category?: string;
|
|
596
|
+
pinned?: boolean;
|
|
597
|
+
pinnedAt?: string | null;
|
|
598
|
+
created_at: string;
|
|
599
|
+
author: PostAuthor;
|
|
600
|
+
reactions: ReactionSummary;
|
|
601
|
+
comments: CommentSummary;
|
|
602
|
+
/** @deprecated Use created_at */
|
|
603
|
+
createdAt?: string;
|
|
604
|
+
/** @deprecated Use reactions.count */
|
|
605
|
+
reactionsCount?: number;
|
|
606
|
+
/** @deprecated Use comments.count */
|
|
607
|
+
commentsCount?: number;
|
|
608
|
+
/** @deprecated Use reactions.viewer_has_reacted */
|
|
609
|
+
viewerReacted?: boolean;
|
|
610
|
+
/** @deprecated Use author */
|
|
611
|
+
viewer?: PostAuthor;
|
|
612
|
+
updatedAt?: string;
|
|
613
|
+
};
|
|
614
|
+
/**
|
|
615
|
+
* Response from react endpoints (toggle)
|
|
616
|
+
*/
|
|
617
|
+
type ReactionResponse = {
|
|
618
|
+
reacted: boolean;
|
|
619
|
+
reactionsCount: number;
|
|
620
|
+
};
|
|
621
|
+
/**
|
|
622
|
+
* Pagination metadata from API
|
|
623
|
+
*/
|
|
624
|
+
type PaginationMeta = {
|
|
625
|
+
/** Current page (1-indexed) */
|
|
626
|
+
page: number;
|
|
627
|
+
/** Items per page */
|
|
628
|
+
page_size: number;
|
|
629
|
+
/** Total items across all pages */
|
|
630
|
+
total_entries: number;
|
|
631
|
+
/** Total number of pages */
|
|
632
|
+
total_pages: number;
|
|
633
|
+
};
|
|
634
|
+
/**
|
|
635
|
+
* Paginated response wrapper
|
|
636
|
+
*/
|
|
637
|
+
type PaginatedResponse<T> = {
|
|
638
|
+
data: T[];
|
|
639
|
+
meta: PaginationMeta;
|
|
640
|
+
};
|
|
641
|
+
/**
|
|
642
|
+
* Options for listing community posts
|
|
643
|
+
*/
|
|
644
|
+
type ListPostsOptions = {
|
|
645
|
+
/** Filter by category (e.g., "announcements", "general") */
|
|
646
|
+
category?: string;
|
|
647
|
+
/** Page number (1-indexed, default: 1) */
|
|
648
|
+
page?: number;
|
|
649
|
+
/** Items per page (default: 20, max: 100) */
|
|
650
|
+
pageSize?: number;
|
|
651
|
+
/** Viewer ID to include viewer_has_reacted in response */
|
|
652
|
+
viewerId?: string;
|
|
653
|
+
};
|
|
654
|
+
/**
|
|
655
|
+
* Data for creating a community post
|
|
656
|
+
*/
|
|
657
|
+
type CreatePostData = {
|
|
658
|
+
/** Post content (supports markdown) */
|
|
659
|
+
content: string;
|
|
660
|
+
/** Category for the post */
|
|
661
|
+
category?: string;
|
|
662
|
+
};
|
|
663
|
+
/**
|
|
664
|
+
* Data for updating a community post
|
|
665
|
+
*/
|
|
666
|
+
type UpdatePostData = {
|
|
667
|
+
/** Post content (supports markdown) */
|
|
668
|
+
content?: string;
|
|
669
|
+
/** Category for the post */
|
|
670
|
+
category?: string;
|
|
671
|
+
};
|
|
672
|
+
/**
|
|
673
|
+
* Data for creating a comment
|
|
674
|
+
*/
|
|
675
|
+
type CreateCommentData = {
|
|
676
|
+
/** Comment content */
|
|
677
|
+
content: string;
|
|
678
|
+
/** Parent comment ID for replies */
|
|
679
|
+
parentId?: string;
|
|
680
|
+
};
|
|
514
681
|
|
|
515
682
|
/**
|
|
516
683
|
* AI client interface for type-safe method overloading
|
|
@@ -693,6 +860,33 @@ declare function createClient(apiKey: string, options?: ClientOptions): {
|
|
|
693
860
|
}>;
|
|
694
861
|
};
|
|
695
862
|
ai: AIClient;
|
|
863
|
+
community: {
|
|
864
|
+
posts: {
|
|
865
|
+
list: (opts?: ListPostsOptions) => Promise<PaginatedResponse<Post>>;
|
|
866
|
+
get: (id: string, viewerId?: string | undefined) => Promise<{
|
|
867
|
+
data: Post;
|
|
868
|
+
}>;
|
|
869
|
+
create: (viewerId: string, data: CreatePostData) => Promise<{
|
|
870
|
+
data: Post;
|
|
871
|
+
}>;
|
|
872
|
+
update: (viewerId: string, id: string, data: UpdatePostData) => Promise<{
|
|
873
|
+
data: Post;
|
|
874
|
+
}>;
|
|
875
|
+
delete: (viewerId: string, id: string) => Promise<{
|
|
876
|
+
data?: unknown;
|
|
877
|
+
}>;
|
|
878
|
+
react: (viewerId: string, id: string) => Promise<ReactionResponse>;
|
|
879
|
+
};
|
|
880
|
+
comments: {
|
|
881
|
+
create: (viewerId: string, postId: string, data: CreateCommentData) => Promise<{
|
|
882
|
+
data: Comment;
|
|
883
|
+
}>;
|
|
884
|
+
delete: (viewerId: string, id: string) => Promise<{
|
|
885
|
+
data?: unknown;
|
|
886
|
+
}>;
|
|
887
|
+
react: (viewerId: string, id: string) => Promise<ReactionResponse>;
|
|
888
|
+
};
|
|
889
|
+
};
|
|
696
890
|
trackEvent: (video: any, event: Event) => void;
|
|
697
891
|
trackPageView: (title: string) => void;
|
|
698
892
|
};
|
|
@@ -706,4 +900,13 @@ declare const DEFAULT_API_BASE_URL = "https://app.boldvideo.io/api/v1/";
|
|
|
706
900
|
*/
|
|
707
901
|
declare const DEFAULT_INTERNAL_API_BASE_URL = "https://app.boldvideo.io/i/v1/";
|
|
708
902
|
|
|
709
|
-
|
|
903
|
+
/**
|
|
904
|
+
* Error thrown by Community API operations
|
|
905
|
+
*/
|
|
906
|
+
declare class CommunityAPIError extends Error {
|
|
907
|
+
readonly status?: number;
|
|
908
|
+
readonly originalError?: Error;
|
|
909
|
+
constructor(method: string, url: string, error: unknown);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
export { AIContextMessage, AIEvent, AIResponse, AIUsage, Account, AccountAI, AnalyticsProvider, AskOptions, AssistantConfig, ChatOptions, Citation, ClientOptions, Comment, CommentSummary, CommentThread, CommunityAPIError, Conversation, ConversationMessage, ConversationMetadata, CreateCommentData, CreatePostData, CreateViewerData, CustomRedirect, DEFAULT_API_BASE_URL, DEFAULT_INTERNAL_API_BASE_URL, ListPostsOptions, ListProgressOptions, ListVideosIndexOptions, ListVideosLatestOptions, ListVideosOptions, MenuItem, PaginatedResponse, PaginationMeta, Playlist, Portal, PortalDisplay, PortalHero, PortalLayout, PortalNavigation, PortalTheme, Post, PostAuthor, ProgressListMeta, ReactionResponse, ReactionSummary, RecommendOptions, RecommendResponse, Recommendation, RecommendationVideo, RecommendationsOptions, RecommendationsResponse, Reply, SaveProgressData, SearchOptions, Segment, Settings, Source, ThemeColors, ThemeConfig, UpdatePostData, UpdateViewerData, UserSummary, Video, VideoAttachment, VideoDownloadUrls, VideoMetadata, VideoSubtitles, VideoTranscript, Viewer, ViewerAPIError, ViewerLookupParams, ViewerProgress, createClient };
|
package/dist/index.js
CHANGED
|
@@ -291,6 +291,188 @@ function saveProgress(client) {
|
|
|
291
291
|
};
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
+
// src/lib/community.ts
|
|
295
|
+
import { AxiosError as AxiosError2 } from "axios";
|
|
296
|
+
var CommunityAPIError = class extends Error {
|
|
297
|
+
constructor(method, url, error) {
|
|
298
|
+
var __super = (...args) => {
|
|
299
|
+
super(...args);
|
|
300
|
+
};
|
|
301
|
+
if (error instanceof AxiosError2) {
|
|
302
|
+
const status = error.response?.status;
|
|
303
|
+
const message = error.response?.data?.error || error.message;
|
|
304
|
+
__super(`${method} ${url} failed (${status}): ${message}`);
|
|
305
|
+
this.status = status;
|
|
306
|
+
this.originalError = error;
|
|
307
|
+
} else if (error instanceof Error) {
|
|
308
|
+
__super(`${method} ${url} failed: ${error.message}`);
|
|
309
|
+
this.originalError = error;
|
|
310
|
+
} else {
|
|
311
|
+
__super(`${method} ${url} failed: ${String(error)}`);
|
|
312
|
+
}
|
|
313
|
+
this.name = "CommunityAPIError";
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
function requireViewerId(viewerId) {
|
|
317
|
+
if (!viewerId)
|
|
318
|
+
throw new Error("Viewer ID is required (X-Viewer-ID)");
|
|
319
|
+
}
|
|
320
|
+
function viewerHeaders(viewerId) {
|
|
321
|
+
return viewerId ? { "X-Viewer-ID": viewerId } : void 0;
|
|
322
|
+
}
|
|
323
|
+
function toQuery2(params) {
|
|
324
|
+
const qs = new URLSearchParams();
|
|
325
|
+
for (const [k, v] of Object.entries(params)) {
|
|
326
|
+
if (v !== void 0 && v !== null)
|
|
327
|
+
qs.set(k, String(v));
|
|
328
|
+
}
|
|
329
|
+
const s = qs.toString();
|
|
330
|
+
return s ? `?${s}` : "";
|
|
331
|
+
}
|
|
332
|
+
async function get3(client, url, viewerId) {
|
|
333
|
+
try {
|
|
334
|
+
const res = await client.get(url, { headers: viewerHeaders(viewerId) });
|
|
335
|
+
return camelizeKeys(res.data);
|
|
336
|
+
} catch (error) {
|
|
337
|
+
throw new CommunityAPIError("GET", url, error);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
async function post2(client, url, data, viewerId) {
|
|
341
|
+
try {
|
|
342
|
+
const res = await client.post(url, data, { headers: viewerHeaders(viewerId) });
|
|
343
|
+
return camelizeKeys(res.data);
|
|
344
|
+
} catch (error) {
|
|
345
|
+
throw new CommunityAPIError("POST", url, error);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
async function put(client, url, data, viewerId) {
|
|
349
|
+
try {
|
|
350
|
+
const res = await client.put(url, data, { headers: viewerHeaders(viewerId) });
|
|
351
|
+
return camelizeKeys(res.data);
|
|
352
|
+
} catch (error) {
|
|
353
|
+
throw new CommunityAPIError("PUT", url, error);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
async function del(client, url, viewerId) {
|
|
357
|
+
try {
|
|
358
|
+
const res = await client.delete(url, { headers: viewerHeaders(viewerId) });
|
|
359
|
+
return camelizeKeys(res.data);
|
|
360
|
+
} catch (error) {
|
|
361
|
+
throw new CommunityAPIError("DELETE", url, error);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function listPosts(client) {
|
|
365
|
+
return async (opts = {}) => {
|
|
366
|
+
return get3(
|
|
367
|
+
client,
|
|
368
|
+
`community/posts${toQuery2({
|
|
369
|
+
category: opts.category,
|
|
370
|
+
page: opts.page,
|
|
371
|
+
page_size: opts.pageSize
|
|
372
|
+
})}`,
|
|
373
|
+
opts.viewerId
|
|
374
|
+
);
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
function getPost(client) {
|
|
378
|
+
return async (id, viewerId) => {
|
|
379
|
+
if (!id)
|
|
380
|
+
throw new Error("Post ID is required");
|
|
381
|
+
return get3(client, `community/posts/${id}`, viewerId);
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
function createPost(client) {
|
|
385
|
+
return async (viewerId, data) => {
|
|
386
|
+
requireViewerId(viewerId);
|
|
387
|
+
if (!data?.content)
|
|
388
|
+
throw new Error("Post content is required");
|
|
389
|
+
return post2(
|
|
390
|
+
client,
|
|
391
|
+
"community/posts",
|
|
392
|
+
{ post: { content: data.content, category: data.category } },
|
|
393
|
+
viewerId
|
|
394
|
+
);
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
function updatePost(client) {
|
|
398
|
+
return async (viewerId, id, data) => {
|
|
399
|
+
requireViewerId(viewerId);
|
|
400
|
+
if (!id)
|
|
401
|
+
throw new Error("Post ID is required");
|
|
402
|
+
const body = {};
|
|
403
|
+
if (data.content !== void 0)
|
|
404
|
+
body.content = data.content;
|
|
405
|
+
if (data.category !== void 0)
|
|
406
|
+
body.category = data.category;
|
|
407
|
+
return put(
|
|
408
|
+
client,
|
|
409
|
+
`community/posts/${id}`,
|
|
410
|
+
{ post: body },
|
|
411
|
+
viewerId
|
|
412
|
+
);
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
function deletePost(client) {
|
|
416
|
+
return async (viewerId, id) => {
|
|
417
|
+
requireViewerId(viewerId);
|
|
418
|
+
if (!id)
|
|
419
|
+
throw new Error("Post ID is required");
|
|
420
|
+
return del(client, `community/posts/${id}`, viewerId);
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
function reactToPost(client) {
|
|
424
|
+
return async (viewerId, id) => {
|
|
425
|
+
requireViewerId(viewerId);
|
|
426
|
+
if (!id)
|
|
427
|
+
throw new Error("Post ID is required");
|
|
428
|
+
return post2(
|
|
429
|
+
client,
|
|
430
|
+
`community/posts/${id}/react`,
|
|
431
|
+
void 0,
|
|
432
|
+
viewerId
|
|
433
|
+
);
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
function createComment(client) {
|
|
437
|
+
return async (viewerId, postId, data) => {
|
|
438
|
+
requireViewerId(viewerId);
|
|
439
|
+
if (!postId)
|
|
440
|
+
throw new Error("Post ID is required");
|
|
441
|
+
if (!data?.content)
|
|
442
|
+
throw new Error("Comment content is required");
|
|
443
|
+
const body = { content: data.content };
|
|
444
|
+
if (data.parentId)
|
|
445
|
+
body.parent_id = data.parentId;
|
|
446
|
+
return post2(
|
|
447
|
+
client,
|
|
448
|
+
`community/posts/${postId}/comments`,
|
|
449
|
+
{ comment: body },
|
|
450
|
+
viewerId
|
|
451
|
+
);
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
function deleteComment(client) {
|
|
455
|
+
return async (viewerId, id) => {
|
|
456
|
+
requireViewerId(viewerId);
|
|
457
|
+
if (!id)
|
|
458
|
+
throw new Error("Comment ID is required");
|
|
459
|
+
return del(client, `community/comments/${id}`, viewerId);
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
function reactToComment(client) {
|
|
463
|
+
return async (viewerId, id) => {
|
|
464
|
+
requireViewerId(viewerId);
|
|
465
|
+
if (!id)
|
|
466
|
+
throw new Error("Comment ID is required");
|
|
467
|
+
return post2(
|
|
468
|
+
client,
|
|
469
|
+
`community/comments/${id}/react`,
|
|
470
|
+
void 0,
|
|
471
|
+
viewerId
|
|
472
|
+
);
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
294
476
|
// src/util/throttle.ts
|
|
295
477
|
var throttle = (fn, delay) => {
|
|
296
478
|
let wait = false;
|
|
@@ -624,11 +806,27 @@ function createClient(apiKey, options = {}) {
|
|
|
624
806
|
saveProgress: saveProgress(apiClient)
|
|
625
807
|
},
|
|
626
808
|
ai: createAI(aiConfig),
|
|
809
|
+
community: {
|
|
810
|
+
posts: {
|
|
811
|
+
list: listPosts(apiClient),
|
|
812
|
+
get: getPost(apiClient),
|
|
813
|
+
create: createPost(apiClient),
|
|
814
|
+
update: updatePost(apiClient),
|
|
815
|
+
delete: deletePost(apiClient),
|
|
816
|
+
react: reactToPost(apiClient)
|
|
817
|
+
},
|
|
818
|
+
comments: {
|
|
819
|
+
create: createComment(apiClient),
|
|
820
|
+
delete: deleteComment(apiClient),
|
|
821
|
+
react: reactToComment(apiClient)
|
|
822
|
+
}
|
|
823
|
+
},
|
|
627
824
|
trackEvent: trackEvent(apiClient, userId, { debug }),
|
|
628
825
|
trackPageView: trackPageView(apiClient, userId, { debug })
|
|
629
826
|
};
|
|
630
827
|
}
|
|
631
828
|
export {
|
|
829
|
+
CommunityAPIError,
|
|
632
830
|
DEFAULT_API_BASE_URL,
|
|
633
831
|
DEFAULT_INTERNAL_API_BASE_URL,
|
|
634
832
|
ViewerAPIError,
|
package/llms.txt
CHANGED
|
@@ -111,6 +111,43 @@ const { data: progress, meta } = await bold.viewers.listProgress(viewer.id, { co
|
|
|
111
111
|
console.log(`Completed ${meta.completed} of ${meta.total} videos`);
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
+
## Community API
|
|
115
|
+
|
|
116
|
+
Build community features with posts, comments, and reactions. Write operations require `viewerId`.
|
|
117
|
+
|
|
118
|
+
### Posts
|
|
119
|
+
|
|
120
|
+
- `bold.community.posts.list(opts?)` - List posts with pagination (returns `PaginatedResponse<Post>`)
|
|
121
|
+
- `bold.community.posts.get(id, viewerId?)` - Get post with full comments
|
|
122
|
+
- `bold.community.posts.create(viewerId, data)` - Create a post
|
|
123
|
+
- `bold.community.posts.update(viewerId, id, data)` - Update post (owner/admin)
|
|
124
|
+
- `bold.community.posts.delete(viewerId, id)` - Delete post (owner/admin)
|
|
125
|
+
- `bold.community.posts.react(viewerId, id)` - Toggle reaction
|
|
126
|
+
|
|
127
|
+
### Comments
|
|
128
|
+
|
|
129
|
+
- `bold.community.comments.create(viewerId, postId, data)` - Create comment
|
|
130
|
+
- `bold.community.comments.delete(viewerId, id)` - Delete comment (owner/admin)
|
|
131
|
+
- `bold.community.comments.react(viewerId, id)` - Toggle reaction
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
// Example: List posts with pagination
|
|
135
|
+
const response = await bold.community.posts.list({ page: 1, pageSize: 20, viewerId });
|
|
136
|
+
// response.data = Post[]
|
|
137
|
+
// response.meta = { page, page_size, total_entries, total_pages }
|
|
138
|
+
|
|
139
|
+
// Access users who reacted/commented (for avatar display)
|
|
140
|
+
const post = response.data[0];
|
|
141
|
+
post.reactions.reacted_by; // UserSummary[] (up to 10)
|
|
142
|
+
post.comments.commented_by; // UserSummary[] (up to 10)
|
|
143
|
+
post.reactions.viewer_has_reacted; // boolean (when viewerId provided)
|
|
144
|
+
|
|
145
|
+
// Create and interact
|
|
146
|
+
await bold.community.posts.create(viewerId, { content: 'Hello!', category: 'general' });
|
|
147
|
+
await bold.community.posts.react(viewerId, post.id);
|
|
148
|
+
await bold.community.comments.create(viewerId, post.id, { content: 'Great post!' });
|
|
149
|
+
```
|
|
150
|
+
|
|
114
151
|
## AI Methods
|
|
115
152
|
|
|
116
153
|
All AI methods return `AsyncIterable<AIEvent>` (streaming) or `Promise<AIResponse>` (non-streaming).
|
|
@@ -275,9 +312,94 @@ type AIEvent =
|
|
|
275
312
|
}
|
|
276
313
|
```
|
|
277
314
|
|
|
315
|
+
### UserSummary
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
{
|
|
319
|
+
id: string;
|
|
320
|
+
name: string;
|
|
321
|
+
avatar_url: string | null;
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Post
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
{
|
|
329
|
+
id: string;
|
|
330
|
+
content: string; // Markdown content
|
|
331
|
+
created_at: string;
|
|
332
|
+
author: PostAuthor; // Creator of the post
|
|
333
|
+
reactions: ReactionSummary; // { count, reacted_by, viewer_has_reacted? }
|
|
334
|
+
comments: CommentSummary; // { count, commented_by, items? }
|
|
335
|
+
category?: string;
|
|
336
|
+
pinned?: boolean;
|
|
337
|
+
pinnedAt?: string | null;
|
|
338
|
+
updatedAt?: string;
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### ReactionSummary
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
{
|
|
346
|
+
count: number;
|
|
347
|
+
reacted_by: UserSummary[]; // Capped to 10 from API
|
|
348
|
+
viewer_has_reacted?: boolean; // Present when viewerId provided
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### CommentSummary
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
{
|
|
356
|
+
count: number;
|
|
357
|
+
commented_by: UserSummary[]; // Capped to 10 from API
|
|
358
|
+
items?: CommentThread[]; // Full threads (detail view only)
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### CommentThread
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
{
|
|
366
|
+
id: string;
|
|
367
|
+
content: string;
|
|
368
|
+
created_at: string;
|
|
369
|
+
author: UserSummary;
|
|
370
|
+
replies: Reply[]; // Nested replies
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Reply
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
{
|
|
378
|
+
id: string;
|
|
379
|
+
content: string;
|
|
380
|
+
created_at: string;
|
|
381
|
+
author: UserSummary;
|
|
382
|
+
parent_comment_id: string;
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### PaginatedResponse<T>
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
{
|
|
390
|
+
data: T[];
|
|
391
|
+
meta: {
|
|
392
|
+
page: number; // Current page (1-indexed)
|
|
393
|
+
page_size: number;
|
|
394
|
+
total_entries: number;
|
|
395
|
+
total_pages: number;
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
278
400
|
### Other types exported
|
|
279
401
|
|
|
280
|
-
`Playlist`, `Settings`, `Portal`, `MenuItem`, `AIResponse`, `AIUsage`, `AIContextMessage`, `Conversation`, `ConversationMessage`, `RecommendationsResponse`, `Viewer`, `ViewerProgress`, `ViewerLookupParams`, `ListProgressOptions`, `ListVideosOptions`, `ListVideosLatestOptions`, `ListVideosIndexOptions`
|
|
402
|
+
`Playlist`, `Settings`, `Portal`, `MenuItem`, `AIResponse`, `AIUsage`, `AIContextMessage`, `Conversation`, `ConversationMessage`, `RecommendationsResponse`, `Viewer`, `ViewerProgress`, `ViewerLookupParams`, `ListProgressOptions`, `ListVideosOptions`, `ListVideosLatestOptions`, `ListVideosIndexOptions`, `Post`, `PostAuthor`, `UserSummary`, `ReactionSummary`, `CommentSummary`, `CommentThread`, `Reply`, `Comment`, `ReactionResponse`, `ListPostsOptions`, `CreatePostData`, `UpdatePostData`, `CreateCommentData`, `PaginationMeta`, `PaginatedResponse`, `CommunityAPIError`
|
|
281
403
|
|
|
282
404
|
## Error Handling
|
|
283
405
|
|