@businessdash/sdk 0.9.60 → 0.9.70

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 (233) hide show
  1. package/README.md +1012 -186
  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 +567 -1
  43. package/dist/client.d.ts.map +1 -1
  44. package/dist/client.js +807 -1
  45. package/dist/client.js.map +1 -1
  46. package/dist/contracts.d.ts +3309 -761
  47. package/dist/contracts.d.ts.map +1 -1
  48. package/dist/contracts.js +541 -4
  49. package/dist/contracts.js.map +1 -1
  50. package/dist/data-model-schema.d.ts +1 -1
  51. package/dist/data-model-schema.d.ts.map +1 -1
  52. package/dist/element.d.ts +30 -0
  53. package/dist/element.d.ts.map +1 -1
  54. package/dist/element.js +56 -0
  55. package/dist/element.js.map +1 -1
  56. package/dist/embed-protocol.d.ts +86 -0
  57. package/dist/embed-protocol.d.ts.map +1 -0
  58. package/dist/embed-protocol.js +60 -0
  59. package/dist/embed-protocol.js.map +1 -0
  60. package/dist/framework-support.d.ts +122 -0
  61. package/dist/framework-support.d.ts.map +1 -0
  62. package/dist/framework-support.js +323 -0
  63. package/dist/framework-support.js.map +1 -0
  64. package/dist/index.cjs +1244 -5
  65. package/dist/legal-core/index.cjs +115 -0
  66. package/dist/legal-core/index.d.ts +160 -0
  67. package/dist/legal-core/index.d.ts.map +1 -0
  68. package/dist/legal-core/index.js +220 -0
  69. package/dist/legal-core/index.js.map +1 -0
  70. package/dist/marketing-core/index.d.ts +66 -0
  71. package/dist/marketing-core/index.d.ts.map +1 -0
  72. package/dist/marketing-core/index.js +154 -0
  73. package/dist/marketing-core/index.js.map +1 -0
  74. package/dist/marketing.d.ts +7 -0
  75. package/dist/marketing.d.ts.map +1 -0
  76. package/dist/marketing.js +7 -0
  77. package/dist/marketing.js.map +1 -0
  78. package/dist/mcp-adapters/index.d.ts +135 -0
  79. package/dist/mcp-adapters/index.d.ts.map +1 -0
  80. package/dist/mcp-adapters/index.js +187 -0
  81. package/dist/mcp-adapters/index.js.map +1 -0
  82. package/dist/mcp-adapters.d.ts +7 -0
  83. package/dist/mcp-adapters.d.ts.map +1 -0
  84. package/dist/mcp-adapters.js +7 -0
  85. package/dist/mcp-adapters.js.map +1 -0
  86. package/dist/model3d-core/index.cjs +95 -0
  87. package/dist/model3d-core/index.d.ts +167 -0
  88. package/dist/model3d-core/index.d.ts.map +1 -0
  89. package/dist/model3d-core/index.js +158 -0
  90. package/dist/model3d-core/index.js.map +1 -0
  91. package/dist/notifications-core/index.cjs +315 -0
  92. package/dist/notifications-core/index.d.ts +229 -0
  93. package/dist/notifications-core/index.d.ts.map +1 -0
  94. package/dist/notifications-core/index.js +445 -0
  95. package/dist/notifications-core/index.js.map +1 -0
  96. package/dist/portal-core/index.d.ts +451 -0
  97. package/dist/portal-core/index.d.ts.map +1 -0
  98. package/dist/portal-core/index.js +819 -0
  99. package/dist/portal-core/index.js.map +1 -0
  100. package/dist/portal.d.ts +7 -0
  101. package/dist/portal.d.ts.map +1 -0
  102. package/dist/portal.js +7 -0
  103. package/dist/portal.js.map +1 -0
  104. package/dist/push-core/index.cjs +171 -0
  105. package/dist/push-core/index.d.ts +149 -0
  106. package/dist/push-core/index.d.ts.map +1 -0
  107. package/dist/push-core/index.js +231 -0
  108. package/dist/push-core/index.js.map +1 -0
  109. package/dist/react-marketing.d.ts +1 -17
  110. package/dist/react-marketing.d.ts.map +1 -1
  111. package/dist/react-marketing.js +4 -0
  112. package/dist/react-marketing.js.map +1 -1
  113. package/dist/react.d.ts +294 -101
  114. package/dist/react.d.ts.map +1 -1
  115. package/dist/react.js +302 -246
  116. package/dist/react.js.map +1 -1
  117. package/dist/seed-core/index.d.ts +354 -0
  118. package/dist/seed-core/index.d.ts.map +1 -0
  119. package/dist/seed-core/index.js +420 -0
  120. package/dist/seed-core/index.js.map +1 -0
  121. package/dist/seed.d.ts +7 -0
  122. package/dist/seed.d.ts.map +1 -0
  123. package/dist/seed.js +7 -0
  124. package/dist/seed.js.map +1 -0
  125. package/dist/seo-core/blog.d.ts +94 -0
  126. package/dist/seo-core/blog.d.ts.map +1 -0
  127. package/dist/seo-core/blog.js +149 -0
  128. package/dist/seo-core/blog.js.map +1 -0
  129. package/dist/seo-core/commerce.d.ts +64 -0
  130. package/dist/seo-core/commerce.d.ts.map +1 -0
  131. package/dist/seo-core/commerce.js +87 -0
  132. package/dist/seo-core/commerce.js.map +1 -0
  133. package/dist/seo-core/index.cjs +751 -0
  134. package/dist/seo-core/index.d.ts +226 -0
  135. package/dist/seo-core/index.d.ts.map +1 -0
  136. package/dist/seo-core/index.js +455 -0
  137. package/dist/seo-core/index.js.map +1 -0
  138. package/dist/seo-core/local.d.ts +181 -0
  139. package/dist/seo-core/local.d.ts.map +1 -0
  140. package/dist/seo-core/local.js +193 -0
  141. package/dist/seo-core/local.js.map +1 -0
  142. package/dist/sitemap-core/index.cjs +167 -0
  143. package/dist/sitemap-core/index.d.ts +155 -0
  144. package/dist/sitemap-core/index.d.ts.map +1 -0
  145. package/dist/sitemap-core/index.js +195 -0
  146. package/dist/sitemap-core/index.js.map +1 -0
  147. package/dist/social-core/index.d.ts +74 -0
  148. package/dist/social-core/index.d.ts.map +1 -0
  149. package/dist/social-core/index.js +72 -0
  150. package/dist/social-core/index.js.map +1 -0
  151. package/dist/social.d.ts +7 -0
  152. package/dist/social.d.ts.map +1 -0
  153. package/dist/social.js +7 -0
  154. package/dist/social.js.map +1 -0
  155. package/dist/solid-compat.d.ts +204 -0
  156. package/dist/solid-compat.d.ts.map +1 -0
  157. package/dist/solid-compat.js +221 -0
  158. package/dist/solid-compat.js.map +1 -0
  159. package/dist/store-core/index.d.ts +141 -0
  160. package/dist/store-core/index.d.ts.map +1 -0
  161. package/dist/store-core/index.js +277 -0
  162. package/dist/store-core/index.js.map +1 -0
  163. package/dist/store.d.ts +12 -0
  164. package/dist/store.d.ts.map +1 -0
  165. package/dist/store.js +12 -0
  166. package/dist/store.js.map +1 -0
  167. package/dist/vanilla.d.ts +111 -0
  168. package/dist/vanilla.d.ts.map +1 -1
  169. package/dist/vanilla.js +206 -0
  170. package/dist/vanilla.js.map +1 -1
  171. package/dist/verify-core/index.d.ts +98 -0
  172. package/dist/verify-core/index.d.ts.map +1 -0
  173. package/dist/verify-core/index.js +186 -0
  174. package/dist/verify-core/index.js.map +1 -0
  175. package/dist/verify.d.ts +8 -0
  176. package/dist/verify.d.ts.map +1 -0
  177. package/dist/verify.js +8 -0
  178. package/dist/verify.js.map +1 -0
  179. package/openapi.json +4316 -1313
  180. package/package.json +121 -9
  181. package/src/account-core/index.ts +309 -0
  182. package/src/account.ts +11 -0
  183. package/src/angular/headless.ts +519 -0
  184. package/src/angular/index.ts +3 -0
  185. package/src/auth-links.ts +76 -0
  186. package/src/blog-core/index.ts +423 -0
  187. package/src/blog.ts +11 -0
  188. package/src/chat-core/index.ts +335 -0
  189. package/src/chat.ts +11 -0
  190. package/src/checkout-core/index.ts +225 -0
  191. package/src/checkout.ts +10 -0
  192. package/src/cli.ts +184 -28
  193. package/src/client.ts +998 -0
  194. package/src/contracts.ts +730 -11
  195. package/src/data-model-schema.ts +1 -1
  196. package/src/element.ts +102 -0
  197. package/src/embed-protocol.ts +100 -0
  198. package/src/framework-support.ts +440 -0
  199. package/src/legal-core/index.ts +276 -0
  200. package/src/marketing-core/index.ts +209 -0
  201. package/src/marketing.ts +7 -0
  202. package/src/mcp-adapters/index.ts +264 -0
  203. package/src/mcp-adapters.ts +7 -0
  204. package/src/model3d-core/index.ts +248 -0
  205. package/src/notifications-core/index.ts +650 -0
  206. package/src/portal-core/index.ts +1564 -0
  207. package/src/portal.ts +7 -0
  208. package/src/push-core/index.ts +335 -0
  209. package/src/qwik.tsx +602 -2
  210. package/src/react-marketing.tsx +5 -0
  211. package/src/react.tsx +434 -319
  212. package/src/seed-core/index.ts +735 -0
  213. package/src/seed.ts +7 -0
  214. package/src/seo-core/blog.ts +216 -0
  215. package/src/seo-core/commerce.ts +138 -0
  216. package/src/seo-core/index.ts +605 -0
  217. package/src/seo-core/local.ts +333 -0
  218. package/src/sitemap-core/index.ts +296 -0
  219. package/src/social-core/index.ts +103 -0
  220. package/src/social.ts +7 -0
  221. package/src/solid-compat.tsx +347 -0
  222. package/src/solid-surfaces.tsx +1311 -0
  223. package/src/solid.tsx +82 -60
  224. package/src/store-core/index.ts +426 -0
  225. package/src/store.ts +12 -0
  226. package/src/svelte/SocialLinks.svelte +84 -0
  227. package/src/svelte/headless.ts +435 -0
  228. package/src/svelte/index.ts +9 -0
  229. package/src/vanilla.ts +367 -0
  230. package/src/verify-core/index.ts +288 -0
  231. package/src/verify.ts +8 -0
  232. package/src/vue/headless.ts +527 -0
  233. package/src/vue/index.ts +3 -0
@@ -0,0 +1,519 @@
1
+ /**
2
+ * `@businessdash/sdk/angular` — headless storefront + chat.
3
+ *
4
+ * `<Storefront>` and `<Chatbot>` are hosted pages in a sandboxed iframe. These
5
+ * are the native path: all behaviour, no markup.
6
+ *
7
+ * Everything real lives in `store-core` / `chat-core`, shared with every other
8
+ * binding. This file bridges a controller's subscribe/snapshot observable into
9
+ * an Angular signal.
10
+ *
11
+ * @module
12
+ */
13
+
14
+ import { Component, Input, type Signal, signal } from "@angular/core";
15
+
16
+ import {
17
+ type BlogController,
18
+ type BlogSnapshot,
19
+ createBlogController,
20
+ } from "../blog-core/index.js";
21
+ import {
22
+ type ChatController,
23
+ type ChatSnapshot,
24
+ createChatController,
25
+ } from "../chat-core/index.js";
26
+ import { BiabDevClient } from "../client.js";
27
+ import {
28
+ createPortalController,
29
+ type PortalSnapshot,
30
+ } from "../portal-core/index.js";
31
+
32
+ import {
33
+ createMarketingController,
34
+ type MarketingController,
35
+ type MarketingSnapshot,
36
+ } from "../marketing-core/index.js";
37
+ import {
38
+ createVerifyController,
39
+ type CreateVerifyControllerOptions,
40
+ type VerifyController,
41
+ type VerifySnapshot,
42
+ } from "../verify-core/index.js";
43
+
44
+ import {
45
+ createFollowersController,
46
+ createUserController,
47
+ type FollowersController,
48
+ type FollowersSnapshot,
49
+ type UserController,
50
+ type UserSnapshot,
51
+ } from "../account-core/index.js";
52
+ import {
53
+ type CheckoutController,
54
+ type CheckoutSnapshot,
55
+ createCheckoutController,
56
+ } from "../checkout-core/index.js";
57
+ import { createBiabClient } from "../sdk.js";
58
+
59
+ import type {
60
+ BlogCommentPostInput,
61
+ CartAddItemInput,
62
+ ChatbotFormSubmitInput,
63
+ ChatbotRequestHumanInput,
64
+ CustomerPortalMessagePostInput,
65
+ } from "../contracts.js";
66
+ import {
67
+ createStoreController,
68
+ type StoreController,
69
+ type StoreSnapshot,
70
+ } from "../store-core/index.js";
71
+
72
+ type Observable<TSnapshot> = {
73
+ snapshot(): TSnapshot;
74
+ subscribe(cb: (s: TSnapshot) => void): () => void;
75
+ destroy(): void;
76
+ };
77
+
78
+ /**
79
+ * Bridge a controller into a signal, and hand back the teardown.
80
+ *
81
+ * Teardown is RETURNED rather than registered with `DestroyRef`, because these
82
+ * are plain functions — they can be called from a service, a route resolver or
83
+ * a test, not only from an injection context. Callers inside a component
84
+ * should pass it to `inject(DestroyRef).onDestroy(...)`; the `destroy()` on
85
+ * each handle below is that function.
86
+ */
87
+ function bridge<TSnapshot>(
88
+ // Structural, not a second generic — see the note in the Vue binding:
89
+ // TypeScript cannot infer `TSnapshot` from a constraint alone, so it would
90
+ // resolve to `unknown` and strip the snapshot's type from every caller.
91
+ controller: Observable<TSnapshot>,
92
+ start?: () => void,
93
+ ): { state: Signal<TSnapshot>; dispose: () => void } {
94
+ // Seeded from `snapshot()` because the cores do not replay on subscribe.
95
+ const state = signal<TSnapshot>(controller.snapshot());
96
+ const unsubscribe = controller.subscribe((next) => state.set(next));
97
+ start?.();
98
+ return {
99
+ state: state.asReadonly(),
100
+ dispose: () => {
101
+ unsubscribe();
102
+ controller.destroy();
103
+ },
104
+ };
105
+ }
106
+
107
+ export type CreateStorefrontOptions = {
108
+ apiKey: string;
109
+ baseUrl: string;
110
+ pageSize?: number;
111
+ categoryId?: string | null;
112
+ };
113
+
114
+ export type StorefrontHandle = {
115
+ state: Signal<StoreSnapshot>;
116
+ /** Unsubscribe and destroy. Wire to `DestroyRef.onDestroy` in a component. */
117
+ destroy: () => void;
118
+ } & Omit<StoreController, "snapshot" | "subscribe" | "destroy" | "load">;
119
+
120
+ /**
121
+ * Headless storefront + cart.
122
+ *
123
+ * ```ts
124
+ * export class ShopComponent {
125
+ * private store = createStorefront({ apiKey, baseUrl });
126
+ * products = computed(() => this.store.state().products);
127
+ * constructor() { inject(DestroyRef).onDestroy(this.store.destroy); }
128
+ * }
129
+ * ```
130
+ */
131
+ export function createStorefront(
132
+ options: CreateStorefrontOptions,
133
+ ): StorefrontHandle {
134
+ const controller = createStoreController({
135
+ client: new BiabDevClient({
136
+ apiKey: options.apiKey,
137
+ baseUrl: options.baseUrl,
138
+ }),
139
+ ...(options.pageSize ? { pageSize: options.pageSize } : {}),
140
+ ...(options.categoryId ? { categoryId: options.categoryId } : {}),
141
+ });
142
+
143
+ const { state, dispose } = bridge(controller, () => void controller.load());
144
+
145
+ return {
146
+ state,
147
+ destroy: dispose,
148
+ loadMore: () => controller.loadMore(),
149
+ filterByCategory: (id: string | null) => controller.filterByCategory(id),
150
+ openProduct: (id: string) => controller.openProduct(id),
151
+ closeProduct: () => controller.closeProduct(),
152
+ addToCart: (input: CartAddItemInput) => controller.addToCart(input),
153
+ updateQuantity: (itemId: string, quantity: number) =>
154
+ controller.updateQuantity(itemId, quantity),
155
+ removeItem: (itemId: string) => controller.removeItem(itemId),
156
+ applyCoupon: (code: string) => controller.applyCoupon(code),
157
+ removeCoupon: () => controller.removeCoupon(),
158
+ clearCart: () => controller.clearCart(),
159
+ refreshCart: () => controller.refreshCart(),
160
+ };
161
+ }
162
+
163
+ export type CreateChatOptions = { apiKey: string; baseUrl: string };
164
+
165
+ export type ChatHandle = {
166
+ state: Signal<ChatSnapshot>;
167
+ destroy: () => void;
168
+ } & Omit<ChatController, "snapshot" | "subscribe" | "destroy">;
169
+
170
+ /** Headless Front Desk chat — the native alternative to `<Chatbot>`. */
171
+ export function createChat(options: CreateChatOptions): ChatHandle {
172
+ const controller = createChatController({
173
+ client: new BiabDevClient({
174
+ apiKey: options.apiKey,
175
+ baseUrl: options.baseUrl,
176
+ }),
177
+ });
178
+
179
+ const { state, dispose } = bridge(controller);
180
+
181
+ return {
182
+ state,
183
+ destroy: dispose,
184
+ send: (content: string) => controller.send(content),
185
+ reset: () => controller.reset(),
186
+ loadConfig: () => controller.loadConfig(),
187
+ refreshAvailability: () => controller.refreshAvailability(),
188
+ getForm: (slug: string) => controller.getForm(slug),
189
+ submitForm: (slug: string, input: ChatbotFormSubmitInput) =>
190
+ controller.submitForm(slug, input),
191
+ requestHuman: (input: ChatbotRequestHumanInput) =>
192
+ controller.requestHuman(input),
193
+ };
194
+ }
195
+
196
+ export type CreateBlogOptions = {
197
+ apiKey: string;
198
+ baseUrl: string;
199
+ pageSize?: number;
200
+ categoryId?: string | null;
201
+ };
202
+
203
+ export type BlogHandle = {
204
+ state: Signal<BlogSnapshot>;
205
+ destroy: () => void;
206
+ } & Omit<BlogController, "snapshot" | "subscribe" | "destroy" | "load">;
207
+
208
+ /** Headless blog — the native alternative to `<Blog>`. */
209
+ export function createBlog(options: CreateBlogOptions): BlogHandle {
210
+ const controller = createBlogController({
211
+ client: new BiabDevClient({
212
+ apiKey: options.apiKey,
213
+ baseUrl: options.baseUrl,
214
+ }),
215
+ ...(options.pageSize ? { pageSize: options.pageSize } : {}),
216
+ ...(options.categoryId ? { categoryId: options.categoryId } : {}),
217
+ });
218
+
219
+ const { state, dispose } = bridge(controller, () => void controller.load());
220
+
221
+ return {
222
+ state,
223
+ destroy: dispose,
224
+ loadMore: () => controller.loadMore(),
225
+ filterByCategory: (id: string | null) => controller.filterByCategory(id),
226
+ openPost: (slug: string) => controller.openPost(slug),
227
+ closePost: () => controller.closePost(),
228
+ loadComments: () => controller.loadComments(),
229
+ loadMoreComments: () => controller.loadMoreComments(),
230
+ postComment: (input: BlogCommentPostInput) =>
231
+ controller.postComment(input),
232
+ toggleLike: () => controller.toggleLike(),
233
+ };
234
+ }
235
+
236
+ export type CreateCheckoutOptions = { apiKey: string; baseUrl: string };
237
+ export type CheckoutHandle = {
238
+ state: Signal<CheckoutSnapshot>;
239
+ destroy: () => void;
240
+ } & Omit<CheckoutController, "snapshot" | "subscribe" | "destroy">;
241
+
242
+ /** Headless checkout. `start()` mints a session; `redirect()` navigates. */
243
+ export function createCheckout(
244
+ options: CreateCheckoutOptions,
245
+ ): CheckoutHandle {
246
+ const controller = createCheckoutController({
247
+ client: new BiabDevClient({
248
+ apiKey: options.apiKey,
249
+ baseUrl: options.baseUrl,
250
+ }),
251
+ });
252
+ const { state, dispose } = bridge(controller);
253
+ return {
254
+ state,
255
+ destroy: dispose,
256
+ start: (input) => controller.start(input),
257
+ redirect: () => controller.redirect(),
258
+ resolve: (sessionId: string) => controller.resolve(sessionId),
259
+ reset: () => controller.reset(),
260
+ };
261
+ }
262
+
263
+ export type UserHandle = {
264
+ state: Signal<UserSnapshot>;
265
+ destroy: () => void;
266
+ refresh: () => Promise<UserSnapshot>;
267
+ };
268
+
269
+ /** The signed-in customer, from the auth handler's `/me`. */
270
+ export function createUser(options: { basePath?: string } = {}): UserHandle {
271
+ const controller = createUserController(options);
272
+ const { state, dispose } = bridge(controller, () => controller.start());
273
+ return { state, destroy: dispose, refresh: () => controller.refresh() };
274
+ }
275
+
276
+ export type CreateFollowersOptions = {
277
+ token: string;
278
+ siteId: string;
279
+ baseUrl?: string;
280
+ };
281
+ export type FollowersHandle = {
282
+ state: Signal<FollowersSnapshot>;
283
+ destroy: () => void;
284
+ } & Omit<FollowersController, "snapshot" | "subscribe" | "destroy">;
285
+
286
+ /** Newsletter / follower subscribe with the local already-subscribed hint. */
287
+ export function createFollowers(
288
+ options: CreateFollowersOptions,
289
+ ): FollowersHandle {
290
+ const controller = createFollowersController({
291
+ client: createBiabClient({
292
+ apiKey: options.token,
293
+ siteId: options.siteId,
294
+ ...(options.baseUrl ? { baseUrl: options.baseUrl } : {}),
295
+ }),
296
+ siteId: options.siteId,
297
+ });
298
+ const { state, dispose } = bridge(controller);
299
+ return {
300
+ state,
301
+ destroy: dispose,
302
+ join: (input) => controller.join(input),
303
+ leave: (email: string) => controller.leave(email),
304
+ me: (email: string) => controller.me(email),
305
+ edit: (email: string, patch: { name?: string; source?: string }) =>
306
+ controller.edit(email, patch),
307
+ };
308
+ }
309
+
310
+ export {
311
+ signIn,
312
+ signInHref,
313
+ signOut,
314
+ signOutHref,
315
+ signUp,
316
+ signUpHref,
317
+ } from "../account-core/index.js";
318
+
319
+ export type CreateMarketingOptions = {
320
+ token: string;
321
+ siteId: string;
322
+ baseUrl?: string;
323
+ };
324
+
325
+ /** Headless marketing pages — the index, plus one page at a time. */
326
+ export function createMarketing(options: CreateMarketingOptions) {
327
+ const controller = createMarketingController({
328
+ client: createBiabClient({
329
+ apiKey: options.token,
330
+ siteId: options.siteId,
331
+ ...(options.baseUrl ? { baseUrl: options.baseUrl } : {}),
332
+ }),
333
+ });
334
+ const { state, dispose } = bridge(controller, () => void controller.load());
335
+ return {
336
+ state,
337
+ destroy: dispose,
338
+ open: (pageKey?: string, opts?: { skipCache?: boolean }) =>
339
+ controller.open(pageKey, opts),
340
+ prefetch: (pageKey?: string) => controller.prefetch(pageKey),
341
+ close: () => controller.close(),
342
+ };
343
+ }
344
+
345
+ export type CreateVerifyOptions = {
346
+ apiKey: string;
347
+ baseUrl: string;
348
+ } & Omit<CreateVerifyControllerOptions, "client">;
349
+
350
+ /** Headless email / phone verification. Read `state().flow` before rendering. */
351
+ export function createVerify(options: CreateVerifyOptions) {
352
+ const { apiKey, baseUrl, ...rest } = options;
353
+ const controller = createVerifyController({
354
+ client: new BiabDevClient({ apiKey, baseUrl }),
355
+ ...rest,
356
+ });
357
+ const { state, dispose } = bridge(controller);
358
+ return {
359
+ state,
360
+ destroy: dispose,
361
+ start: (destination: string) => controller.start(destination),
362
+ resend: () => controller.resend(),
363
+ confirm: (token: string) => controller.confirm(token),
364
+ canResend: (now?: number) => controller.canResend(now),
365
+ reset: () => controller.reset(),
366
+ };
367
+ }
368
+
369
+ export type CreatePortalOptions = {
370
+ apiKey: string;
371
+ baseUrl: string;
372
+ sessionToken: string;
373
+ };
374
+
375
+ /**
376
+ * The signed-in customer's portal — work, quotes, contracts, invoices, orders,
377
+ * downloads, shipping, reviews, referrals, profile and notification settings.
378
+ *
379
+ * `sessionToken` is required: every portal route is scoped to the signed-in
380
+ * customer, and without one the portal renders empty rather than signed-out.
381
+ *
382
+ * Note two things the API itself imposes. There are no list endpoints for
383
+ * invoices, quotes, contracts or shipments — `state.work` carries the counts
384
+ * and you open each by id. And a submitted review is queued for staff approval,
385
+ * so it will not appear in `state.reviews`; watch `reviewAwaitingModeration`.
386
+ */
387
+ export function createPortal(options: CreatePortalOptions) {
388
+ const controller = createPortalController({
389
+ client: new BiabDevClient({
390
+ apiKey: options.apiKey,
391
+ baseUrl: options.baseUrl,
392
+ }),
393
+ sessionToken: options.sessionToken,
394
+ });
395
+ const { state, dispose } = bridge(controller, () => void controller.load());
396
+ return {
397
+ state: state as Signal<PortalSnapshot>,
398
+ destroy: dispose,
399
+ load: () => controller.load(),
400
+ loadOrders: (opts?: { limit?: number }) => controller.loadOrders(opts),
401
+ loadInvoices: (opts?: {
402
+ limit?: number;
403
+ status?: string;
404
+ unpaid?: boolean;
405
+ }) => controller.loadInvoices(opts),
406
+ loadQuotes: (opts?: { limit?: number; status?: string }) =>
407
+ controller.loadQuotes(opts),
408
+ loadContracts: (opts?: { limit?: number; status?: string }) =>
409
+ controller.loadContracts(opts),
410
+ loadShipments: (opts?: { limit?: number; active?: boolean }) =>
411
+ controller.loadShipments(opts),
412
+ loadMessages: (opts?: { limit?: number; jobId?: string }) =>
413
+ controller.loadMessages(opts),
414
+ postMessage: (input: CustomerPortalMessagePostInput) =>
415
+ controller.postMessage(input),
416
+ markMessagesRead: () => controller.markMessagesRead(),
417
+ loadPayments: (opts?: { limit?: number }) => controller.loadPayments(opts),
418
+ loadReviews: () => controller.loadReviews(),
419
+ loadReferrals: () => controller.loadReferrals(),
420
+ loadNotificationPreferences: () => controller.loadNotificationPreferences(),
421
+ loadOtherOrgs: () => controller.loadOtherOrgs(),
422
+ openJob: (id: string) => controller.openJob(id),
423
+ openOrder: (id: string) => controller.openOrder(id),
424
+ openInvoice: (id: string) => controller.openInvoice(id),
425
+ openQuote: (id: string) => controller.openQuote(id),
426
+ openContract: (id: string) => controller.openContract(id),
427
+ trackShipment: (id: string) => controller.trackShipment(id),
428
+ closeDetail: () => controller.closeDetail(),
429
+ postJobComment: controller.postJobComment,
430
+ acceptQuote: (id: string) => controller.acceptQuote(id),
431
+ rejectQuote: (id: string) => controller.rejectQuote(id),
432
+ signContract: (id: string) => controller.signContract(id),
433
+ payInvoice: controller.payInvoice,
434
+ updateProfile: controller.updateProfile,
435
+ updateNotificationPreferences: controller.updateNotificationPreferences,
436
+ submitReview: controller.submitReview,
437
+ submitReturnRequest: controller.submitReturnRequest,
438
+ contactCompany: controller.contactCompany,
439
+ };
440
+ }
441
+
442
+ import {
443
+ resolveSocialLinks,
444
+ SOCIAL_FALLBACK_GLYPH,
445
+ SOCIAL_LIST_STYLE_STRING,
446
+ type SocialLinkRow,
447
+ } from "../social-core/index.js";
448
+
449
+ /**
450
+ * The company's social profiles as icon links.
451
+ *
452
+ * Standalone with an inline template so it needs no NgModule and no separate
453
+ * `.html`, which keeps it inside this package's typecheck.
454
+ */
455
+ @Component({
456
+ selector: "biab-social-links",
457
+ standalone: true,
458
+ template: `
459
+ <ul *ngIf="rows.length" [attr.style]="listStyle">
460
+ <li *ngFor="let row of rows">
461
+ <a
462
+ [href]="row.url"
463
+ target="_blank"
464
+ rel="noopener noreferrer"
465
+ [title]="row.label"
466
+ [attr.aria-label]="row.label"
467
+ [class]="linkClass"
468
+ >
469
+ <img
470
+ *ngIf="row.iconUrl"
471
+ [src]="row.iconUrl"
472
+ [alt]="row.label"
473
+ [width]="size"
474
+ [height]="size"
475
+ loading="lazy"
476
+ [class]="iconClass"
477
+ />
478
+ <svg
479
+ *ngIf="!row.iconUrl"
480
+ [attr.viewBox]="glyph.viewBox"
481
+ [attr.width]="size"
482
+ [attr.height]="size"
483
+ fill="none"
484
+ stroke="currentColor"
485
+ [attr.stroke-width]="glyph.strokeWidth"
486
+ aria-hidden="true"
487
+ [class]="iconClass"
488
+ >
489
+ <title>{{ row.label }}</title>
490
+ <circle
491
+ [attr.cx]="glyph.circle.cx"
492
+ [attr.cy]="glyph.circle.cy"
493
+ [attr.r]="glyph.circle.r"
494
+ />
495
+ <path [attr.d]="glyph.path" />
496
+ </svg>
497
+ </a>
498
+ </li>
499
+ </ul>
500
+ `,
501
+ })
502
+ export class BiabSocialLinksComponent {
503
+ /** The company profile's `socials` record. */
504
+ @Input() set socials(value: unknown) {
505
+ this.rows = resolveSocialLinks(value, { color: this.color });
506
+ }
507
+ /** Icon colour — hex without `#`, or a CSS keyword. */
508
+ @Input() color?: string;
509
+ /** Icon edge length in px. */
510
+ @Input() size = 20;
511
+ @Input() linkClass?: string;
512
+ @Input() iconClass?: string;
513
+
514
+ rows: SocialLinkRow[] = [];
515
+ readonly listStyle = SOCIAL_LIST_STYLE_STRING;
516
+ readonly glyph = SOCIAL_FALLBACK_GLYPH;
517
+ }
518
+
519
+ export { resolveSocialLinks } from "../social-core/index.js";
@@ -24,3 +24,6 @@ export {
24
24
  // Re-export the headless core (controller + validation + SDK form types) so a
25
25
  // consumer reaches the full forms surface from the single `/angular` entry.
26
26
  export * from "../forms-core/index.js";
27
+
28
+ // Headless storefront + chat — the native (non-iframe) surfaces.
29
+ export * from "./headless.js";
@@ -0,0 +1,76 @@
1
+ /**
2
+ * URL construction for the tenant auth handler, shared by every binding.
3
+ *
4
+ * The handler (`createAuthHandler`) is the one server-side piece a consumer
5
+ * mounts; the sign-in / sign-up / sign-out surfaces in each framework are just
6
+ * links into it. Framework-free so a second binding doesn't fork the
7
+ * query-parameter contract.
8
+ *
9
+ * @module
10
+ */
11
+
12
+ /** Where `createAuthHandler` is mounted unless the consumer says otherwise. */
13
+ export const DEFAULT_AUTH_BASE_PATH = "/api/biab-auth";
14
+
15
+ export type AuthHrefOptions = {
16
+ /** Where to send the user after auth completes. */
17
+ returnTo?: string | undefined;
18
+ /** Pre-fill the email field on the platform-hosted page. */
19
+ loginHint?: string | undefined;
20
+ /** Override the basePath the auth handler is mounted at. */
21
+ basePath?: string | undefined;
22
+ };
23
+
24
+ /** Build the href for one of the auth handler's routes. */
25
+ export function buildAuthHref(
26
+ subroute: "sign-in" | "sign-up" | "sign-out",
27
+ options: AuthHrefOptions,
28
+ ): string {
29
+ const base = (options.basePath ?? DEFAULT_AUTH_BASE_PATH).replace(/\/$/, "");
30
+ const params = new URLSearchParams();
31
+ if (options.returnTo) params.set("returnTo", options.returnTo);
32
+ if (options.loginHint) params.set("loginHint", options.loginHint);
33
+ const qs = params.toString();
34
+ return `${base}/${subroute}${qs ? `?${qs}` : ""}`;
35
+ }
36
+
37
+ /** Local-storage bookkeeping for the "this browser already subscribed" hint. */
38
+ export type LocalFollow = { email: string; at: number };
39
+
40
+ export const FOLLOWER_STORE_PREFIX = "biab.followers.";
41
+
42
+ export function followerStoreKey(siteId: string): string {
43
+ return `${FOLLOWER_STORE_PREFIX}${siteId}`;
44
+ }
45
+
46
+ export function readLocalFollow(key: string): LocalFollow | null {
47
+ if (typeof window === "undefined") return null;
48
+ try {
49
+ const raw = window.localStorage.getItem(key);
50
+ if (!raw) return null;
51
+ const parsed = JSON.parse(raw) as Partial<LocalFollow>;
52
+ return typeof parsed?.email === "string"
53
+ ? { email: parsed.email, at: Number(parsed.at) || 0 }
54
+ : null;
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+
60
+ export function writeLocalFollow(key: string, value: LocalFollow): void {
61
+ if (typeof window === "undefined") return;
62
+ try {
63
+ window.localStorage.setItem(key, JSON.stringify(value));
64
+ } catch {
65
+ // storage disabled / quota — the local hint is best-effort.
66
+ }
67
+ }
68
+
69
+ export function clearLocalFollow(key: string): void {
70
+ if (typeof window === "undefined") return;
71
+ try {
72
+ window.localStorage.removeItem(key);
73
+ } catch {
74
+ // ignore
75
+ }
76
+ }