@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.
Files changed (239) hide show
  1. package/README.md +991 -295
  2. package/assets/biab-sw.js +92 -0
  3. package/dist/account-core/index.d.ts +126 -0
  4. package/dist/account-core/index.d.ts.map +1 -0
  5. package/dist/account-core/index.js +191 -0
  6. package/dist/account-core/index.js.map +1 -0
  7. package/dist/account.d.ts +11 -0
  8. package/dist/account.d.ts.map +1 -0
  9. package/dist/account.js +11 -0
  10. package/dist/account.js.map +1 -0
  11. package/dist/auth-handler.d.ts +1 -1
  12. package/dist/auth-links.d.ts +33 -0
  13. package/dist/auth-links.d.ts.map +1 -0
  14. package/dist/auth-links.js +64 -0
  15. package/dist/auth-links.js.map +1 -0
  16. package/dist/blog-core/index.d.ts +112 -0
  17. package/dist/blog-core/index.d.ts.map +1 -0
  18. package/dist/blog-core/index.js +330 -0
  19. package/dist/blog-core/index.js.map +1 -0
  20. package/dist/blog.d.ts +11 -0
  21. package/dist/blog.d.ts.map +1 -0
  22. package/dist/blog.js +11 -0
  23. package/dist/blog.js.map +1 -0
  24. package/dist/chat-core/index.d.ts +97 -0
  25. package/dist/chat-core/index.d.ts.map +1 -0
  26. package/dist/chat-core/index.js +237 -0
  27. package/dist/chat-core/index.js.map +1 -0
  28. package/dist/chat.d.ts +11 -0
  29. package/dist/chat.d.ts.map +1 -0
  30. package/dist/chat.js +11 -0
  31. package/dist/chat.js.map +1 -0
  32. package/dist/checkout-core/index.d.ts +65 -0
  33. package/dist/checkout-core/index.d.ts.map +1 -0
  34. package/dist/checkout-core/index.js +159 -0
  35. package/dist/checkout-core/index.js.map +1 -0
  36. package/dist/checkout.d.ts +10 -0
  37. package/dist/checkout.d.ts.map +1 -0
  38. package/dist/checkout.js +10 -0
  39. package/dist/checkout.js.map +1 -0
  40. package/dist/cli.js +133 -25
  41. package/dist/cli.js.map +1 -1
  42. package/dist/client.d.ts +622 -1
  43. package/dist/client.d.ts.map +1 -1
  44. package/dist/client.js +888 -1
  45. package/dist/client.js.map +1 -1
  46. package/dist/consent-core/index.cjs +107 -0
  47. package/dist/consent-core/index.d.ts +103 -0
  48. package/dist/consent-core/index.d.ts.map +1 -0
  49. package/dist/consent-core/index.js +157 -0
  50. package/dist/consent-core/index.js.map +1 -0
  51. package/dist/contracts.d.ts +3526 -767
  52. package/dist/contracts.d.ts.map +1 -1
  53. package/dist/contracts.js +571 -4
  54. package/dist/contracts.js.map +1 -1
  55. package/dist/data-model-schema.d.ts +1 -1
  56. package/dist/data-model-schema.d.ts.map +1 -1
  57. package/dist/element.d.ts +30 -0
  58. package/dist/element.d.ts.map +1 -1
  59. package/dist/element.js +56 -0
  60. package/dist/element.js.map +1 -1
  61. package/dist/embed-protocol.d.ts +86 -0
  62. package/dist/embed-protocol.d.ts.map +1 -0
  63. package/dist/embed-protocol.js +60 -0
  64. package/dist/embed-protocol.js.map +1 -0
  65. package/dist/framework-support.d.ts +122 -0
  66. package/dist/framework-support.d.ts.map +1 -0
  67. package/dist/framework-support.js +323 -0
  68. package/dist/framework-support.js.map +1 -0
  69. package/dist/index.cjs +1355 -24
  70. package/dist/legal-core/index.cjs +115 -0
  71. package/dist/legal-core/index.d.ts +160 -0
  72. package/dist/legal-core/index.d.ts.map +1 -0
  73. package/dist/legal-core/index.js +220 -0
  74. package/dist/legal-core/index.js.map +1 -0
  75. package/dist/marketing-core/index.d.ts +66 -0
  76. package/dist/marketing-core/index.d.ts.map +1 -0
  77. package/dist/marketing-core/index.js +154 -0
  78. package/dist/marketing-core/index.js.map +1 -0
  79. package/dist/marketing.d.ts +7 -0
  80. package/dist/marketing.d.ts.map +1 -0
  81. package/dist/marketing.js +7 -0
  82. package/dist/marketing.js.map +1 -0
  83. package/dist/mcp-adapters/index.d.ts +135 -0
  84. package/dist/mcp-adapters/index.d.ts.map +1 -0
  85. package/dist/mcp-adapters/index.js +187 -0
  86. package/dist/mcp-adapters/index.js.map +1 -0
  87. package/dist/mcp-adapters.d.ts +7 -0
  88. package/dist/mcp-adapters.d.ts.map +1 -0
  89. package/dist/mcp-adapters.js +7 -0
  90. package/dist/mcp-adapters.js.map +1 -0
  91. package/dist/model3d-core/index.cjs +95 -0
  92. package/dist/model3d-core/index.d.ts +167 -0
  93. package/dist/model3d-core/index.d.ts.map +1 -0
  94. package/dist/model3d-core/index.js +158 -0
  95. package/dist/model3d-core/index.js.map +1 -0
  96. package/dist/notifications-core/index.cjs +315 -0
  97. package/dist/notifications-core/index.d.ts +229 -0
  98. package/dist/notifications-core/index.d.ts.map +1 -0
  99. package/dist/notifications-core/index.js +445 -0
  100. package/dist/notifications-core/index.js.map +1 -0
  101. package/dist/portal-core/index.d.ts +451 -0
  102. package/dist/portal-core/index.d.ts.map +1 -0
  103. package/dist/portal-core/index.js +819 -0
  104. package/dist/portal-core/index.js.map +1 -0
  105. package/dist/portal.d.ts +7 -0
  106. package/dist/portal.d.ts.map +1 -0
  107. package/dist/portal.js +7 -0
  108. package/dist/portal.js.map +1 -0
  109. package/dist/push-core/index.cjs +171 -0
  110. package/dist/push-core/index.d.ts +149 -0
  111. package/dist/push-core/index.d.ts.map +1 -0
  112. package/dist/push-core/index.js +231 -0
  113. package/dist/push-core/index.js.map +1 -0
  114. package/dist/react-marketing.d.ts +1 -17
  115. package/dist/react-marketing.d.ts.map +1 -1
  116. package/dist/react-marketing.js +4 -0
  117. package/dist/react-marketing.js.map +1 -1
  118. package/dist/react.d.ts +294 -101
  119. package/dist/react.d.ts.map +1 -1
  120. package/dist/react.js +302 -246
  121. package/dist/react.js.map +1 -1
  122. package/dist/seed-core/index.d.ts +354 -0
  123. package/dist/seed-core/index.d.ts.map +1 -0
  124. package/dist/seed-core/index.js +420 -0
  125. package/dist/seed-core/index.js.map +1 -0
  126. package/dist/seed.d.ts +7 -0
  127. package/dist/seed.d.ts.map +1 -0
  128. package/dist/seed.js +7 -0
  129. package/dist/seed.js.map +1 -0
  130. package/dist/seo-core/blog.d.ts +94 -0
  131. package/dist/seo-core/blog.d.ts.map +1 -0
  132. package/dist/seo-core/blog.js +149 -0
  133. package/dist/seo-core/blog.js.map +1 -0
  134. package/dist/seo-core/commerce.d.ts +64 -0
  135. package/dist/seo-core/commerce.d.ts.map +1 -0
  136. package/dist/seo-core/commerce.js +87 -0
  137. package/dist/seo-core/commerce.js.map +1 -0
  138. package/dist/seo-core/index.cjs +751 -0
  139. package/dist/seo-core/index.d.ts +226 -0
  140. package/dist/seo-core/index.d.ts.map +1 -0
  141. package/dist/seo-core/index.js +455 -0
  142. package/dist/seo-core/index.js.map +1 -0
  143. package/dist/seo-core/local.d.ts +181 -0
  144. package/dist/seo-core/local.d.ts.map +1 -0
  145. package/dist/seo-core/local.js +193 -0
  146. package/dist/seo-core/local.js.map +1 -0
  147. package/dist/sitemap-core/index.cjs +167 -0
  148. package/dist/sitemap-core/index.d.ts +155 -0
  149. package/dist/sitemap-core/index.d.ts.map +1 -0
  150. package/dist/sitemap-core/index.js +195 -0
  151. package/dist/sitemap-core/index.js.map +1 -0
  152. package/dist/social-core/index.d.ts +74 -0
  153. package/dist/social-core/index.d.ts.map +1 -0
  154. package/dist/social-core/index.js +72 -0
  155. package/dist/social-core/index.js.map +1 -0
  156. package/dist/social.d.ts +7 -0
  157. package/dist/social.d.ts.map +1 -0
  158. package/dist/social.js +7 -0
  159. package/dist/social.js.map +1 -0
  160. package/dist/solid-compat.d.ts +204 -0
  161. package/dist/solid-compat.d.ts.map +1 -0
  162. package/dist/solid-compat.js +221 -0
  163. package/dist/solid-compat.js.map +1 -0
  164. package/dist/store-core/index.d.ts +141 -0
  165. package/dist/store-core/index.d.ts.map +1 -0
  166. package/dist/store-core/index.js +277 -0
  167. package/dist/store-core/index.js.map +1 -0
  168. package/dist/store.d.ts +12 -0
  169. package/dist/store.d.ts.map +1 -0
  170. package/dist/store.js +12 -0
  171. package/dist/store.js.map +1 -0
  172. package/dist/vanilla.d.ts +111 -0
  173. package/dist/vanilla.d.ts.map +1 -1
  174. package/dist/vanilla.js +206 -0
  175. package/dist/vanilla.js.map +1 -1
  176. package/dist/verify-core/index.d.ts +98 -0
  177. package/dist/verify-core/index.d.ts.map +1 -0
  178. package/dist/verify-core/index.js +186 -0
  179. package/dist/verify-core/index.js.map +1 -0
  180. package/dist/verify.d.ts +8 -0
  181. package/dist/verify.d.ts.map +1 -0
  182. package/dist/verify.js +8 -0
  183. package/dist/verify.js.map +1 -0
  184. package/openapi.json +4516 -1311
  185. package/package.json +128 -11
  186. package/src/account-core/index.ts +309 -0
  187. package/src/account.ts +11 -0
  188. package/src/angular/headless.ts +519 -0
  189. package/src/angular/index.ts +3 -0
  190. package/src/auth-links.ts +76 -0
  191. package/src/blog-core/index.ts +423 -0
  192. package/src/blog.ts +11 -0
  193. package/src/chat-core/index.ts +335 -0
  194. package/src/chat.ts +11 -0
  195. package/src/checkout-core/index.ts +225 -0
  196. package/src/checkout.ts +10 -0
  197. package/src/cli.ts +184 -28
  198. package/src/client.ts +1097 -0
  199. package/src/consent-core/index.ts +213 -0
  200. package/src/contracts.ts +768 -11
  201. package/src/data-model-schema.ts +1 -1
  202. package/src/element.ts +102 -0
  203. package/src/embed-protocol.ts +100 -0
  204. package/src/framework-support.ts +440 -0
  205. package/src/legal-core/index.ts +276 -0
  206. package/src/marketing-core/index.ts +209 -0
  207. package/src/marketing.ts +7 -0
  208. package/src/mcp-adapters/index.ts +264 -0
  209. package/src/mcp-adapters.ts +7 -0
  210. package/src/model3d-core/index.ts +248 -0
  211. package/src/notifications-core/index.ts +650 -0
  212. package/src/portal-core/index.ts +1564 -0
  213. package/src/portal.ts +7 -0
  214. package/src/push-core/index.ts +335 -0
  215. package/src/qwik.tsx +602 -2
  216. package/src/react-marketing.tsx +5 -0
  217. package/src/react.tsx +434 -319
  218. package/src/seed-core/index.ts +735 -0
  219. package/src/seed.ts +7 -0
  220. package/src/seo-core/blog.ts +216 -0
  221. package/src/seo-core/commerce.ts +138 -0
  222. package/src/seo-core/index.ts +605 -0
  223. package/src/seo-core/local.ts +333 -0
  224. package/src/sitemap-core/index.ts +296 -0
  225. package/src/social-core/index.ts +103 -0
  226. package/src/social.ts +7 -0
  227. package/src/solid-compat.tsx +347 -0
  228. package/src/solid-surfaces.tsx +1311 -0
  229. package/src/solid.tsx +82 -60
  230. package/src/store-core/index.ts +426 -0
  231. package/src/store.ts +12 -0
  232. package/src/svelte/SocialLinks.svelte +84 -0
  233. package/src/svelte/headless.ts +435 -0
  234. package/src/svelte/index.ts +9 -0
  235. package/src/vanilla.ts +367 -0
  236. package/src/verify-core/index.ts +288 -0
  237. package/src/verify.ts +8 -0
  238. package/src/vue/headless.ts +527 -0
  239. 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":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EACX,YAAY,EACZ,oBAAoB,EACpB,sBAAsB,EACtB,iBAAiB,EACjB,cAAc,EACd,mBAAmB,EACnB,MAAM,iBAAiB,CAAC;AAEzB,6EAA6E;AAC7E,MAAM,MAAM,OAAO,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D,8EAA8E;AAC9E,MAAM,MAAM,cAAc,GAAG;IAC5B,IAAI,EAAE;QACL,SAAS,CAAC,KAAK,CAAC,EAAE;YACjB,KAAK,CAAC,EAAE,MAAM,CAAC;YACf,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YACvB,UAAU,CAAC,EAAE,MAAM,CAAC;SACpB,GAAG,OAAO,CAAC;YAAE,KAAK,EAAE,cAAc,EAAE,CAAC;YAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;SAAE,CAAC,CAAC;QACpE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;QACvD,cAAc,IAAI,OAAO,CAAC;YAAE,KAAK,EAAE,YAAY,EAAE,CAAA;SAAE,CAAC,CAAC;QACrD,QAAQ,IAAI,OAAO,CAAC;YAAE,KAAK,EAAE,OAAO,EAAE,CAAA;SAAE,CAAC,CAAC;QAC1C,YAAY,CACX,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE;YAAE,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;SAAE,GAChD,OAAO,CAAC;YAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC;YAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;SAAE,CAAC,CAAC;QACtE,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACzE,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;YAAE,KAAK,EAAE,OAAO,CAAC;YAAC,UAAU,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAC1E,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;YAAE,KAAK,EAAE,OAAO,CAAC;YAAC,UAAU,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAC1E,CAAC;CACF,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;AAEhE,2EAA2E;AAC3E,MAAM,MAAM,YAAY,GAAG;IAC1B,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;IACjC,UAAU,EAAE,SAAS,YAAY,EAAE,CAAC;IACpC,IAAI,EAAE,SAAS,OAAO,EAAE,CAAC;IACzB,gDAAgD;IAChD,UAAU,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC1C,oEAAoE;IACpE,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACvC,mCAAmC;IACnC,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;IACzB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE;QACR,KAAK,EAAE,OAAO,CAAC;QACf,IAAI,EAAE,OAAO,CAAC;QACd,QAAQ,EAAE,OAAO,CAAC;QAClB,IAAI,EAAE,OAAO,CAAC;KACd,CAAC;CACF,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC5B,QAAQ,IAAI,YAAY,CAAC;IACzB,SAAS,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAClE,+DAA+D;IAC/D,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,SAAS,IAAI,IAAI,CAAC;IAClB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,WAAW,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,IAAI,IAAI,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACzC,MAAM,EAAE,cAAc,CAAC;IACvB,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACjC,CAAC;AASF,wBAAgB,oBAAoB,CACnC,OAAO,EAAE,2BAA2B,GAClC,cAAc,CAgThB"}
@@ -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
@@ -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"}