@boldvideo/bold-js 1.16.0 → 1.18.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 +56 -0
- package/README.md +95 -2
- package/dist/index.cjs +243 -5
- package/dist/index.d.ts +170 -2
- package/dist/index.js +240 -3
- package/llms.txt +87 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,61 @@
|
|
|
1
1
|
# @boldvideo/bold-js
|
|
2
2
|
|
|
3
|
+
## 1.18.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 48d8b40: Add Community API for posts, comments, and reactions
|
|
8
|
+
|
|
9
|
+
New `bold.community` namespace with full CRUD operations:
|
|
10
|
+
|
|
11
|
+
**Posts:**
|
|
12
|
+
|
|
13
|
+
- `bold.community.posts.list(opts?)` - List posts with category/limit/offset filters
|
|
14
|
+
- `bold.community.posts.get(id, viewerId?)` - Get single post with comments
|
|
15
|
+
- `bold.community.posts.create(viewerId, data)` - Create a post (markdown supported)
|
|
16
|
+
- `bold.community.posts.update(viewerId, id, data)` - Update post (owner/admin only)
|
|
17
|
+
- `bold.community.posts.delete(viewerId, id)` - Delete post (owner/admin only)
|
|
18
|
+
- `bold.community.posts.react(viewerId, id)` - Toggle reaction (like/unlike)
|
|
19
|
+
|
|
20
|
+
**Comments:**
|
|
21
|
+
|
|
22
|
+
- `bold.community.comments.create(viewerId, postId, data)` - Create comment with optional `parentId` for nested replies
|
|
23
|
+
- `bold.community.comments.delete(viewerId, id)` - Delete comment (owner/admin only)
|
|
24
|
+
- `bold.community.comments.react(viewerId, id)` - Toggle reaction
|
|
25
|
+
|
|
26
|
+
**New Types:**
|
|
27
|
+
|
|
28
|
+
- `Post`, `PostAuthor`, `Comment`, `ReactionResponse`
|
|
29
|
+
- `ListPostsOptions`, `CreatePostData`, `UpdatePostData`, `CreateCommentData`
|
|
30
|
+
- `CommunityAPIError` - Error class for Community API operations
|
|
31
|
+
|
|
32
|
+
## 1.17.0
|
|
33
|
+
|
|
34
|
+
### Minor Changes
|
|
35
|
+
|
|
36
|
+
- 563730d: Add full options support to `videos.list()` for tag, collectionId, viewerId, and page filtering
|
|
37
|
+
|
|
38
|
+
**New features:**
|
|
39
|
+
|
|
40
|
+
- Filter videos by `tag` and `collectionId` on both endpoints
|
|
41
|
+
- Include watch progress with `viewerId` parameter
|
|
42
|
+
- Access paginated `/videos` endpoint using `page` parameter
|
|
43
|
+
|
|
44
|
+
**Usage:**
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// Latest videos with filters
|
|
48
|
+
await bold.videos.list({ limit: 20, tag: "sales", collectionId: "col_123" });
|
|
49
|
+
|
|
50
|
+
// Include viewer watch progress
|
|
51
|
+
await bold.videos.list({ limit: 20, viewerId: "viewer_123" });
|
|
52
|
+
|
|
53
|
+
// Paginated index (uses /videos endpoint)
|
|
54
|
+
await bold.videos.list({ page: 2, tag: "sales" });
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Breaking:** None. Existing `bold.videos.list()` and `bold.videos.list(12)` calls work unchanged.
|
|
58
|
+
|
|
3
59
|
## 1.16.0
|
|
4
60
|
|
|
5
61
|
### Minor 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');
|
|
@@ -160,6 +178,69 @@ console.log(`Completed ${meta.completed} of ${meta.total} videos`);
|
|
|
160
178
|
|
|
161
179
|
---
|
|
162
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
|
+
|
|
163
244
|
## AI Methods
|
|
164
245
|
|
|
165
246
|
All AI methods support both streaming (default) and non-streaming modes.
|
|
@@ -357,7 +438,19 @@ import type {
|
|
|
357
438
|
Viewer,
|
|
358
439
|
ViewerProgress,
|
|
359
440
|
ViewerLookupParams,
|
|
360
|
-
ListProgressOptions
|
|
441
|
+
ListProgressOptions,
|
|
442
|
+
ListVideosOptions,
|
|
443
|
+
ListVideosLatestOptions,
|
|
444
|
+
ListVideosIndexOptions,
|
|
445
|
+
// Community API
|
|
446
|
+
Post,
|
|
447
|
+
PostAuthor,
|
|
448
|
+
Comment,
|
|
449
|
+
ReactionResponse,
|
|
450
|
+
ListPostsOptions,
|
|
451
|
+
CreatePostData,
|
|
452
|
+
UpdatePostData,
|
|
453
|
+
CreateCommentData
|
|
361
454
|
} from '@boldvideo/bold-js';
|
|
362
455
|
```
|
|
363
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);
|
|
@@ -64,6 +65,15 @@ function camelizeKeys(input, options = {}) {
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
// src/lib/fetchers.ts
|
|
68
|
+
function toQuery(params) {
|
|
69
|
+
const qs = new URLSearchParams();
|
|
70
|
+
for (const [k, v] of Object.entries(params)) {
|
|
71
|
+
if (v !== void 0 && v !== null)
|
|
72
|
+
qs.set(k, String(v));
|
|
73
|
+
}
|
|
74
|
+
const s = qs.toString();
|
|
75
|
+
return s ? `?${s}` : "";
|
|
76
|
+
}
|
|
67
77
|
async function get(client, url) {
|
|
68
78
|
try {
|
|
69
79
|
const res = await client.get(url);
|
|
@@ -90,14 +100,44 @@ function fetchSettings(client) {
|
|
|
90
100
|
};
|
|
91
101
|
}
|
|
92
102
|
function fetchVideos(client) {
|
|
93
|
-
return async (
|
|
103
|
+
return async (arg = 12) => {
|
|
94
104
|
try {
|
|
105
|
+
if (typeof arg === "number") {
|
|
106
|
+
return await get(
|
|
107
|
+
client,
|
|
108
|
+
`videos/latest${toQuery({ limit: arg })}`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
const opts = arg;
|
|
112
|
+
const hasPage = "page" in opts && opts.page !== void 0;
|
|
113
|
+
if (hasPage && ("limit" in opts || "viewerId" in opts)) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
"videos.list(): cannot use `page` with `limit` or `viewerId` (these belong to different endpoints)"
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
if (hasPage) {
|
|
119
|
+
const { page, tag: tag2, collectionId: collectionId2 } = opts;
|
|
120
|
+
return await get(
|
|
121
|
+
client,
|
|
122
|
+
`videos${toQuery({
|
|
123
|
+
page,
|
|
124
|
+
tag: tag2,
|
|
125
|
+
collection_id: collectionId2
|
|
126
|
+
})}`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
const { limit, tag, collectionId, viewerId } = opts;
|
|
95
130
|
return await get(
|
|
96
131
|
client,
|
|
97
|
-
`videos/latest
|
|
132
|
+
`videos/latest${toQuery({
|
|
133
|
+
limit: limit ?? 12,
|
|
134
|
+
tag,
|
|
135
|
+
collection_id: collectionId,
|
|
136
|
+
viewer_id: viewerId
|
|
137
|
+
})}`
|
|
98
138
|
);
|
|
99
139
|
} catch (error) {
|
|
100
|
-
console.error(`Error fetching videos
|
|
140
|
+
console.error(`Error fetching videos`, error);
|
|
101
141
|
throw error;
|
|
102
142
|
}
|
|
103
143
|
};
|
|
@@ -291,6 +331,188 @@ function saveProgress(client) {
|
|
|
291
331
|
};
|
|
292
332
|
}
|
|
293
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
|
+
limit: opts.limit,
|
|
411
|
+
offset: opts.offset
|
|
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
|
+
|
|
294
516
|
// src/util/throttle.ts
|
|
295
517
|
var throttle = (fn, delay) => {
|
|
296
518
|
let wait = false;
|
|
@@ -592,7 +814,7 @@ function createClient(apiKey, options = {}) {
|
|
|
592
814
|
};
|
|
593
815
|
let apiClient;
|
|
594
816
|
try {
|
|
595
|
-
apiClient =
|
|
817
|
+
apiClient = import_axios3.default.create(apiClientOptions);
|
|
596
818
|
} catch (error) {
|
|
597
819
|
console.error("Error creating API client", error);
|
|
598
820
|
throw error;
|
|
@@ -624,12 +846,28 @@ function createClient(apiKey, options = {}) {
|
|
|
624
846
|
saveProgress: saveProgress(apiClient)
|
|
625
847
|
},
|
|
626
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
|
+
},
|
|
627
864
|
trackEvent: trackEvent(apiClient, userId, { debug }),
|
|
628
865
|
trackPageView: trackPageView(apiClient, userId, { debug })
|
|
629
866
|
};
|
|
630
867
|
}
|
|
631
868
|
// Annotate the CommonJS export names for ESM import in node:
|
|
632
869
|
0 && (module.exports = {
|
|
870
|
+
CommunityAPIError,
|
|
633
871
|
DEFAULT_API_BASE_URL,
|
|
634
872
|
DEFAULT_INTERNAL_API_BASE_URL,
|
|
635
873
|
ViewerAPIError,
|
package/dist/index.d.ts
CHANGED
|
@@ -473,6 +473,136 @@ type ProgressListMeta = {
|
|
|
473
473
|
/** Number of in-progress videos */
|
|
474
474
|
inProgress: number;
|
|
475
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
|
+
});
|
|
514
|
+
/**
|
|
515
|
+
* Author information for posts and comments
|
|
516
|
+
*/
|
|
517
|
+
type PostAuthor = {
|
|
518
|
+
id: string;
|
|
519
|
+
name: string;
|
|
520
|
+
email?: string;
|
|
521
|
+
isAdmin: boolean;
|
|
522
|
+
};
|
|
523
|
+
/**
|
|
524
|
+
* A comment on a community post
|
|
525
|
+
*/
|
|
526
|
+
type Comment = {
|
|
527
|
+
id: string;
|
|
528
|
+
content: string;
|
|
529
|
+
/** Nesting depth (0 = top-level) */
|
|
530
|
+
depth: number;
|
|
531
|
+
reactionsCount: number;
|
|
532
|
+
/** Whether current viewer has reacted (only present when X-Viewer-ID header sent) */
|
|
533
|
+
viewerReacted?: boolean;
|
|
534
|
+
viewer: PostAuthor;
|
|
535
|
+
/** Nested replies */
|
|
536
|
+
replies: Comment[];
|
|
537
|
+
createdAt: string;
|
|
538
|
+
updatedAt: string;
|
|
539
|
+
};
|
|
540
|
+
/**
|
|
541
|
+
* A community post
|
|
542
|
+
*/
|
|
543
|
+
type Post = {
|
|
544
|
+
id: string;
|
|
545
|
+
content: string;
|
|
546
|
+
category: string;
|
|
547
|
+
pinned: boolean;
|
|
548
|
+
pinnedAt?: string | null;
|
|
549
|
+
reactionsCount: number;
|
|
550
|
+
commentsCount: number;
|
|
551
|
+
/** Whether current viewer has reacted (only present when X-Viewer-ID header sent) */
|
|
552
|
+
viewerReacted?: boolean;
|
|
553
|
+
viewer: PostAuthor;
|
|
554
|
+
/** Comments (only present on single post fetch) */
|
|
555
|
+
comments?: Comment[];
|
|
556
|
+
createdAt: string;
|
|
557
|
+
updatedAt: string;
|
|
558
|
+
};
|
|
559
|
+
/**
|
|
560
|
+
* Response from react endpoints (toggle)
|
|
561
|
+
*/
|
|
562
|
+
type ReactionResponse = {
|
|
563
|
+
reacted: boolean;
|
|
564
|
+
reactionsCount: number;
|
|
565
|
+
};
|
|
566
|
+
/**
|
|
567
|
+
* Options for listing community posts
|
|
568
|
+
*/
|
|
569
|
+
type ListPostsOptions = {
|
|
570
|
+
/** Filter by category (e.g., "announcements", "general") */
|
|
571
|
+
category?: string;
|
|
572
|
+
/** Max posts to return (default: 20) */
|
|
573
|
+
limit?: number;
|
|
574
|
+
/** Pagination offset */
|
|
575
|
+
offset?: number;
|
|
576
|
+
/** Viewer ID to include viewerReacted in response */
|
|
577
|
+
viewerId?: string;
|
|
578
|
+
};
|
|
579
|
+
/**
|
|
580
|
+
* Data for creating a community post
|
|
581
|
+
*/
|
|
582
|
+
type CreatePostData = {
|
|
583
|
+
/** Post content (supports markdown) */
|
|
584
|
+
content: string;
|
|
585
|
+
/** Category for the post */
|
|
586
|
+
category?: string;
|
|
587
|
+
};
|
|
588
|
+
/**
|
|
589
|
+
* Data for updating a community post
|
|
590
|
+
*/
|
|
591
|
+
type UpdatePostData = {
|
|
592
|
+
/** Post content (supports markdown) */
|
|
593
|
+
content?: string;
|
|
594
|
+
/** Category for the post */
|
|
595
|
+
category?: string;
|
|
596
|
+
};
|
|
597
|
+
/**
|
|
598
|
+
* Data for creating a comment
|
|
599
|
+
*/
|
|
600
|
+
type CreateCommentData = {
|
|
601
|
+
/** Comment content */
|
|
602
|
+
content: string;
|
|
603
|
+
/** Parent comment ID for replies */
|
|
604
|
+
parentId?: string;
|
|
605
|
+
};
|
|
476
606
|
|
|
477
607
|
/**
|
|
478
608
|
* AI client interface for type-safe method overloading
|
|
@@ -609,7 +739,7 @@ declare function createClient(apiKey: string, options?: ClientOptions): {
|
|
|
609
739
|
data: Settings;
|
|
610
740
|
}>;
|
|
611
741
|
videos: {
|
|
612
|
-
list: (
|
|
742
|
+
list: (arg?: number | ListVideosOptions) => Promise<{
|
|
613
743
|
data: Video[];
|
|
614
744
|
}>;
|
|
615
745
|
get: (id: string) => Promise<{
|
|
@@ -655,6 +785,35 @@ declare function createClient(apiKey: string, options?: ClientOptions): {
|
|
|
655
785
|
}>;
|
|
656
786
|
};
|
|
657
787
|
ai: AIClient;
|
|
788
|
+
community: {
|
|
789
|
+
posts: {
|
|
790
|
+
list: (opts?: ListPostsOptions) => Promise<{
|
|
791
|
+
data: Post[];
|
|
792
|
+
}>;
|
|
793
|
+
get: (id: string, viewerId?: string | undefined) => Promise<{
|
|
794
|
+
data: Post;
|
|
795
|
+
}>;
|
|
796
|
+
create: (viewerId: string, data: CreatePostData) => Promise<{
|
|
797
|
+
data: Post;
|
|
798
|
+
}>;
|
|
799
|
+
update: (viewerId: string, id: string, data: UpdatePostData) => Promise<{
|
|
800
|
+
data: Post;
|
|
801
|
+
}>;
|
|
802
|
+
delete: (viewerId: string, id: string) => Promise<{
|
|
803
|
+
data?: unknown;
|
|
804
|
+
}>;
|
|
805
|
+
react: (viewerId: string, id: string) => Promise<ReactionResponse>;
|
|
806
|
+
};
|
|
807
|
+
comments: {
|
|
808
|
+
create: (viewerId: string, postId: string, data: CreateCommentData) => Promise<{
|
|
809
|
+
data: Comment;
|
|
810
|
+
}>;
|
|
811
|
+
delete: (viewerId: string, id: string) => Promise<{
|
|
812
|
+
data?: unknown;
|
|
813
|
+
}>;
|
|
814
|
+
react: (viewerId: string, id: string) => Promise<ReactionResponse>;
|
|
815
|
+
};
|
|
816
|
+
};
|
|
658
817
|
trackEvent: (video: any, event: Event) => void;
|
|
659
818
|
trackPageView: (title: string) => void;
|
|
660
819
|
};
|
|
@@ -668,4 +827,13 @@ declare const DEFAULT_API_BASE_URL = "https://app.boldvideo.io/api/v1/";
|
|
|
668
827
|
*/
|
|
669
828
|
declare const DEFAULT_INTERNAL_API_BASE_URL = "https://app.boldvideo.io/i/v1/";
|
|
670
829
|
|
|
671
|
-
|
|
830
|
+
/**
|
|
831
|
+
* Error thrown by Community API operations
|
|
832
|
+
*/
|
|
833
|
+
declare class CommunityAPIError extends Error {
|
|
834
|
+
readonly status?: number;
|
|
835
|
+
readonly originalError?: Error;
|
|
836
|
+
constructor(method: string, url: string, error: unknown);
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
export { AIContextMessage, AIEvent, AIResponse, AIUsage, Account, AccountAI, AnalyticsProvider, AskOptions, AssistantConfig, ChatOptions, Citation, ClientOptions, Comment, CommunityAPIError, Conversation, ConversationMessage, ConversationMetadata, CreateCommentData, CreatePostData, CreateViewerData, CustomRedirect, DEFAULT_API_BASE_URL, DEFAULT_INTERNAL_API_BASE_URL, ListPostsOptions, ListProgressOptions, ListVideosIndexOptions, ListVideosLatestOptions, ListVideosOptions, MenuItem, Playlist, Portal, PortalDisplay, PortalHero, PortalLayout, PortalNavigation, PortalTheme, Post, PostAuthor, ProgressListMeta, ReactionResponse, RecommendOptions, RecommendResponse, Recommendation, RecommendationVideo, RecommendationsOptions, RecommendationsResponse, SaveProgressData, SearchOptions, Segment, Settings, Source, ThemeColors, ThemeConfig, UpdatePostData, UpdateViewerData, Video, VideoAttachment, VideoDownloadUrls, VideoMetadata, VideoSubtitles, VideoTranscript, Viewer, ViewerAPIError, ViewerLookupParams, ViewerProgress, createClient };
|
package/dist/index.js
CHANGED
|
@@ -25,6 +25,15 @@ function camelizeKeys(input, options = {}) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
// src/lib/fetchers.ts
|
|
28
|
+
function toQuery(params) {
|
|
29
|
+
const qs = new URLSearchParams();
|
|
30
|
+
for (const [k, v] of Object.entries(params)) {
|
|
31
|
+
if (v !== void 0 && v !== null)
|
|
32
|
+
qs.set(k, String(v));
|
|
33
|
+
}
|
|
34
|
+
const s = qs.toString();
|
|
35
|
+
return s ? `?${s}` : "";
|
|
36
|
+
}
|
|
28
37
|
async function get(client, url) {
|
|
29
38
|
try {
|
|
30
39
|
const res = await client.get(url);
|
|
@@ -51,14 +60,44 @@ function fetchSettings(client) {
|
|
|
51
60
|
};
|
|
52
61
|
}
|
|
53
62
|
function fetchVideos(client) {
|
|
54
|
-
return async (
|
|
63
|
+
return async (arg = 12) => {
|
|
55
64
|
try {
|
|
65
|
+
if (typeof arg === "number") {
|
|
66
|
+
return await get(
|
|
67
|
+
client,
|
|
68
|
+
`videos/latest${toQuery({ limit: arg })}`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
const opts = arg;
|
|
72
|
+
const hasPage = "page" in opts && opts.page !== void 0;
|
|
73
|
+
if (hasPage && ("limit" in opts || "viewerId" in opts)) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
"videos.list(): cannot use `page` with `limit` or `viewerId` (these belong to different endpoints)"
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
if (hasPage) {
|
|
79
|
+
const { page, tag: tag2, collectionId: collectionId2 } = opts;
|
|
80
|
+
return await get(
|
|
81
|
+
client,
|
|
82
|
+
`videos${toQuery({
|
|
83
|
+
page,
|
|
84
|
+
tag: tag2,
|
|
85
|
+
collection_id: collectionId2
|
|
86
|
+
})}`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
const { limit, tag, collectionId, viewerId } = opts;
|
|
56
90
|
return await get(
|
|
57
91
|
client,
|
|
58
|
-
`videos/latest
|
|
92
|
+
`videos/latest${toQuery({
|
|
93
|
+
limit: limit ?? 12,
|
|
94
|
+
tag,
|
|
95
|
+
collection_id: collectionId,
|
|
96
|
+
viewer_id: viewerId
|
|
97
|
+
})}`
|
|
59
98
|
);
|
|
60
99
|
} catch (error) {
|
|
61
|
-
console.error(`Error fetching videos
|
|
100
|
+
console.error(`Error fetching videos`, error);
|
|
62
101
|
throw error;
|
|
63
102
|
}
|
|
64
103
|
};
|
|
@@ -252,6 +291,188 @@ function saveProgress(client) {
|
|
|
252
291
|
};
|
|
253
292
|
}
|
|
254
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
|
+
limit: opts.limit,
|
|
371
|
+
offset: opts.offset
|
|
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
|
+
|
|
255
476
|
// src/util/throttle.ts
|
|
256
477
|
var throttle = (fn, delay) => {
|
|
257
478
|
let wait = false;
|
|
@@ -585,11 +806,27 @@ function createClient(apiKey, options = {}) {
|
|
|
585
806
|
saveProgress: saveProgress(apiClient)
|
|
586
807
|
},
|
|
587
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
|
+
},
|
|
588
824
|
trackEvent: trackEvent(apiClient, userId, { debug }),
|
|
589
825
|
trackPageView: trackPageView(apiClient, userId, { debug })
|
|
590
826
|
};
|
|
591
827
|
}
|
|
592
828
|
export {
|
|
829
|
+
CommunityAPIError,
|
|
593
830
|
DEFAULT_API_BASE_URL,
|
|
594
831
|
DEFAULT_INTERNAL_API_BASE_URL,
|
|
595
832
|
ViewerAPIError,
|
package/llms.txt
CHANGED
|
@@ -55,12 +55,35 @@ interface ClientOptions {
|
|
|
55
55
|
All content methods return `Promise<{ data: T }>`.
|
|
56
56
|
|
|
57
57
|
- `bold.settings(videoLimit?)` - Channel settings, menus, featured playlists
|
|
58
|
-
- `bold.videos.list(
|
|
58
|
+
- `bold.videos.list()` - List latest videos (default limit: 12)
|
|
59
|
+
- `bold.videos.list(limit)` - List with numeric limit (backwards compatible)
|
|
60
|
+
- `bold.videos.list(options)` - List with full options (see ListVideosOptions)
|
|
59
61
|
- `bold.videos.get(id)` - Get video by ID or slug
|
|
60
62
|
- `bold.videos.search(query)` - Search videos
|
|
61
63
|
- `bold.playlists.list()` - List playlists
|
|
62
64
|
- `bold.playlists.get(id)` - Get playlist with videos
|
|
63
65
|
|
|
66
|
+
### ListVideosOptions
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// For /videos/latest endpoint (default)
|
|
70
|
+
{
|
|
71
|
+
limit?: number; // Max videos (default: 12)
|
|
72
|
+
tag?: string; // Filter by tag
|
|
73
|
+
collectionId?: string; // Filter to collection
|
|
74
|
+
viewerId?: string; // Include watch progress
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// For /videos (index) endpoint - use when you need pagination
|
|
78
|
+
{
|
|
79
|
+
page?: number; // Page number
|
|
80
|
+
tag?: string;
|
|
81
|
+
collectionId?: string;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Note: If `page` is provided, SDK uses `/videos` (index); otherwise uses `/videos/latest`.
|
|
86
|
+
|
|
64
87
|
## Viewers API
|
|
65
88
|
|
|
66
89
|
Manage external users and track video progress. Returns `Promise<{ data: T }>` (consistent with other SDK methods).
|
|
@@ -88,6 +111,33 @@ const { data: progress, meta } = await bold.viewers.listProgress(viewer.id, { co
|
|
|
88
111
|
console.log(`Completed ${meta.completed} of ${meta.total} videos`);
|
|
89
112
|
```
|
|
90
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 optional filters
|
|
121
|
+
- `bold.community.posts.get(id, viewerId?)` - Get post with 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
|
|
135
|
+
const { data: posts } = await bold.community.posts.list({ category: 'general', limit: 20 });
|
|
136
|
+
await bold.community.posts.create(viewerId, { content: 'Hello!', category: 'general' });
|
|
137
|
+
await bold.community.posts.react(viewerId, posts[0].id);
|
|
138
|
+
await bold.community.comments.create(viewerId, posts[0].id, { content: 'Great post!' });
|
|
139
|
+
```
|
|
140
|
+
|
|
91
141
|
## AI Methods
|
|
92
142
|
|
|
93
143
|
All AI methods return `AsyncIterable<AIEvent>` (streaming) or `Promise<AIResponse>` (non-streaming).
|
|
@@ -252,9 +302,44 @@ type AIEvent =
|
|
|
252
302
|
}
|
|
253
303
|
```
|
|
254
304
|
|
|
305
|
+
### Post
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
{
|
|
309
|
+
id: string;
|
|
310
|
+
content: string; // Markdown content
|
|
311
|
+
category: string;
|
|
312
|
+
pinned: boolean;
|
|
313
|
+
pinnedAt?: string | null;
|
|
314
|
+
reactionsCount: number;
|
|
315
|
+
commentsCount: number;
|
|
316
|
+
viewerReacted?: boolean; // Only with X-Viewer-ID
|
|
317
|
+
viewer: PostAuthor;
|
|
318
|
+
comments?: Comment[]; // Only on single post fetch
|
|
319
|
+
createdAt: string;
|
|
320
|
+
updatedAt: string;
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Comment
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
{
|
|
328
|
+
id: string;
|
|
329
|
+
content: string;
|
|
330
|
+
depth: number; // Nesting level (0 = top-level)
|
|
331
|
+
reactionsCount: number;
|
|
332
|
+
viewerReacted?: boolean;
|
|
333
|
+
viewer: PostAuthor;
|
|
334
|
+
replies: Comment[]; // Nested replies
|
|
335
|
+
createdAt: string;
|
|
336
|
+
updatedAt: string;
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
255
340
|
### Other types exported
|
|
256
341
|
|
|
257
|
-
`Playlist`, `Settings`, `Portal`, `MenuItem`, `AIResponse`, `AIUsage`, `AIContextMessage`, `Conversation`, `ConversationMessage`, `RecommendationsResponse`, `Viewer`, `ViewerProgress`, `ViewerLookupParams`, `ListProgressOptions`
|
|
342
|
+
`Playlist`, `Settings`, `Portal`, `MenuItem`, `AIResponse`, `AIUsage`, `AIContextMessage`, `Conversation`, `ConversationMessage`, `RecommendationsResponse`, `Viewer`, `ViewerProgress`, `ViewerLookupParams`, `ListProgressOptions`, `ListVideosOptions`, `ListVideosLatestOptions`, `ListVideosIndexOptions`, `Post`, `PostAuthor`, `Comment`, `ReactionResponse`, `ListPostsOptions`, `CreatePostData`, `UpdatePostData`, `CreateCommentData`, `CommunityAPIError`
|
|
258
343
|
|
|
259
344
|
## Error Handling
|
|
260
345
|
|