@boldvideo/bold-js 1.17.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 +29 -0
- package/README.md +73 -1
- package/dist/index.cjs +201 -2
- package/dist/index.d.ts +131 -1
- package/dist/index.js +198 -0
- package/llms.txt +63 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
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
|
+
|
|
3
32
|
## 1.17.0
|
|
4
33
|
|
|
5
34
|
### 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
|
+
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
|
+
|
|
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,98 @@ type ListVideosOptions = (ListVideosLatestOptions & {
|
|
|
511
511
|
limit?: never;
|
|
512
512
|
viewerId?: never;
|
|
513
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
|
+
};
|
|
514
606
|
|
|
515
607
|
/**
|
|
516
608
|
* AI client interface for type-safe method overloading
|
|
@@ -693,6 +785,35 @@ declare function createClient(apiKey: string, options?: ClientOptions): {
|
|
|
693
785
|
}>;
|
|
694
786
|
};
|
|
695
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
|
+
};
|
|
696
817
|
trackEvent: (video: any, event: Event) => void;
|
|
697
818
|
trackPageView: (title: string) => void;
|
|
698
819
|
};
|
|
@@ -706,4 +827,13 @@ declare const DEFAULT_API_BASE_URL = "https://app.boldvideo.io/api/v1/";
|
|
|
706
827
|
*/
|
|
707
828
|
declare const DEFAULT_INTERNAL_API_BASE_URL = "https://app.boldvideo.io/i/v1/";
|
|
708
829
|
|
|
709
|
-
|
|
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
|
@@ -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
|
+
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
|
+
|
|
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,33 @@ 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 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
|
+
|
|
114
141
|
## AI Methods
|
|
115
142
|
|
|
116
143
|
All AI methods return `AsyncIterable<AIEvent>` (streaming) or `Promise<AIResponse>` (non-streaming).
|
|
@@ -275,9 +302,44 @@ type AIEvent =
|
|
|
275
302
|
}
|
|
276
303
|
```
|
|
277
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
|
+
|
|
278
340
|
### Other types exported
|
|
279
341
|
|
|
280
|
-
`Playlist`, `Settings`, `Portal`, `MenuItem`, `AIResponse`, `AIUsage`, `AIContextMessage`, `Conversation`, `ConversationMessage`, `RecommendationsResponse`, `Viewer`, `ViewerProgress`, `ViewerLookupParams`, `ListProgressOptions`, `ListVideosOptions`, `ListVideosLatestOptions`, `ListVideosIndexOptions`
|
|
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`
|
|
281
343
|
|
|
282
344
|
## Error Handling
|
|
283
345
|
|