@businessdash/sdk 0.9.60 → 0.9.80
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/README.md +991 -295
- package/assets/biab-sw.js +92 -0
- package/dist/account-core/index.d.ts +126 -0
- package/dist/account-core/index.d.ts.map +1 -0
- package/dist/account-core/index.js +191 -0
- package/dist/account-core/index.js.map +1 -0
- package/dist/account.d.ts +11 -0
- package/dist/account.d.ts.map +1 -0
- package/dist/account.js +11 -0
- package/dist/account.js.map +1 -0
- package/dist/auth-handler.d.ts +1 -1
- package/dist/auth-links.d.ts +33 -0
- package/dist/auth-links.d.ts.map +1 -0
- package/dist/auth-links.js +64 -0
- package/dist/auth-links.js.map +1 -0
- package/dist/blog-core/index.d.ts +112 -0
- package/dist/blog-core/index.d.ts.map +1 -0
- package/dist/blog-core/index.js +330 -0
- package/dist/blog-core/index.js.map +1 -0
- package/dist/blog.d.ts +11 -0
- package/dist/blog.d.ts.map +1 -0
- package/dist/blog.js +11 -0
- package/dist/blog.js.map +1 -0
- package/dist/chat-core/index.d.ts +97 -0
- package/dist/chat-core/index.d.ts.map +1 -0
- package/dist/chat-core/index.js +237 -0
- package/dist/chat-core/index.js.map +1 -0
- package/dist/chat.d.ts +11 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +11 -0
- package/dist/chat.js.map +1 -0
- package/dist/checkout-core/index.d.ts +65 -0
- package/dist/checkout-core/index.d.ts.map +1 -0
- package/dist/checkout-core/index.js +159 -0
- package/dist/checkout-core/index.js.map +1 -0
- package/dist/checkout.d.ts +10 -0
- package/dist/checkout.d.ts.map +1 -0
- package/dist/checkout.js +10 -0
- package/dist/checkout.js.map +1 -0
- package/dist/cli.js +133 -25
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +622 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +888 -1
- package/dist/client.js.map +1 -1
- package/dist/consent-core/index.cjs +107 -0
- package/dist/consent-core/index.d.ts +103 -0
- package/dist/consent-core/index.d.ts.map +1 -0
- package/dist/consent-core/index.js +157 -0
- package/dist/consent-core/index.js.map +1 -0
- package/dist/contracts.d.ts +3526 -767
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +571 -4
- package/dist/contracts.js.map +1 -1
- package/dist/data-model-schema.d.ts +1 -1
- package/dist/data-model-schema.d.ts.map +1 -1
- package/dist/element.d.ts +30 -0
- package/dist/element.d.ts.map +1 -1
- package/dist/element.js +56 -0
- package/dist/element.js.map +1 -1
- package/dist/embed-protocol.d.ts +86 -0
- package/dist/embed-protocol.d.ts.map +1 -0
- package/dist/embed-protocol.js +60 -0
- package/dist/embed-protocol.js.map +1 -0
- package/dist/framework-support.d.ts +122 -0
- package/dist/framework-support.d.ts.map +1 -0
- package/dist/framework-support.js +323 -0
- package/dist/framework-support.js.map +1 -0
- package/dist/index.cjs +1355 -24
- package/dist/legal-core/index.cjs +115 -0
- package/dist/legal-core/index.d.ts +160 -0
- package/dist/legal-core/index.d.ts.map +1 -0
- package/dist/legal-core/index.js +220 -0
- package/dist/legal-core/index.js.map +1 -0
- package/dist/marketing-core/index.d.ts +66 -0
- package/dist/marketing-core/index.d.ts.map +1 -0
- package/dist/marketing-core/index.js +154 -0
- package/dist/marketing-core/index.js.map +1 -0
- package/dist/marketing.d.ts +7 -0
- package/dist/marketing.d.ts.map +1 -0
- package/dist/marketing.js +7 -0
- package/dist/marketing.js.map +1 -0
- package/dist/mcp-adapters/index.d.ts +135 -0
- package/dist/mcp-adapters/index.d.ts.map +1 -0
- package/dist/mcp-adapters/index.js +187 -0
- package/dist/mcp-adapters/index.js.map +1 -0
- package/dist/mcp-adapters.d.ts +7 -0
- package/dist/mcp-adapters.d.ts.map +1 -0
- package/dist/mcp-adapters.js +7 -0
- package/dist/mcp-adapters.js.map +1 -0
- package/dist/model3d-core/index.cjs +95 -0
- package/dist/model3d-core/index.d.ts +167 -0
- package/dist/model3d-core/index.d.ts.map +1 -0
- package/dist/model3d-core/index.js +158 -0
- package/dist/model3d-core/index.js.map +1 -0
- package/dist/notifications-core/index.cjs +315 -0
- package/dist/notifications-core/index.d.ts +229 -0
- package/dist/notifications-core/index.d.ts.map +1 -0
- package/dist/notifications-core/index.js +445 -0
- package/dist/notifications-core/index.js.map +1 -0
- package/dist/portal-core/index.d.ts +451 -0
- package/dist/portal-core/index.d.ts.map +1 -0
- package/dist/portal-core/index.js +819 -0
- package/dist/portal-core/index.js.map +1 -0
- package/dist/portal.d.ts +7 -0
- package/dist/portal.d.ts.map +1 -0
- package/dist/portal.js +7 -0
- package/dist/portal.js.map +1 -0
- package/dist/push-core/index.cjs +171 -0
- package/dist/push-core/index.d.ts +149 -0
- package/dist/push-core/index.d.ts.map +1 -0
- package/dist/push-core/index.js +231 -0
- package/dist/push-core/index.js.map +1 -0
- package/dist/react-marketing.d.ts +1 -17
- package/dist/react-marketing.d.ts.map +1 -1
- package/dist/react-marketing.js +4 -0
- package/dist/react-marketing.js.map +1 -1
- package/dist/react.d.ts +294 -101
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +302 -246
- package/dist/react.js.map +1 -1
- package/dist/seed-core/index.d.ts +354 -0
- package/dist/seed-core/index.d.ts.map +1 -0
- package/dist/seed-core/index.js +420 -0
- package/dist/seed-core/index.js.map +1 -0
- package/dist/seed.d.ts +7 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +7 -0
- package/dist/seed.js.map +1 -0
- package/dist/seo-core/blog.d.ts +94 -0
- package/dist/seo-core/blog.d.ts.map +1 -0
- package/dist/seo-core/blog.js +149 -0
- package/dist/seo-core/blog.js.map +1 -0
- package/dist/seo-core/commerce.d.ts +64 -0
- package/dist/seo-core/commerce.d.ts.map +1 -0
- package/dist/seo-core/commerce.js +87 -0
- package/dist/seo-core/commerce.js.map +1 -0
- package/dist/seo-core/index.cjs +751 -0
- package/dist/seo-core/index.d.ts +226 -0
- package/dist/seo-core/index.d.ts.map +1 -0
- package/dist/seo-core/index.js +455 -0
- package/dist/seo-core/index.js.map +1 -0
- package/dist/seo-core/local.d.ts +181 -0
- package/dist/seo-core/local.d.ts.map +1 -0
- package/dist/seo-core/local.js +193 -0
- package/dist/seo-core/local.js.map +1 -0
- package/dist/sitemap-core/index.cjs +167 -0
- package/dist/sitemap-core/index.d.ts +155 -0
- package/dist/sitemap-core/index.d.ts.map +1 -0
- package/dist/sitemap-core/index.js +195 -0
- package/dist/sitemap-core/index.js.map +1 -0
- package/dist/social-core/index.d.ts +74 -0
- package/dist/social-core/index.d.ts.map +1 -0
- package/dist/social-core/index.js +72 -0
- package/dist/social-core/index.js.map +1 -0
- package/dist/social.d.ts +7 -0
- package/dist/social.d.ts.map +1 -0
- package/dist/social.js +7 -0
- package/dist/social.js.map +1 -0
- package/dist/solid-compat.d.ts +204 -0
- package/dist/solid-compat.d.ts.map +1 -0
- package/dist/solid-compat.js +221 -0
- package/dist/solid-compat.js.map +1 -0
- package/dist/store-core/index.d.ts +141 -0
- package/dist/store-core/index.d.ts.map +1 -0
- package/dist/store-core/index.js +277 -0
- package/dist/store-core/index.js.map +1 -0
- package/dist/store.d.ts +12 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +12 -0
- package/dist/store.js.map +1 -0
- package/dist/vanilla.d.ts +111 -0
- package/dist/vanilla.d.ts.map +1 -1
- package/dist/vanilla.js +206 -0
- package/dist/vanilla.js.map +1 -1
- package/dist/verify-core/index.d.ts +98 -0
- package/dist/verify-core/index.d.ts.map +1 -0
- package/dist/verify-core/index.js +186 -0
- package/dist/verify-core/index.js.map +1 -0
- package/dist/verify.d.ts +8 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +8 -0
- package/dist/verify.js.map +1 -0
- package/openapi.json +4516 -1311
- package/package.json +128 -11
- package/src/account-core/index.ts +309 -0
- package/src/account.ts +11 -0
- package/src/angular/headless.ts +519 -0
- package/src/angular/index.ts +3 -0
- package/src/auth-links.ts +76 -0
- package/src/blog-core/index.ts +423 -0
- package/src/blog.ts +11 -0
- package/src/chat-core/index.ts +335 -0
- package/src/chat.ts +11 -0
- package/src/checkout-core/index.ts +225 -0
- package/src/checkout.ts +10 -0
- package/src/cli.ts +184 -28
- package/src/client.ts +1097 -0
- package/src/consent-core/index.ts +213 -0
- package/src/contracts.ts +768 -11
- package/src/data-model-schema.ts +1 -1
- package/src/element.ts +102 -0
- package/src/embed-protocol.ts +100 -0
- package/src/framework-support.ts +440 -0
- package/src/legal-core/index.ts +276 -0
- package/src/marketing-core/index.ts +209 -0
- package/src/marketing.ts +7 -0
- package/src/mcp-adapters/index.ts +264 -0
- package/src/mcp-adapters.ts +7 -0
- package/src/model3d-core/index.ts +248 -0
- package/src/notifications-core/index.ts +650 -0
- package/src/portal-core/index.ts +1564 -0
- package/src/portal.ts +7 -0
- package/src/push-core/index.ts +335 -0
- package/src/qwik.tsx +602 -2
- package/src/react-marketing.tsx +5 -0
- package/src/react.tsx +434 -319
- package/src/seed-core/index.ts +735 -0
- package/src/seed.ts +7 -0
- package/src/seo-core/blog.ts +216 -0
- package/src/seo-core/commerce.ts +138 -0
- package/src/seo-core/index.ts +605 -0
- package/src/seo-core/local.ts +333 -0
- package/src/sitemap-core/index.ts +296 -0
- package/src/social-core/index.ts +103 -0
- package/src/social.ts +7 -0
- package/src/solid-compat.tsx +347 -0
- package/src/solid-surfaces.tsx +1311 -0
- package/src/solid.tsx +82 -60
- package/src/store-core/index.ts +426 -0
- package/src/store.ts +12 -0
- package/src/svelte/SocialLinks.svelte +84 -0
- package/src/svelte/headless.ts +435 -0
- package/src/svelte/index.ts +9 -0
- package/src/vanilla.ts +367 -0
- package/src/verify-core/index.ts +288 -0
- package/src/verify.ts +8 -0
- package/src/vue/headless.ts +527 -0
- package/src/vue/index.ts +3 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@businessdash/sdk/blog` — the headless blog controller.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this exists
|
|
5
|
+
*
|
|
6
|
+
* Blog was the last surface that was iframe-only on EVERY binding, React
|
|
7
|
+
* included. `<Blog>` renders a BusinessDash-hosted page in a sandbox, which
|
|
8
|
+
* works but puts our markup inside your site — and for a blog specifically that
|
|
9
|
+
* is the worst place to lose control, because the content is the thing search
|
|
10
|
+
* engines and readers came for and it sits behind an iframe boundary.
|
|
11
|
+
*
|
|
12
|
+
* This is the native path: the post list, a single post, categories, tags,
|
|
13
|
+
* comments and likes as data. You render it, in your own layout, on your own
|
|
14
|
+
* URLs.
|
|
15
|
+
*
|
|
16
|
+
* Same contract as `createFormController`, `createStoreController` and
|
|
17
|
+
* `createChatController`: `snapshot()`, `subscribe()` (which does NOT replay),
|
|
18
|
+
* `destroy()`, and actions.
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
import type { BlogCategory, BlogCommentPostInput, BlogPostBySlugResponse, BlogPublicComment, BlogPublicPost, BlogTagListResponse } from "../contracts.js";
|
|
23
|
+
/** Tags have no standalone contract type; take it from the list response. */
|
|
24
|
+
export type BlogTag = BlogTagListResponse["items"][number];
|
|
25
|
+
/** Structural client surface, so a proxy or a test double works unchanged. */
|
|
26
|
+
export type BlogCoreClient = {
|
|
27
|
+
blog: {
|
|
28
|
+
listPosts(input?: {
|
|
29
|
+
limit?: number;
|
|
30
|
+
cursor?: number | null;
|
|
31
|
+
categoryId?: string;
|
|
32
|
+
}): Promise<{
|
|
33
|
+
items: BlogPublicPost[];
|
|
34
|
+
nextCursor: number | null;
|
|
35
|
+
}>;
|
|
36
|
+
getPost(slug: string): Promise<BlogPostBySlugResponse>;
|
|
37
|
+
listCategories(): Promise<{
|
|
38
|
+
items: BlogCategory[];
|
|
39
|
+
}>;
|
|
40
|
+
listTags(): Promise<{
|
|
41
|
+
items: BlogTag[];
|
|
42
|
+
}>;
|
|
43
|
+
listComments(slug: string, input?: {
|
|
44
|
+
limit?: number;
|
|
45
|
+
cursor?: number | null;
|
|
46
|
+
}): Promise<{
|
|
47
|
+
items: BlogPublicComment[];
|
|
48
|
+
nextCursor: number | null;
|
|
49
|
+
}>;
|
|
50
|
+
postComment(slug: string, input: BlogCommentPostInput): Promise<unknown>;
|
|
51
|
+
likeStatus(slug: string): Promise<{
|
|
52
|
+
liked: boolean;
|
|
53
|
+
likesCount: number;
|
|
54
|
+
}>;
|
|
55
|
+
toggleLike(slug: string): Promise<{
|
|
56
|
+
liked: boolean;
|
|
57
|
+
likesCount: number;
|
|
58
|
+
}>;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
export type BlogStatus = "idle" | "loading" | "ready" | "error";
|
|
62
|
+
/** Field names are STABLE and load-bearing for every framework binding. */
|
|
63
|
+
export type BlogSnapshot = {
|
|
64
|
+
status: BlogStatus;
|
|
65
|
+
posts: readonly BlogPublicPost[];
|
|
66
|
+
categories: readonly BlogCategory[];
|
|
67
|
+
tags: readonly BlogTag[];
|
|
68
|
+
/** The post opened via `openPost()`, if any. */
|
|
69
|
+
activePost: BlogPostBySlugResponse | null;
|
|
70
|
+
/** Comments for `activePost`. Empty until `loadComments()` runs. */
|
|
71
|
+
comments: readonly BlogPublicComment[];
|
|
72
|
+
/** Like state for `activePost`. */
|
|
73
|
+
liked: boolean;
|
|
74
|
+
likesCount: number;
|
|
75
|
+
categoryId: string | null;
|
|
76
|
+
hasMore: boolean;
|
|
77
|
+
hasMoreComments: boolean;
|
|
78
|
+
error: Error | null;
|
|
79
|
+
pending: {
|
|
80
|
+
posts: boolean;
|
|
81
|
+
post: boolean;
|
|
82
|
+
comments: boolean;
|
|
83
|
+
like: boolean;
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
export type BlogController = {
|
|
87
|
+
snapshot(): BlogSnapshot;
|
|
88
|
+
subscribe(callback: (snapshot: BlogSnapshot) => void): () => void;
|
|
89
|
+
/** Fetch the first page of posts, plus categories and tags. */
|
|
90
|
+
load(): Promise<void>;
|
|
91
|
+
loadMore(): Promise<void>;
|
|
92
|
+
filterByCategory(categoryId: string | null): Promise<void>;
|
|
93
|
+
/** Load a post by slug into `activePost`, with its like state. */
|
|
94
|
+
openPost(slug: string): Promise<void>;
|
|
95
|
+
closePost(): void;
|
|
96
|
+
loadComments(): Promise<void>;
|
|
97
|
+
loadMoreComments(): Promise<void>;
|
|
98
|
+
postComment(input: BlogCommentPostInput): Promise<void>;
|
|
99
|
+
toggleLike(): Promise<void>;
|
|
100
|
+
destroy(): void;
|
|
101
|
+
};
|
|
102
|
+
export type CreateBlogControllerOptions = {
|
|
103
|
+
client: BlogCoreClient;
|
|
104
|
+
/** Posts per page. Default 12. */
|
|
105
|
+
pageSize?: number;
|
|
106
|
+
/** Comments per page. Default 20. */
|
|
107
|
+
commentPageSize?: number;
|
|
108
|
+
categoryId?: string | null;
|
|
109
|
+
onError?: (error: Error) => void;
|
|
110
|
+
};
|
|
111
|
+
export declare function createBlogController(options: CreateBlogControllerOptions): BlogController;
|
|
112
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/blog-core/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@businessdash/sdk/blog` — the headless blog controller.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this exists
|
|
5
|
+
*
|
|
6
|
+
* Blog was the last surface that was iframe-only on EVERY binding, React
|
|
7
|
+
* included. `<Blog>` renders a BusinessDash-hosted page in a sandbox, which
|
|
8
|
+
* works but puts our markup inside your site — and for a blog specifically that
|
|
9
|
+
* is the worst place to lose control, because the content is the thing search
|
|
10
|
+
* engines and readers came for and it sits behind an iframe boundary.
|
|
11
|
+
*
|
|
12
|
+
* This is the native path: the post list, a single post, categories, tags,
|
|
13
|
+
* comments and likes as data. You render it, in your own layout, on your own
|
|
14
|
+
* URLs.
|
|
15
|
+
*
|
|
16
|
+
* Same contract as `createFormController`, `createStoreController` and
|
|
17
|
+
* `createChatController`: `snapshot()`, `subscribe()` (which does NOT replay),
|
|
18
|
+
* `destroy()`, and actions.
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
const DEFAULT_PAGE_SIZE = 12;
|
|
23
|
+
const DEFAULT_COMMENT_PAGE_SIZE = 20;
|
|
24
|
+
function toError(cause, fallback) {
|
|
25
|
+
return cause instanceof Error ? cause : new Error(fallback);
|
|
26
|
+
}
|
|
27
|
+
export function createBlogController(options) {
|
|
28
|
+
const { client, onError } = options;
|
|
29
|
+
const pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;
|
|
30
|
+
const commentPageSize = options.commentPageSize ?? DEFAULT_COMMENT_PAGE_SIZE;
|
|
31
|
+
let destroyed = false;
|
|
32
|
+
let cursor = null;
|
|
33
|
+
let commentCursor = null;
|
|
34
|
+
/** Same stale-response guard as the store: see `store-core`. */
|
|
35
|
+
let postsToken = 0;
|
|
36
|
+
/**
|
|
37
|
+
* Separate token for the post detail. Opening post A then post B before A
|
|
38
|
+
* resolves must not leave A's body under B's title — and the comments and
|
|
39
|
+
* like state that follow must belong to whichever post actually won.
|
|
40
|
+
*/
|
|
41
|
+
let postToken = 0;
|
|
42
|
+
const subscribers = new Set();
|
|
43
|
+
const state = {
|
|
44
|
+
status: "idle",
|
|
45
|
+
posts: [],
|
|
46
|
+
categories: [],
|
|
47
|
+
tags: [],
|
|
48
|
+
activePost: null,
|
|
49
|
+
activeSlug: null,
|
|
50
|
+
comments: [],
|
|
51
|
+
liked: false,
|
|
52
|
+
likesCount: 0,
|
|
53
|
+
categoryId: options.categoryId ?? null,
|
|
54
|
+
hasMore: false,
|
|
55
|
+
hasMoreComments: false,
|
|
56
|
+
error: null,
|
|
57
|
+
pending: { posts: false, post: false, comments: false, like: false },
|
|
58
|
+
};
|
|
59
|
+
let cached = null;
|
|
60
|
+
function snapshot() {
|
|
61
|
+
if (!cached) {
|
|
62
|
+
cached = {
|
|
63
|
+
status: state.status,
|
|
64
|
+
posts: state.posts,
|
|
65
|
+
categories: state.categories,
|
|
66
|
+
tags: state.tags,
|
|
67
|
+
activePost: state.activePost,
|
|
68
|
+
comments: state.comments,
|
|
69
|
+
liked: state.liked,
|
|
70
|
+
likesCount: state.likesCount,
|
|
71
|
+
categoryId: state.categoryId,
|
|
72
|
+
hasMore: state.hasMore,
|
|
73
|
+
hasMoreComments: state.hasMoreComments,
|
|
74
|
+
error: state.error,
|
|
75
|
+
pending: state.pending,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return cached;
|
|
79
|
+
}
|
|
80
|
+
function notify() {
|
|
81
|
+
if (destroyed)
|
|
82
|
+
return;
|
|
83
|
+
cached = null;
|
|
84
|
+
const next = snapshot();
|
|
85
|
+
for (const cb of subscribers)
|
|
86
|
+
cb(next);
|
|
87
|
+
}
|
|
88
|
+
function fail(cause, fallback) {
|
|
89
|
+
const error = toError(cause, fallback);
|
|
90
|
+
state.error = error;
|
|
91
|
+
if (state.status !== "ready")
|
|
92
|
+
state.status = "error";
|
|
93
|
+
onError?.(error);
|
|
94
|
+
}
|
|
95
|
+
async function fetchPosts(append) {
|
|
96
|
+
const token = ++postsToken;
|
|
97
|
+
state.pending.posts = true;
|
|
98
|
+
if (!append)
|
|
99
|
+
state.error = null;
|
|
100
|
+
notify();
|
|
101
|
+
try {
|
|
102
|
+
const page = await client.blog.listPosts({
|
|
103
|
+
limit: pageSize,
|
|
104
|
+
...(append && cursor !== null ? { cursor } : {}),
|
|
105
|
+
...(state.categoryId ? { categoryId: state.categoryId } : {}),
|
|
106
|
+
});
|
|
107
|
+
if (destroyed || token !== postsToken)
|
|
108
|
+
return;
|
|
109
|
+
state.posts = append ? [...state.posts, ...page.items] : page.items;
|
|
110
|
+
cursor = page.nextCursor;
|
|
111
|
+
state.hasMore = page.nextCursor !== null;
|
|
112
|
+
state.status = "ready";
|
|
113
|
+
}
|
|
114
|
+
catch (cause) {
|
|
115
|
+
if (destroyed || token !== postsToken)
|
|
116
|
+
return;
|
|
117
|
+
fail(cause, "Failed to load posts.");
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
if (!destroyed && token === postsToken) {
|
|
121
|
+
state.pending.posts = false;
|
|
122
|
+
notify();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async function fetchComments(append) {
|
|
127
|
+
const slug = state.activeSlug;
|
|
128
|
+
if (!slug)
|
|
129
|
+
return;
|
|
130
|
+
const token = postToken;
|
|
131
|
+
state.pending.comments = true;
|
|
132
|
+
notify();
|
|
133
|
+
try {
|
|
134
|
+
const page = await client.blog.listComments(slug, {
|
|
135
|
+
limit: commentPageSize,
|
|
136
|
+
...(append && commentCursor !== null ? { cursor: commentCursor } : {}),
|
|
137
|
+
});
|
|
138
|
+
// The reader may have navigated to another post mid-request.
|
|
139
|
+
if (destroyed || token !== postToken)
|
|
140
|
+
return;
|
|
141
|
+
state.comments = append ? [...state.comments, ...page.items] : page.items;
|
|
142
|
+
commentCursor = page.nextCursor;
|
|
143
|
+
state.hasMoreComments = page.nextCursor !== null;
|
|
144
|
+
}
|
|
145
|
+
catch (cause) {
|
|
146
|
+
if (destroyed || token !== postToken)
|
|
147
|
+
return;
|
|
148
|
+
fail(cause, "Failed to load comments.");
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
if (!destroyed && token === postToken) {
|
|
152
|
+
state.pending.comments = false;
|
|
153
|
+
notify();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
snapshot,
|
|
159
|
+
subscribe(callback) {
|
|
160
|
+
subscribers.add(callback);
|
|
161
|
+
return () => {
|
|
162
|
+
subscribers.delete(callback);
|
|
163
|
+
};
|
|
164
|
+
},
|
|
165
|
+
async load() {
|
|
166
|
+
if (destroyed)
|
|
167
|
+
return;
|
|
168
|
+
if (state.status === "idle")
|
|
169
|
+
state.status = "loading";
|
|
170
|
+
notify();
|
|
171
|
+
// Taxonomy is independent of the post page, so it goes out alongside
|
|
172
|
+
// rather than after. Neither failing should hide the other.
|
|
173
|
+
const taxonomy = Promise.all([
|
|
174
|
+
client.blog
|
|
175
|
+
.listCategories()
|
|
176
|
+
.then((res) => {
|
|
177
|
+
if (!destroyed)
|
|
178
|
+
state.categories = res.items;
|
|
179
|
+
})
|
|
180
|
+
.catch((cause) => {
|
|
181
|
+
if (!destroyed)
|
|
182
|
+
fail(cause, "Failed to load categories.");
|
|
183
|
+
}),
|
|
184
|
+
client.blog
|
|
185
|
+
.listTags()
|
|
186
|
+
.then((res) => {
|
|
187
|
+
if (!destroyed)
|
|
188
|
+
state.tags = res.items;
|
|
189
|
+
})
|
|
190
|
+
.catch((cause) => {
|
|
191
|
+
if (!destroyed)
|
|
192
|
+
fail(cause, "Failed to load tags.");
|
|
193
|
+
}),
|
|
194
|
+
]);
|
|
195
|
+
await Promise.all([fetchPosts(false), taxonomy]);
|
|
196
|
+
if (!destroyed)
|
|
197
|
+
notify();
|
|
198
|
+
},
|
|
199
|
+
async loadMore() {
|
|
200
|
+
if (destroyed || !state.hasMore || state.pending.posts)
|
|
201
|
+
return;
|
|
202
|
+
await fetchPosts(true);
|
|
203
|
+
},
|
|
204
|
+
async filterByCategory(categoryId) {
|
|
205
|
+
if (destroyed)
|
|
206
|
+
return;
|
|
207
|
+
state.categoryId = categoryId;
|
|
208
|
+
cursor = null;
|
|
209
|
+
await fetchPosts(false);
|
|
210
|
+
},
|
|
211
|
+
async openPost(slug) {
|
|
212
|
+
if (destroyed)
|
|
213
|
+
return;
|
|
214
|
+
const token = ++postToken;
|
|
215
|
+
state.pending.post = true;
|
|
216
|
+
state.error = null;
|
|
217
|
+
// Clear the previous post's comments and like state immediately, so a
|
|
218
|
+
// slow fetch can't briefly show the last post's discussion under the
|
|
219
|
+
// new post's title.
|
|
220
|
+
state.comments = [];
|
|
221
|
+
state.liked = false;
|
|
222
|
+
state.likesCount = 0;
|
|
223
|
+
commentCursor = null;
|
|
224
|
+
state.activeSlug = slug;
|
|
225
|
+
notify();
|
|
226
|
+
try {
|
|
227
|
+
const post = await client.blog.getPost(slug);
|
|
228
|
+
if (destroyed || token !== postToken)
|
|
229
|
+
return;
|
|
230
|
+
state.activePost = post;
|
|
231
|
+
// Like state is decorative; a failure here must not blank the post.
|
|
232
|
+
try {
|
|
233
|
+
const like = await client.blog.likeStatus(slug);
|
|
234
|
+
if (!destroyed && token === postToken) {
|
|
235
|
+
state.liked = like.liked;
|
|
236
|
+
state.likesCount = like.likesCount;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
// leave the defaults
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
catch (cause) {
|
|
244
|
+
if (destroyed || token !== postToken)
|
|
245
|
+
return;
|
|
246
|
+
fail(cause, "Failed to load the post.");
|
|
247
|
+
}
|
|
248
|
+
finally {
|
|
249
|
+
if (!destroyed && token === postToken) {
|
|
250
|
+
state.pending.post = false;
|
|
251
|
+
notify();
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
closePost() {
|
|
256
|
+
if (destroyed)
|
|
257
|
+
return;
|
|
258
|
+
postToken += 1; // orphan any in-flight detail request
|
|
259
|
+
state.activePost = null;
|
|
260
|
+
state.activeSlug = null;
|
|
261
|
+
state.comments = [];
|
|
262
|
+
state.liked = false;
|
|
263
|
+
state.likesCount = 0;
|
|
264
|
+
commentCursor = null;
|
|
265
|
+
notify();
|
|
266
|
+
},
|
|
267
|
+
loadComments() {
|
|
268
|
+
return fetchComments(false);
|
|
269
|
+
},
|
|
270
|
+
async loadMoreComments() {
|
|
271
|
+
if (destroyed || !state.hasMoreComments || state.pending.comments)
|
|
272
|
+
return;
|
|
273
|
+
await fetchComments(true);
|
|
274
|
+
},
|
|
275
|
+
async postComment(input) {
|
|
276
|
+
const slug = state.activeSlug;
|
|
277
|
+
if (destroyed || !slug)
|
|
278
|
+
return;
|
|
279
|
+
state.pending.comments = true;
|
|
280
|
+
state.error = null;
|
|
281
|
+
notify();
|
|
282
|
+
try {
|
|
283
|
+
await client.blog.postComment(slug, input);
|
|
284
|
+
if (destroyed)
|
|
285
|
+
return;
|
|
286
|
+
// Re-read rather than splice the response in: a comment may be held
|
|
287
|
+
// for moderation, and inventing an approved-looking one locally
|
|
288
|
+
// would tell the reader something untrue.
|
|
289
|
+
commentCursor = null;
|
|
290
|
+
await fetchComments(false);
|
|
291
|
+
}
|
|
292
|
+
catch (cause) {
|
|
293
|
+
if (!destroyed) {
|
|
294
|
+
fail(cause, "Failed to post the comment.");
|
|
295
|
+
state.pending.comments = false;
|
|
296
|
+
notify();
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
async toggleLike() {
|
|
301
|
+
const slug = state.activeSlug;
|
|
302
|
+
if (destroyed || !slug)
|
|
303
|
+
return;
|
|
304
|
+
state.pending.like = true;
|
|
305
|
+
notify();
|
|
306
|
+
try {
|
|
307
|
+
const res = await client.blog.toggleLike(slug);
|
|
308
|
+
if (destroyed)
|
|
309
|
+
return;
|
|
310
|
+
state.liked = res.liked;
|
|
311
|
+
state.likesCount = res.likesCount;
|
|
312
|
+
}
|
|
313
|
+
catch (cause) {
|
|
314
|
+
if (!destroyed)
|
|
315
|
+
fail(cause, "Failed to update the like.");
|
|
316
|
+
}
|
|
317
|
+
finally {
|
|
318
|
+
if (!destroyed) {
|
|
319
|
+
state.pending.like = false;
|
|
320
|
+
notify();
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
destroy() {
|
|
325
|
+
destroyed = true;
|
|
326
|
+
subscribers.clear();
|
|
327
|
+
},
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/blog-core/index.ts"],"names":[],"mappings":"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"}
|
package/dist/blog.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@businessdash/sdk/blog` — the framework-agnostic blog core.
|
|
3
|
+
*
|
|
4
|
+
* Bindings shipped as SOURCE import through this public subpath rather than a
|
|
5
|
+
* relative path, so the consumer's bundler resolves it exactly as their own
|
|
6
|
+
* code would. `forms`, `store` and `chat` work the same way.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
export * from "./blog-core/index.js";
|
|
11
|
+
//# sourceMappingURL=blog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blog.d.ts","sourceRoot":"","sources":["../src/blog.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,cAAc,sBAAsB,CAAC"}
|
package/dist/blog.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@businessdash/sdk/blog` — the framework-agnostic blog core.
|
|
3
|
+
*
|
|
4
|
+
* Bindings shipped as SOURCE import through this public subpath rather than a
|
|
5
|
+
* relative path, so the consumer's bundler resolves it exactly as their own
|
|
6
|
+
* code would. `forms`, `store` and `chat` work the same way.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
export * from "./blog-core/index.js";
|
|
11
|
+
//# sourceMappingURL=blog.js.map
|
package/dist/blog.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blog.js","sourceRoot":"","sources":["../src/blog.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,cAAc,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@businessdash/sdk/chat` — the headless Front Desk chat controller.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this exists
|
|
5
|
+
*
|
|
6
|
+
* Chat was the one capability where React was genuinely ahead rather than
|
|
7
|
+
* merely first: it shipped a headless `useChatbot` plus `<ChatbotInline>`, so a
|
|
8
|
+
* React consumer could render chat in their own design system, while every
|
|
9
|
+
* other framework got an iframe widget and nothing else.
|
|
10
|
+
*
|
|
11
|
+
* The logic was never React-specific. It is a message list, a pending flag, the
|
|
12
|
+
* latest UI actions and an availability snapshot — none of which needs hooks.
|
|
13
|
+
* This module is that logic, lifted out verbatim so React's behaviour is
|
|
14
|
+
* preserved rather than reimplemented, and so every other binding can offer the
|
|
15
|
+
* same thing.
|
|
16
|
+
*
|
|
17
|
+
* ## What a binding does
|
|
18
|
+
*
|
|
19
|
+
* Subscribe to `snapshot()`, render it, call the actions. That is the whole
|
|
20
|
+
* contract — the same one `createFormController` and `createStoreController`
|
|
21
|
+
* use.
|
|
22
|
+
*
|
|
23
|
+
* ## Transcript ownership
|
|
24
|
+
*
|
|
25
|
+
* The controller holds the transcript and sends the WHOLE thing on every turn,
|
|
26
|
+
* because the chat endpoint is stateless: it answers from the messages it is
|
|
27
|
+
* given. A binding must not keep its own copy and pass it in — two transcripts
|
|
28
|
+
* diverge the moment one render is dropped, and the visitor gets an answer to a
|
|
29
|
+
* question they didn't ask.
|
|
30
|
+
*
|
|
31
|
+
* @module
|
|
32
|
+
*/
|
|
33
|
+
import type { ChatbotAvailability, ChatbotChatInput, ChatbotChatResponse, ChatbotConfig, ChatbotFormDefinition, ChatbotFormSubmitInput, ChatbotFormSubmitResponse, ChatbotMessage, ChatbotRequestHumanInput, ChatbotRequestHumanResponse, ChatbotUiAction } from "../contracts.js";
|
|
34
|
+
/**
|
|
35
|
+
* The client surface this controller needs — declared structurally so a
|
|
36
|
+
* same-origin proxy or a test double works without subclassing anything.
|
|
37
|
+
*/
|
|
38
|
+
export type ChatCoreClient = {
|
|
39
|
+
chatbot: {
|
|
40
|
+
chat(input: ChatbotChatInput): Promise<ChatbotChatResponse>;
|
|
41
|
+
getConfig(): Promise<ChatbotConfig>;
|
|
42
|
+
getAvailability(): Promise<ChatbotAvailability>;
|
|
43
|
+
getChatForm(slug: string): Promise<ChatbotFormDefinition>;
|
|
44
|
+
submitChatForm(slug: string, input: ChatbotFormSubmitInput): Promise<ChatbotFormSubmitResponse>;
|
|
45
|
+
requestHuman(input: ChatbotRequestHumanInput): Promise<ChatbotRequestHumanResponse>;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
export type ChatStatus = "idle" | "ready" | "error";
|
|
49
|
+
/**
|
|
50
|
+
* What a binding renders. Field names are STABLE and load-bearing across every
|
|
51
|
+
* framework binding.
|
|
52
|
+
*/
|
|
53
|
+
export type ChatSnapshot = {
|
|
54
|
+
status: ChatStatus;
|
|
55
|
+
messages: readonly ChatbotMessage[];
|
|
56
|
+
/** A turn is in flight — disable the send button, show a typing indicator. */
|
|
57
|
+
isPending: boolean;
|
|
58
|
+
error: Error | null;
|
|
59
|
+
/** UI actions from the most recent reply, e.g. `show_form`. */
|
|
60
|
+
uiActions: readonly ChatbotUiAction[];
|
|
61
|
+
/** Front-desk presence + wait-time hint, or `null` before it is fetched. */
|
|
62
|
+
availability: ChatbotAvailability | null;
|
|
63
|
+
/** The org's chatbot config, once `loadConfig()` has run. */
|
|
64
|
+
config: ChatbotConfig | null;
|
|
65
|
+
};
|
|
66
|
+
export type ChatController = {
|
|
67
|
+
snapshot(): ChatSnapshot;
|
|
68
|
+
/**
|
|
69
|
+
* Subscribe to snapshot changes. Returns an unsubscribe function. NOT
|
|
70
|
+
* invoked on subscribe — read `snapshot()` for the initial value.
|
|
71
|
+
*/
|
|
72
|
+
subscribe(callback: (snapshot: ChatSnapshot) => void): () => void;
|
|
73
|
+
/**
|
|
74
|
+
* Send a turn. Empty/whitespace input is ignored and resolves `null` without
|
|
75
|
+
* a request, so a binding can wire this straight to a form submit.
|
|
76
|
+
*/
|
|
77
|
+
send(content: string): Promise<ChatbotChatResponse | null>;
|
|
78
|
+
/** Clear the transcript, the error and the pending UI actions. */
|
|
79
|
+
reset(): void;
|
|
80
|
+
loadConfig(): Promise<ChatbotConfig | null>;
|
|
81
|
+
refreshAvailability(): Promise<ChatbotAvailability | null>;
|
|
82
|
+
/** Fetch a form the assistant asked for via a `show_form` UI action. */
|
|
83
|
+
getForm(slug: string): Promise<ChatbotFormDefinition | null>;
|
|
84
|
+
/** Submit that form. The transcript is attached automatically. */
|
|
85
|
+
submitForm(slug: string, input: ChatbotFormSubmitInput): Promise<ChatbotFormSubmitResponse | null>;
|
|
86
|
+
/** Escalate to a human. */
|
|
87
|
+
requestHuman(input: ChatbotRequestHumanInput): Promise<ChatbotRequestHumanResponse | null>;
|
|
88
|
+
destroy(): void;
|
|
89
|
+
};
|
|
90
|
+
export type CreateChatControllerOptions = {
|
|
91
|
+
client: ChatCoreClient;
|
|
92
|
+
/** Seed the transcript, e.g. restoring a conversation from storage. */
|
|
93
|
+
initialMessages?: readonly ChatbotMessage[];
|
|
94
|
+
onError?: (error: Error) => void;
|
|
95
|
+
};
|
|
96
|
+
export declare function createChatController(options: CreateChatControllerOptions): ChatController;
|
|
97
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/chat-core/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EACX,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,sBAAsB,EACtB,yBAAyB,EACzB,cAAc,EACd,wBAAwB,EACxB,2BAA2B,EAC3B,eAAe,EACf,MAAM,iBAAiB,CAAC;AAEzB;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG;IAC5B,OAAO,EAAE;QACR,IAAI,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;QAC5D,SAAS,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;QACpC,eAAe,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAC;QAChD,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;QAC1D,cAAc,CACb,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,sBAAsB,GAC3B,OAAO,CAAC,yBAAyB,CAAC,CAAC;QACtC,YAAY,CACX,KAAK,EAAE,wBAAwB,GAC7B,OAAO,CAAC,2BAA2B,CAAC,CAAC;KACxC,CAAC;CACF,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAEpD;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG;IAC1B,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;IACpC,8EAA8E;IAC9E,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,+DAA+D;IAC/D,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;IACtC,4EAA4E;IAC5E,YAAY,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACzC,6DAA6D;IAC7D,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC5B,QAAQ,IAAI,YAAY,CAAC;IACzB;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAClE;;;OAGG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;IAC3D,kEAAkE;IAClE,KAAK,IAAI,IAAI,CAAC;IACd,UAAU,IAAI,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAC5C,mBAAmB,IAAI,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;IAC3D,wEAAwE;IACxE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAC;IAC7D,kEAAkE;IAClE,UAAU,CACT,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,sBAAsB,GAC3B,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAAC;IAC7C,2BAA2B;IAC3B,YAAY,CACX,KAAK,EAAE,wBAAwB,GAC7B,OAAO,CAAC,2BAA2B,GAAG,IAAI,CAAC,CAAC;IAC/C,OAAO,IAAI,IAAI,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACzC,MAAM,EAAE,cAAc,CAAC;IACvB,uEAAuE;IACvE,eAAe,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IAC5C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACjC,CAAC;AAMF,wBAAgB,oBAAoB,CACnC,OAAO,EAAE,2BAA2B,GAClC,cAAc,CA4MhB"}
|