@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
package/src/qwik.tsx CHANGED
@@ -269,13 +269,16 @@ const BiabFieldInput = component$<{
269
269
  ) : null;
270
270
 
271
271
  case "textarea":
272
+ // `autocomplete` and `accept` below are SPREAD rather than passed as
273
+ // `attr={maybeUndefined}`. Under `exactOptionalPropertyTypes` an
274
+ // optional attribute must be absent, not present-and-undefined.
272
275
  return (
273
276
  <textarea
274
277
  class="biab-input biab-textarea"
275
278
  value={asString(value)}
276
279
  placeholder={field.placeholder || ""}
277
280
  rows={4}
278
- autocomplete={autoComplete as AutoFill | undefined}
281
+ {...(autoComplete ? { autocomplete: autoComplete as AutoFill } : {})}
279
282
  onInput$={(_, el) => void set((el as HTMLTextAreaElement).value)}
280
283
  />
281
284
  );
@@ -656,7 +659,7 @@ const BiabFieldInput = component$<{
656
659
  class="biab-file__input"
657
660
  type="file"
658
661
  multiple={maxFiles > 1}
659
- accept={accept}
662
+ {...(accept ? { accept } : {})}
660
663
  disabled={uploading || remaining === 0}
661
664
  onChange$={async (_, el) => {
662
665
  const picked = (el as HTMLInputElement).files;
@@ -1232,3 +1235,600 @@ export const BiabForm = component$<BiabFormProps>((props) => {
1232
1235
  });
1233
1236
 
1234
1237
  export default BiabForm;
1238
+
1239
+ // ─────────────────────────────────────────────────────────────────────────────
1240
+ // Native storefront + chat
1241
+ //
1242
+ // The controllers are NOT serializable, so they can never live in a `useStore`
1243
+ // or cross Qwik's resumability boundary. They are created inside
1244
+ // `useVisibleTask$` — client-side, after hydration — and their snapshots are
1245
+ // copied into a serializable signal that the template reads. That is the same
1246
+ // shape `<BiabForm>` already uses for its controller.
1247
+ // ─────────────────────────────────────────────────────────────────────────────
1248
+
1249
+ import {
1250
+ createFollowersController,
1251
+ createUserController,
1252
+ type FollowersController,
1253
+ type FollowersSnapshot,
1254
+ type UserController,
1255
+ type UserSnapshot,
1256
+ } from "./account-core/index.js";
1257
+ import {
1258
+ type CheckoutController,
1259
+ type CheckoutSnapshot,
1260
+ createCheckoutController,
1261
+ } from "./checkout-core/index.js";
1262
+ import {
1263
+ createPortalController,
1264
+ type PortalController,
1265
+ type PortalSnapshot,
1266
+ } from "./portal-core/index.js";
1267
+ import {
1268
+ createMarketingController,
1269
+ type MarketingController,
1270
+ type MarketingSnapshot,
1271
+ } from "./marketing-core/index.js";
1272
+ import {
1273
+ type CreateVerifyControllerOptions,
1274
+ createVerifyController,
1275
+ type VerifyController,
1276
+ type VerifySnapshot,
1277
+ } from "./verify-core/index.js";
1278
+ import { createBiabClient as createBiabClientForFollowers } from "./sdk.js";
1279
+ import {
1280
+ type BlogController,
1281
+ type BlogSnapshot,
1282
+ createBlogController,
1283
+ } from "./blog-core/index.js";
1284
+ import {
1285
+ type ChatController,
1286
+ type ChatSnapshot,
1287
+ createChatController,
1288
+ } from "./chat-core/index.js";
1289
+ import { BiabDevClient as BiabDevClientForSurfaces } from "./client.js";
1290
+ import {
1291
+ createStoreController,
1292
+ type StoreController,
1293
+ type StoreSnapshot,
1294
+ } from "./store-core/index.js";
1295
+
1296
+ export type UseStorefrontOptions = {
1297
+ apiKey: string;
1298
+ baseUrl: string;
1299
+ pageSize?: number;
1300
+ categoryId?: string | null;
1301
+ };
1302
+
1303
+ /**
1304
+ * Headless storefront + cart.
1305
+ *
1306
+ * Returns a signal of the snapshot plus QRL-safe actions. Actions are plain
1307
+ * functions closed over the client-side controller, so call them from an
1308
+ * event handler (`onClick$`) rather than during render.
1309
+ *
1310
+ * ```tsx
1311
+ * const store = useStorefront({ apiKey, baseUrl });
1312
+ * return <>{store.state.value.products.map((p) => <MyCard product={p} />)}</>;
1313
+ * ```
1314
+ */
1315
+ export function useStorefront(options: UseStorefrontOptions) {
1316
+ const state = useSignal<StoreSnapshot | null>(null);
1317
+ // `noSerialize` is mandatory, not stylistic: a controller holds a client, a
1318
+ // subscriber set and timers. Qwik would try to serialise it into the HTML on
1319
+ // pause and throw. Marked this way it simply becomes `undefined` after a
1320
+ // resume, which is correct — the visible task recreates it client-side.
1321
+ const actions = useStore<{ current: NoSerialize<StoreController> | undefined }>(
1322
+ { current: undefined },
1323
+ { deep: false },
1324
+ );
1325
+
1326
+ useVisibleTask$(({ cleanup }) => {
1327
+ const controller = createStoreController({
1328
+ client: new BiabDevClientForSurfaces({
1329
+ apiKey: options.apiKey,
1330
+ baseUrl: options.baseUrl,
1331
+ }),
1332
+ ...(options.pageSize ? { pageSize: options.pageSize } : {}),
1333
+ ...(options.categoryId ? { categoryId: options.categoryId } : {}),
1334
+ });
1335
+ actions.current = noSerialize(controller);
1336
+ state.value = controller.snapshot();
1337
+ const unsubscribe = controller.subscribe((next) => {
1338
+ state.value = next;
1339
+ });
1340
+ void controller.load();
1341
+ cleanup(() => {
1342
+ unsubscribe();
1343
+ controller.destroy();
1344
+ actions.current = undefined;
1345
+ });
1346
+ });
1347
+
1348
+ return {
1349
+ state,
1350
+ loadMore: () => actions.current?.loadMore(),
1351
+ filterByCategory: (id: string | null) =>
1352
+ actions.current?.filterByCategory(id),
1353
+ openProduct: (id: string) => actions.current?.openProduct(id),
1354
+ closeProduct: () => actions.current?.closeProduct(),
1355
+ // biome-ignore lint/suspicious/noExplicitAny: input mirrors CartAddItemInput
1356
+ addToCart: (input: any) => actions.current?.addToCart(input),
1357
+ updateQuantity: (itemId: string, quantity: number) =>
1358
+ actions.current?.updateQuantity(itemId, quantity),
1359
+ removeItem: (itemId: string) => actions.current?.removeItem(itemId),
1360
+ applyCoupon: (code: string) => actions.current?.applyCoupon(code),
1361
+ removeCoupon: () => actions.current?.removeCoupon(),
1362
+ clearCart: () => actions.current?.clearCart(),
1363
+ refreshCart: () => actions.current?.refreshCart(),
1364
+ };
1365
+ }
1366
+
1367
+ export type UseChatOptions = { apiKey: string; baseUrl: string };
1368
+
1369
+ /** Headless Front Desk chat — the native alternative to the iframe widget. */
1370
+ export function useChat(options: UseChatOptions) {
1371
+ const state = useSignal<ChatSnapshot | null>(null);
1372
+ const actions = useStore<{ current: NoSerialize<ChatController> | undefined }>(
1373
+ { current: undefined },
1374
+ { deep: false },
1375
+ );
1376
+
1377
+ useVisibleTask$(({ cleanup }) => {
1378
+ const controller = createChatController({
1379
+ client: new BiabDevClientForSurfaces({
1380
+ apiKey: options.apiKey,
1381
+ baseUrl: options.baseUrl,
1382
+ }),
1383
+ });
1384
+ actions.current = noSerialize(controller);
1385
+ state.value = controller.snapshot();
1386
+ const unsubscribe = controller.subscribe((next) => {
1387
+ state.value = next;
1388
+ });
1389
+ cleanup(() => {
1390
+ unsubscribe();
1391
+ controller.destroy();
1392
+ actions.current = undefined;
1393
+ });
1394
+ });
1395
+
1396
+ return {
1397
+ state,
1398
+ send: (content: string) => actions.current?.send(content),
1399
+ reset: () => actions.current?.reset(),
1400
+ loadConfig: () => actions.current?.loadConfig(),
1401
+ refreshAvailability: () => actions.current?.refreshAvailability(),
1402
+ getForm: (slug: string) => actions.current?.getForm(slug),
1403
+ // biome-ignore lint/suspicious/noExplicitAny: input mirrors ChatbotFormSubmitInput
1404
+ submitForm: (slug: string, input: any) =>
1405
+ actions.current?.submitForm(slug, input),
1406
+ // biome-ignore lint/suspicious/noExplicitAny: input mirrors ChatbotRequestHumanInput
1407
+ requestHuman: (input: any) => actions.current?.requestHuman(input),
1408
+ };
1409
+ }
1410
+
1411
+ export type UseBlogOptions = {
1412
+ apiKey: string;
1413
+ baseUrl: string;
1414
+ pageSize?: number;
1415
+ categoryId?: string | null;
1416
+ };
1417
+
1418
+ /** Headless blog — the native alternative to the hosted `<Blog>` iframe. */
1419
+ export function useBlog(options: UseBlogOptions) {
1420
+ const state = useSignal<BlogSnapshot | null>(null);
1421
+ const actions = useStore<{ current: NoSerialize<BlogController> | undefined }>(
1422
+ { current: undefined },
1423
+ { deep: false },
1424
+ );
1425
+
1426
+ useVisibleTask$(({ cleanup }) => {
1427
+ const controller = createBlogController({
1428
+ client: new BiabDevClientForSurfaces({
1429
+ apiKey: options.apiKey,
1430
+ baseUrl: options.baseUrl,
1431
+ }),
1432
+ ...(options.pageSize ? { pageSize: options.pageSize } : {}),
1433
+ ...(options.categoryId ? { categoryId: options.categoryId } : {}),
1434
+ });
1435
+ actions.current = noSerialize(controller);
1436
+ state.value = controller.snapshot();
1437
+ const unsubscribe = controller.subscribe((next) => {
1438
+ state.value = next;
1439
+ });
1440
+ void controller.load();
1441
+ cleanup(() => {
1442
+ unsubscribe();
1443
+ controller.destroy();
1444
+ actions.current = undefined;
1445
+ });
1446
+ });
1447
+
1448
+ return {
1449
+ state,
1450
+ loadMore: () => actions.current?.loadMore(),
1451
+ filterByCategory: (id: string | null) =>
1452
+ actions.current?.filterByCategory(id),
1453
+ openPost: (slug: string) => actions.current?.openPost(slug),
1454
+ closePost: () => actions.current?.closePost(),
1455
+ loadComments: () => actions.current?.loadComments(),
1456
+ loadMoreComments: () => actions.current?.loadMoreComments(),
1457
+ // biome-ignore lint/suspicious/noExplicitAny: input mirrors BlogCommentPostInput
1458
+ postComment: (input: any) => actions.current?.postComment(input),
1459
+ toggleLike: () => actions.current?.toggleLike(),
1460
+ };
1461
+ }
1462
+
1463
+ /** Headless checkout. `start()` mints a session; `redirect()` navigates. */
1464
+ export function useCheckout(options: { apiKey: string; baseUrl: string }) {
1465
+ const state = useSignal<CheckoutSnapshot | null>(null);
1466
+ const actions = useStore<{
1467
+ current: NoSerialize<CheckoutController> | undefined;
1468
+ }>({ current: undefined }, { deep: false });
1469
+
1470
+ useVisibleTask$(({ cleanup }) => {
1471
+ const controller = createCheckoutController({
1472
+ client: new BiabDevClientForSurfaces({
1473
+ apiKey: options.apiKey,
1474
+ baseUrl: options.baseUrl,
1475
+ }),
1476
+ });
1477
+ actions.current = noSerialize(controller);
1478
+ state.value = controller.snapshot();
1479
+ const unsubscribe = controller.subscribe((next) => {
1480
+ state.value = next;
1481
+ });
1482
+ cleanup(() => {
1483
+ unsubscribe();
1484
+ controller.destroy();
1485
+ actions.current = undefined;
1486
+ });
1487
+ });
1488
+
1489
+ return {
1490
+ state,
1491
+ // biome-ignore lint/suspicious/noExplicitAny: input mirrors CheckoutSessionInput
1492
+ start: (input: any) => actions.current?.start(input),
1493
+ redirect: () => actions.current?.redirect(),
1494
+ resolve: (sessionId: string) => actions.current?.resolve(sessionId),
1495
+ reset: () => actions.current?.reset(),
1496
+ };
1497
+ }
1498
+
1499
+ /** The signed-in customer, from the auth handler's `/me`. */
1500
+ export function useUser(options: { basePath?: string } = {}) {
1501
+ const state = useSignal<UserSnapshot | null>(null);
1502
+ const actions = useStore<{ current: NoSerialize<UserController> | undefined }>(
1503
+ { current: undefined },
1504
+ { deep: false },
1505
+ );
1506
+
1507
+ useVisibleTask$(({ cleanup }) => {
1508
+ const controller = createUserController(options);
1509
+ actions.current = noSerialize(controller);
1510
+ state.value = controller.snapshot();
1511
+ const unsubscribe = controller.subscribe((next) => {
1512
+ state.value = next;
1513
+ });
1514
+ controller.start();
1515
+ cleanup(() => {
1516
+ unsubscribe();
1517
+ controller.destroy();
1518
+ actions.current = undefined;
1519
+ });
1520
+ });
1521
+
1522
+ return { state, refresh: () => actions.current?.refresh() };
1523
+ }
1524
+
1525
+ /** Newsletter / follower subscribe with the local already-subscribed hint. */
1526
+ export function useFollowers(options: {
1527
+ token: string;
1528
+ siteId: string;
1529
+ baseUrl?: string;
1530
+ }) {
1531
+ const state = useSignal<FollowersSnapshot | null>(null);
1532
+ const actions = useStore<{
1533
+ current: NoSerialize<FollowersController> | undefined;
1534
+ }>({ current: undefined }, { deep: false });
1535
+
1536
+ useVisibleTask$(({ cleanup }) => {
1537
+ const controller = createFollowersController({
1538
+ client: createBiabClientForFollowers({
1539
+ apiKey: options.token,
1540
+ siteId: options.siteId,
1541
+ ...(options.baseUrl ? { baseUrl: options.baseUrl } : {}),
1542
+ }),
1543
+ siteId: options.siteId,
1544
+ });
1545
+ actions.current = noSerialize(controller);
1546
+ state.value = controller.snapshot();
1547
+ const unsubscribe = controller.subscribe((next) => {
1548
+ state.value = next;
1549
+ });
1550
+ cleanup(() => {
1551
+ unsubscribe();
1552
+ controller.destroy();
1553
+ actions.current = undefined;
1554
+ });
1555
+ });
1556
+
1557
+ return {
1558
+ state,
1559
+ join: (input: { email: string; name?: string; source?: string }) =>
1560
+ actions.current?.join(input),
1561
+ leave: (email: string) => actions.current?.leave(email),
1562
+ me: (email: string) => actions.current?.me(email),
1563
+ edit: (email: string, patch: { name?: string; source?: string }) =>
1564
+ actions.current?.edit(email, patch),
1565
+ };
1566
+ }
1567
+
1568
+ export {
1569
+ signIn,
1570
+ signInHref,
1571
+ signOut,
1572
+ signOutHref,
1573
+ signUp,
1574
+ signUpHref,
1575
+ } from "./account-core/index.js";
1576
+
1577
+ /** Headless marketing pages — the index, plus one page at a time. */
1578
+ export function useMarketing(options: {
1579
+ token: string;
1580
+ siteId: string;
1581
+ baseUrl?: string;
1582
+ }) {
1583
+ const state = useSignal<MarketingSnapshot | null>(null);
1584
+ const actions = useStore<{
1585
+ current: NoSerialize<MarketingController> | undefined;
1586
+ }>({ current: undefined }, { deep: false });
1587
+
1588
+ useVisibleTask$(({ cleanup }) => {
1589
+ const controller = createMarketingController({
1590
+ client: createBiabClientForFollowers({
1591
+ apiKey: options.token,
1592
+ siteId: options.siteId,
1593
+ ...(options.baseUrl ? { baseUrl: options.baseUrl } : {}),
1594
+ }),
1595
+ });
1596
+ actions.current = noSerialize(controller);
1597
+ state.value = controller.snapshot();
1598
+ const unsubscribe = controller.subscribe((next) => {
1599
+ state.value = next;
1600
+ });
1601
+ void controller.load();
1602
+ cleanup(() => {
1603
+ unsubscribe();
1604
+ controller.destroy();
1605
+ actions.current = undefined;
1606
+ });
1607
+ });
1608
+
1609
+ return {
1610
+ state,
1611
+ open: (pageKey?: string, opts?: { skipCache?: boolean }) =>
1612
+ actions.current?.open(pageKey, opts),
1613
+ prefetch: (pageKey?: string) => actions.current?.prefetch(pageKey),
1614
+ close: () => actions.current?.close(),
1615
+ };
1616
+ }
1617
+
1618
+ /** Headless email / phone verification. Read `state.value.flow` before rendering. */
1619
+ export function useVerify(
1620
+ options: { apiKey: string; baseUrl: string } & Omit<
1621
+ CreateVerifyControllerOptions,
1622
+ "client"
1623
+ >,
1624
+ ) {
1625
+ const state = useSignal<VerifySnapshot | null>(null);
1626
+ const actions = useStore<{
1627
+ current: NoSerialize<VerifyController> | undefined;
1628
+ }>({ current: undefined }, { deep: false });
1629
+
1630
+ useVisibleTask$(({ cleanup }) => {
1631
+ const { apiKey, baseUrl, ...rest } = options;
1632
+ const controller = createVerifyController({
1633
+ client: new BiabDevClientForSurfaces({ apiKey, baseUrl }),
1634
+ ...rest,
1635
+ });
1636
+ actions.current = noSerialize(controller);
1637
+ state.value = controller.snapshot();
1638
+ const unsubscribe = controller.subscribe((next) => {
1639
+ state.value = next;
1640
+ });
1641
+ cleanup(() => {
1642
+ unsubscribe();
1643
+ controller.destroy();
1644
+ actions.current = undefined;
1645
+ });
1646
+ });
1647
+
1648
+ return {
1649
+ state,
1650
+ start: (destination: string) => actions.current?.start(destination),
1651
+ resend: () => actions.current?.resend(),
1652
+ confirm: (token: string) => actions.current?.confirm(token),
1653
+ canResend: (now?: number) => actions.current?.canResend(now) ?? false,
1654
+ reset: () => actions.current?.reset(),
1655
+ };
1656
+ }
1657
+
1658
+ /**
1659
+ * The signed-in customer's portal. `sessionToken` is required — every portal
1660
+ * route is scoped to the customer.
1661
+ *
1662
+ * There are no list endpoints for invoices, quotes, contracts or shipments:
1663
+ * `state.value.work` carries the counts and you open each by id. A submitted
1664
+ * review is queued for staff approval and will not appear in `reviews`.
1665
+ */
1666
+ export function usePortal(options: {
1667
+ apiKey: string;
1668
+ baseUrl: string;
1669
+ sessionToken: string;
1670
+ }) {
1671
+ const state = useSignal<PortalSnapshot | null>(null);
1672
+ const actions = useStore<{
1673
+ current: NoSerialize<PortalController> | undefined;
1674
+ }>({ current: undefined }, { deep: false });
1675
+
1676
+ useVisibleTask$(({ cleanup }) => {
1677
+ const controller = createPortalController({
1678
+ client: new BiabDevClientForSurfaces({
1679
+ apiKey: options.apiKey,
1680
+ baseUrl: options.baseUrl,
1681
+ }),
1682
+ sessionToken: options.sessionToken,
1683
+ });
1684
+ actions.current = noSerialize(controller);
1685
+ state.value = controller.snapshot();
1686
+ const unsubscribe = controller.subscribe((next) => {
1687
+ state.value = next;
1688
+ });
1689
+ void controller.load();
1690
+ cleanup(() => {
1691
+ unsubscribe();
1692
+ controller.destroy();
1693
+ actions.current = undefined;
1694
+ });
1695
+ });
1696
+
1697
+ return {
1698
+ state,
1699
+ loadOrders: (opts?: { limit?: number }) =>
1700
+ actions.current?.loadOrders(opts),
1701
+ loadInvoices: (opts?: {
1702
+ limit?: number;
1703
+ status?: string;
1704
+ unpaid?: boolean;
1705
+ }) => actions.current?.loadInvoices(opts),
1706
+ loadQuotes: (opts?: { limit?: number; status?: string }) =>
1707
+ actions.current?.loadQuotes(opts),
1708
+ loadContracts: (opts?: { limit?: number; status?: string }) =>
1709
+ actions.current?.loadContracts(opts),
1710
+ loadShipments: (opts?: { limit?: number; active?: boolean }) =>
1711
+ actions.current?.loadShipments(opts),
1712
+ loadMessages: (opts?: { limit?: number; jobId?: string }) =>
1713
+ actions.current?.loadMessages(opts),
1714
+ // biome-ignore lint/suspicious/noExplicitAny: input mirrors the contract
1715
+ postMessage: (input: any) => actions.current?.postMessage(input),
1716
+ markMessagesRead: () => actions.current?.markMessagesRead(),
1717
+ loadPayments: (opts?: { limit?: number }) =>
1718
+ actions.current?.loadPayments(opts),
1719
+ loadReviews: () => actions.current?.loadReviews(),
1720
+ loadReferrals: () => actions.current?.loadReferrals(),
1721
+ loadNotificationPreferences: () =>
1722
+ actions.current?.loadNotificationPreferences(),
1723
+ loadOtherOrgs: () => actions.current?.loadOtherOrgs(),
1724
+ openJob: (id: string) => actions.current?.openJob(id),
1725
+ openOrder: (id: string) => actions.current?.openOrder(id),
1726
+ openInvoice: (id: string) => actions.current?.openInvoice(id),
1727
+ openQuote: (id: string) => actions.current?.openQuote(id),
1728
+ openContract: (id: string) => actions.current?.openContract(id),
1729
+ trackShipment: (id: string) => actions.current?.trackShipment(id),
1730
+ closeDetail: () => actions.current?.closeDetail(),
1731
+ // biome-ignore lint/suspicious/noExplicitAny: inputs mirror the contracts
1732
+ postJobComment: (jobId: string, input: any) =>
1733
+ actions.current?.postJobComment(jobId, input),
1734
+ acceptQuote: (id: string) => actions.current?.acceptQuote(id),
1735
+ rejectQuote: (id: string) => actions.current?.rejectQuote(id),
1736
+ signContract: (id: string) => actions.current?.signContract(id),
1737
+ // biome-ignore lint/suspicious/noExplicitAny: inputs mirror the contracts
1738
+ payInvoice: (id: string, opts?: any) =>
1739
+ actions.current?.payInvoice(id, opts),
1740
+ // biome-ignore lint/suspicious/noExplicitAny: inputs mirror the contracts
1741
+ updateProfile: (input: any) => actions.current?.updateProfile(input),
1742
+ // biome-ignore lint/suspicious/noExplicitAny: inputs mirror the contracts
1743
+ updateNotificationPreferences: (input: any) =>
1744
+ actions.current?.updateNotificationPreferences(input),
1745
+ // biome-ignore lint/suspicious/noExplicitAny: inputs mirror the contracts
1746
+ submitReview: (input: any) => actions.current?.submitReview(input),
1747
+ // biome-ignore lint/suspicious/noExplicitAny: inputs mirror the contracts
1748
+ submitReturnRequest: (orderId: string, input?: any) =>
1749
+ actions.current?.submitReturnRequest(orderId, input),
1750
+ // biome-ignore lint/suspicious/noExplicitAny: inputs mirror the contracts
1751
+ contactCompany: (input: any) => actions.current?.contactCompany(input),
1752
+ };
1753
+ }
1754
+
1755
+ import {
1756
+ resolveSocialLinks,
1757
+ SOCIAL_FALLBACK_GLYPH,
1758
+ SOCIAL_LIST_STYLE,
1759
+ } from "./social-core/index.js";
1760
+
1761
+ export type SocialLinksProps = {
1762
+ /** The company profile's `socials` record. */
1763
+ socials: unknown;
1764
+ /** Icon colour — hex without `#`, or a CSS keyword. */
1765
+ color?: string;
1766
+ /** Icon edge length in px. */
1767
+ size?: number;
1768
+ linkClass?: string;
1769
+ iconClass?: string;
1770
+ };
1771
+
1772
+ /**
1773
+ * The company's social profiles as icon links.
1774
+ *
1775
+ * Pure render from serializable props, so it needs no `useVisibleTask$` and
1776
+ * survives resumption untouched — unlike the controller-backed surfaces above.
1777
+ */
1778
+ export const SocialLinks = component$<SocialLinksProps>((props) => {
1779
+ const size = props.size ?? 20;
1780
+ const rows = resolveSocialLinks(props.socials, {
1781
+ ...(props.color ? { color: props.color } : {}),
1782
+ });
1783
+ // Nothing at all rather than an empty <ul> in a footer.
1784
+ if (rows.length === 0) return null;
1785
+
1786
+ return (
1787
+ <ul style={SOCIAL_LIST_STYLE}>
1788
+ {rows.map((row) => (
1789
+ <li key={row.key}>
1790
+ <a
1791
+ aria-label={row.label}
1792
+ class={props.linkClass}
1793
+ href={row.url}
1794
+ rel="noopener noreferrer"
1795
+ target="_blank"
1796
+ title={row.label}
1797
+ >
1798
+ {row.iconUrl ? (
1799
+ <img
1800
+ alt={row.label}
1801
+ class={props.iconClass}
1802
+ height={size}
1803
+ loading="lazy"
1804
+ src={row.iconUrl}
1805
+ width={size}
1806
+ />
1807
+ ) : (
1808
+ <svg
1809
+ aria-hidden="true"
1810
+ class={props.iconClass}
1811
+ fill="none"
1812
+ height={size}
1813
+ stroke="currentColor"
1814
+ stroke-width={SOCIAL_FALLBACK_GLYPH.strokeWidth}
1815
+ viewBox={SOCIAL_FALLBACK_GLYPH.viewBox}
1816
+ width={size}
1817
+ >
1818
+ <title>{row.label}</title>
1819
+ <circle
1820
+ cx={SOCIAL_FALLBACK_GLYPH.circle.cx}
1821
+ cy={SOCIAL_FALLBACK_GLYPH.circle.cy}
1822
+ r={SOCIAL_FALLBACK_GLYPH.circle.r}
1823
+ />
1824
+ <path d={SOCIAL_FALLBACK_GLYPH.path} />
1825
+ </svg>
1826
+ )}
1827
+ </a>
1828
+ </li>
1829
+ ))}
1830
+ </ul>
1831
+ );
1832
+ });
1833
+
1834
+ export { resolveSocialLinks } from "./social-core/index.js";
@@ -1,6 +1,11 @@
1
+ "use client";
2
+
1
3
  /**
2
4
  * React hooks for the schema-driven marketing-page flow.
3
5
  *
6
+ * Marked client-side in its own right, not just via `react.tsx`, so a deep
7
+ * import lands on the same boundary as the barrel export.
8
+ *
4
9
  * The base SDK file (`react.tsx`) is already past the workspace's
5
10
  * "stop and ask before growing" file-size threshold, so these new hooks
6
11
  * live alongside it in their own module. Same import target for