@businessdash/sdk 0.9.60

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 (268) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +899 -0
  3. package/dist/adapters-revalidate.d.ts +44 -0
  4. package/dist/adapters-revalidate.d.ts.map +1 -0
  5. package/dist/adapters-revalidate.js +68 -0
  6. package/dist/adapters-revalidate.js.map +1 -0
  7. package/dist/alpha.d.ts +21 -0
  8. package/dist/alpha.d.ts.map +1 -0
  9. package/dist/alpha.js +21 -0
  10. package/dist/alpha.js.map +1 -0
  11. package/dist/analytics-core.d.ts +34 -0
  12. package/dist/analytics-core.d.ts.map +1 -0
  13. package/dist/analytics-core.js +138 -0
  14. package/dist/analytics-core.js.map +1 -0
  15. package/dist/analytics-server.d.ts +54 -0
  16. package/dist/analytics-server.d.ts.map +1 -0
  17. package/dist/analytics-server.js +78 -0
  18. package/dist/analytics-server.js.map +1 -0
  19. package/dist/attribution-logo.d.ts +2 -0
  20. package/dist/attribution-logo.d.ts.map +1 -0
  21. package/dist/attribution-logo.js +5 -0
  22. package/dist/attribution-logo.js.map +1 -0
  23. package/dist/auth-handler.d.ts +92 -0
  24. package/dist/auth-handler.d.ts.map +1 -0
  25. package/dist/auth-handler.js +277 -0
  26. package/dist/auth-handler.js.map +1 -0
  27. package/dist/biab-forms.css +484 -0
  28. package/dist/cli.d.ts +46 -0
  29. package/dist/cli.d.ts.map +1 -0
  30. package/dist/cli.js +1824 -0
  31. package/dist/cli.js.map +1 -0
  32. package/dist/client.d.ts +1681 -0
  33. package/dist/client.d.ts.map +1 -0
  34. package/dist/client.js +2845 -0
  35. package/dist/client.js.map +1 -0
  36. package/dist/collections.d.ts +13 -0
  37. package/dist/collections.d.ts.map +1 -0
  38. package/dist/collections.js +13 -0
  39. package/dist/collections.js.map +1 -0
  40. package/dist/contracts.d.ts +14609 -0
  41. package/dist/contracts.d.ts.map +1 -0
  42. package/dist/contracts.js +2428 -0
  43. package/dist/contracts.js.map +1 -0
  44. package/dist/data-model-schema.d.ts +962 -0
  45. package/dist/data-model-schema.d.ts.map +1 -0
  46. package/dist/data-model-schema.js +2295 -0
  47. package/dist/data-model-schema.js.map +1 -0
  48. package/dist/distribution.d.ts +38 -0
  49. package/dist/distribution.d.ts.map +1 -0
  50. package/dist/distribution.js +61 -0
  51. package/dist/distribution.js.map +1 -0
  52. package/dist/element.d.ts +121 -0
  53. package/dist/element.d.ts.map +1 -0
  54. package/dist/element.js +276 -0
  55. package/dist/element.js.map +1 -0
  56. package/dist/enums.d.ts +25 -0
  57. package/dist/enums.d.ts.map +1 -0
  58. package/dist/enums.js +38 -0
  59. package/dist/enums.js.map +1 -0
  60. package/dist/errors.d.ts +195 -0
  61. package/dist/errors.d.ts.map +1 -0
  62. package/dist/errors.js +188 -0
  63. package/dist/errors.js.map +1 -0
  64. package/dist/forms-core/autocomplete.d.ts +37 -0
  65. package/dist/forms-core/autocomplete.d.ts.map +1 -0
  66. package/dist/forms-core/autocomplete.js +82 -0
  67. package/dist/forms-core/autocomplete.js.map +1 -0
  68. package/dist/forms-core/business-function.d.ts +61 -0
  69. package/dist/forms-core/business-function.d.ts.map +1 -0
  70. package/dist/forms-core/business-function.js +149 -0
  71. package/dist/forms-core/business-function.js.map +1 -0
  72. package/dist/forms-core/calculation.d.ts +72 -0
  73. package/dist/forms-core/calculation.d.ts.map +1 -0
  74. package/dist/forms-core/calculation.js +103 -0
  75. package/dist/forms-core/calculation.js.map +1 -0
  76. package/dist/forms-core/controller.d.ts +249 -0
  77. package/dist/forms-core/controller.d.ts.map +1 -0
  78. package/dist/forms-core/controller.js +590 -0
  79. package/dist/forms-core/controller.js.map +1 -0
  80. package/dist/forms-core/elements.d.ts +44 -0
  81. package/dist/forms-core/elements.d.ts.map +1 -0
  82. package/dist/forms-core/elements.js +68 -0
  83. package/dist/forms-core/elements.js.map +1 -0
  84. package/dist/forms-core/email-domains.d.ts +10 -0
  85. package/dist/forms-core/email-domains.d.ts.map +1 -0
  86. package/dist/forms-core/email-domains.js +69 -0
  87. package/dist/forms-core/email-domains.js.map +1 -0
  88. package/dist/forms-core/file-rules.d.ts +51 -0
  89. package/dist/forms-core/file-rules.d.ts.map +1 -0
  90. package/dist/forms-core/file-rules.js +105 -0
  91. package/dist/forms-core/file-rules.js.map +1 -0
  92. package/dist/forms-core/formula.d.ts +114 -0
  93. package/dist/forms-core/formula.d.ts.map +1 -0
  94. package/dist/forms-core/formula.js +1170 -0
  95. package/dist/forms-core/formula.js.map +1 -0
  96. package/dist/forms-core/index.d.ts +24 -0
  97. package/dist/forms-core/index.d.ts.map +1 -0
  98. package/dist/forms-core/index.js +28 -0
  99. package/dist/forms-core/index.js.map +1 -0
  100. package/dist/forms-core/units.d.ts +49 -0
  101. package/dist/forms-core/units.d.ts.map +1 -0
  102. package/dist/forms-core/units.js +255 -0
  103. package/dist/forms-core/units.js.map +1 -0
  104. package/dist/forms-core/us-states.d.ts +14 -0
  105. package/dist/forms-core/us-states.d.ts.map +1 -0
  106. package/dist/forms-core/us-states.js +66 -0
  107. package/dist/forms-core/us-states.js.map +1 -0
  108. package/dist/forms-core/validation.d.ts +266 -0
  109. package/dist/forms-core/validation.d.ts.map +1 -0
  110. package/dist/forms-core/validation.js +850 -0
  111. package/dist/forms-core/validation.js.map +1 -0
  112. package/dist/forms.d.ts +19 -0
  113. package/dist/forms.d.ts.map +1 -0
  114. package/dist/forms.js +29 -0
  115. package/dist/forms.js.map +1 -0
  116. package/dist/gallery-types.d.ts +32 -0
  117. package/dist/gallery-types.d.ts.map +1 -0
  118. package/dist/gallery-types.js +2 -0
  119. package/dist/gallery-types.js.map +1 -0
  120. package/dist/graphql.d.ts +97 -0
  121. package/dist/graphql.d.ts.map +1 -0
  122. package/dist/graphql.js +212 -0
  123. package/dist/graphql.js.map +1 -0
  124. package/dist/index.cjs +19448 -0
  125. package/dist/index.d.cts +19 -0
  126. package/dist/index.d.mts +19 -0
  127. package/dist/index.d.ts +19 -0
  128. package/dist/index.d.ts.map +1 -0
  129. package/dist/index.js +20 -0
  130. package/dist/index.js.map +1 -0
  131. package/dist/marketing-schema.d.ts +216 -0
  132. package/dist/marketing-schema.d.ts.map +1 -0
  133. package/dist/marketing-schema.js +200 -0
  134. package/dist/marketing-schema.js.map +1 -0
  135. package/dist/mcp.d.ts +42 -0
  136. package/dist/mcp.d.ts.map +1 -0
  137. package/dist/mcp.js +87 -0
  138. package/dist/mcp.js.map +1 -0
  139. package/dist/native.d.ts +45 -0
  140. package/dist/native.d.ts.map +1 -0
  141. package/dist/native.js +43 -0
  142. package/dist/native.js.map +1 -0
  143. package/dist/next-revalidate.d.ts +43 -0
  144. package/dist/next-revalidate.d.ts.map +1 -0
  145. package/dist/next-revalidate.js +84 -0
  146. package/dist/next-revalidate.js.map +1 -0
  147. package/dist/parallel-pages.d.ts +102 -0
  148. package/dist/parallel-pages.d.ts.map +1 -0
  149. package/dist/parallel-pages.js +108 -0
  150. package/dist/parallel-pages.js.map +1 -0
  151. package/dist/payment-icons.d.ts +66 -0
  152. package/dist/payment-icons.d.ts.map +1 -0
  153. package/dist/payment-icons.js +160 -0
  154. package/dist/payment-icons.js.map +1 -0
  155. package/dist/proxy.d.ts +67 -0
  156. package/dist/proxy.d.ts.map +1 -0
  157. package/dist/proxy.js +97 -0
  158. package/dist/proxy.js.map +1 -0
  159. package/dist/react-analytics.d.ts +39 -0
  160. package/dist/react-analytics.d.ts.map +1 -0
  161. package/dist/react-analytics.js +59 -0
  162. package/dist/react-analytics.js.map +1 -0
  163. package/dist/react-attribution.d.ts +99 -0
  164. package/dist/react-attribution.d.ts.map +1 -0
  165. package/dist/react-attribution.js +52 -0
  166. package/dist/react-attribution.js.map +1 -0
  167. package/dist/react-marketing.d.ts +181 -0
  168. package/dist/react-marketing.d.ts.map +1 -0
  169. package/dist/react-marketing.js +215 -0
  170. package/dist/react-marketing.js.map +1 -0
  171. package/dist/react.d.ts +727 -0
  172. package/dist/react.d.ts.map +1 -0
  173. package/dist/react.js +2418 -0
  174. package/dist/react.js.map +1 -0
  175. package/dist/reserved-paths.d.ts +24 -0
  176. package/dist/reserved-paths.d.ts.map +1 -0
  177. package/dist/reserved-paths.js +60 -0
  178. package/dist/reserved-paths.js.map +1 -0
  179. package/dist/revalidate-verify.d.ts +30 -0
  180. package/dist/revalidate-verify.d.ts.map +1 -0
  181. package/dist/revalidate-verify.js +74 -0
  182. package/dist/revalidate-verify.js.map +1 -0
  183. package/dist/sdk.d.ts +678 -0
  184. package/dist/sdk.d.ts.map +1 -0
  185. package/dist/sdk.js +801 -0
  186. package/dist/sdk.js.map +1 -0
  187. package/dist/seo.d.ts +218 -0
  188. package/dist/seo.d.ts.map +1 -0
  189. package/dist/seo.js +309 -0
  190. package/dist/seo.js.map +1 -0
  191. package/dist/socials.d.ts +47 -0
  192. package/dist/socials.d.ts.map +1 -0
  193. package/dist/socials.js +172 -0
  194. package/dist/socials.js.map +1 -0
  195. package/dist/static.d.ts +14 -0
  196. package/dist/static.d.ts.map +1 -0
  197. package/dist/static.js +14 -0
  198. package/dist/static.js.map +1 -0
  199. package/dist/url-pattern.d.ts +47 -0
  200. package/dist/url-pattern.d.ts.map +1 -0
  201. package/dist/url-pattern.js +124 -0
  202. package/dist/url-pattern.js.map +1 -0
  203. package/dist/vanilla.d.ts +77 -0
  204. package/dist/vanilla.d.ts.map +1 -0
  205. package/dist/vanilla.js +903 -0
  206. package/dist/vanilla.js.map +1 -0
  207. package/openapi.json +13779 -0
  208. package/package.json +204 -0
  209. package/src/adapters-revalidate.ts +88 -0
  210. package/src/alpha.ts +89 -0
  211. package/src/analytics-core.ts +175 -0
  212. package/src/analytics-server.ts +99 -0
  213. package/src/angular/biab-form.component.ts +1443 -0
  214. package/src/angular/index.ts +26 -0
  215. package/src/attribution-logo.ts +5 -0
  216. package/src/auth-handler.ts +352 -0
  217. package/src/cli.ts +2517 -0
  218. package/src/client.ts +3578 -0
  219. package/src/collections.ts +101 -0
  220. package/src/contracts.ts +3390 -0
  221. package/src/data-model-schema.ts +4042 -0
  222. package/src/distribution.ts +73 -0
  223. package/src/element.ts +321 -0
  224. package/src/enums.ts +65 -0
  225. package/src/errors.ts +338 -0
  226. package/src/forms-core/autocomplete.ts +97 -0
  227. package/src/forms-core/business-function.ts +209 -0
  228. package/src/forms-core/calculation.ts +167 -0
  229. package/src/forms-core/controller.ts +922 -0
  230. package/src/forms-core/elements.ts +86 -0
  231. package/src/forms-core/email-domains.ts +68 -0
  232. package/src/forms-core/file-rules.ts +123 -0
  233. package/src/forms-core/formula.ts +1339 -0
  234. package/src/forms-core/index.ts +122 -0
  235. package/src/forms-core/units.ts +308 -0
  236. package/src/forms-core/us-states.ts +68 -0
  237. package/src/forms-core/validation.ts +1037 -0
  238. package/src/forms.ts +63 -0
  239. package/src/gallery-types.ts +31 -0
  240. package/src/graphql.ts +295 -0
  241. package/src/index.ts +505 -0
  242. package/src/marketing-schema.ts +395 -0
  243. package/src/mcp.ts +108 -0
  244. package/src/native.ts +73 -0
  245. package/src/next-revalidate.ts +124 -0
  246. package/src/parallel-pages.ts +192 -0
  247. package/src/payment-icons.ts +205 -0
  248. package/src/proxy.ts +173 -0
  249. package/src/qwik.tsx +1234 -0
  250. package/src/react-analytics.tsx +76 -0
  251. package/src/react-attribution.tsx +192 -0
  252. package/src/react-marketing.tsx +338 -0
  253. package/src/react.tsx +5495 -0
  254. package/src/reserved-paths.ts +65 -0
  255. package/src/revalidate-verify.ts +98 -0
  256. package/src/sdk.ts +1671 -0
  257. package/src/seo.ts +488 -0
  258. package/src/socials.ts +230 -0
  259. package/src/solid.tsx +1466 -0
  260. package/src/static.ts +21 -0
  261. package/src/styles/biab-forms.css +484 -0
  262. package/src/svelte/BiabForm.svelte +1069 -0
  263. package/src/svelte/context.ts +57 -0
  264. package/src/svelte/index.ts +79 -0
  265. package/src/url-pattern.ts +153 -0
  266. package/src/vanilla.ts +1290 -0
  267. package/src/vue/BiabForm.vue +1538 -0
  268. package/src/vue/index.ts +119 -0
package/dist/client.js ADDED
@@ -0,0 +1,2845 @@
1
+ import { z } from "zod";
2
+ import { addressDetailsResponseSchema, addressFormValuesSchema, addressSearchInputSchema, addressSearchResponseSchema, addressVerifyResponseSchema, aiVisibilityInputSchema, aiVisibilityResponseSchema, attributionHeartbeatInputSchema, attributionHeartbeatResponseSchema, attributionPolicySchema, authIntrospectResponseSchema, blogCategoryListResponseSchema, blogCommentListResponseSchema, blogCommentPostInputSchema, blogCommentPostResponseSchema, blogLikeResponseSchema, blogListPostsResponseSchema, blogPostBySlugResponseSchema, blogSessionInputSchema, blogSessionResponseSchema, blogTagListResponseSchema, cartAddItemInputSchema, cartCouponInputSchema, cartSessionResponseSchema, cartSnapshotSchema, cartUpdateItemInputSchema, chatbotAppendMessageInputSchema, chatbotAvailabilitySchema, chatbotChatInputSchema, chatbotChatResponseSchema, chatbotConfigSchema, chatbotConfigUpdateInputSchema, chatbotFormDefinitionSchema, chatbotFormSubmitInputSchema, chatbotFormSubmitResponseSchema, chatbotMessagesResponseSchema, chatbotPersistedSessionInputSchema, chatbotPersistedSessionResponseSchema, chatbotRequestHumanInputSchema, chatbotRequestHumanResponseSchema, chatbotSessionResponseSchema, chatbotUploadUrlResponseSchema, checkoutSessionInputSchema, checkoutSessionResponseSchema, checkoutStartInputSchema, checkoutStartResponseSchema, checkoutStatusResponseSchema, confirmVerificationInputSchema, confirmVerificationResponseSchema, couponValidateInputSchema, couponValidateResponseSchema, createCollectionInputSchema, createCollectionResponseSchema, createEmailTemplateInputSchema, createEmailTemplateResponseSchema, createEmailTemplateVersionInputSchema, createEmailTemplateVersionResponseSchema, customerContactCompanyInputSchema, customerContactCompanyResponseSchema, customerContractDetailSchema, customerContractSignResponseSchema, customerDownloadsResponseSchema, customerInvoiceDetailSchema, customerInvoicePayInputSchema, customerInvoicePayResponseSchema, customerInvoicePdfSchema, customerJobActivityResponseSchema, customerJobCommentPostInputSchema, customerJobCommentSchema, customerJobCommentsListResponseSchema, customerJobDetailSchema, customerJobEtaResponseSchema, customerNotificationPreferencesResponseSchema, customerNotificationPreferencesUpdateInputSchema, customerNotificationPreferencesUpdateResponseSchema, customerOrderDetailSchema, customerOrderListResponseSchema, customerPaymentHistoryResponseSchema, customerPortalContextResponseSchema, customerPortalOtherOrgsResponseSchema, customerPortalProfileSchema, customerPortalUpdateProfileInputSchema, customerPortalUpdateProfileResponseSchema, customerQuoteActionResponseSchema, customerQuoteDetailSchema, customerReferralHistoryResponseSchema, customerReferralPayoutsResponseSchema, customerReferralStatsSchema, customerReturnRequestCreatedSchema, customerReturnRequestInputSchema, customerReviewListResponseSchema, customerReviewSubmitInputSchema, customerReviewSubmitResponseSchema, customerShipmentTrackingSchema, customerWorkBundleSchema, dashboardSessionResponseSchema, executeActionInputSchema, executeActionResponseSchema, followerEditInputSchema, followerEditResponseSchema, followerJoinInputSchema, followerJoinResponseSchema, followerLeaveInputSchema, followerLeaveResponseSchema, followerMeInputSchema, followerMeResponseSchema, getCollectionResponseSchema, getEmailTemplateResponseSchema, getMarketingLocalesResponseSchema, getMarketingPageBundleResponseSchema, getPublishedMarketingSchemaResponseSchema, getSiteMarketingPageResponseSchema, jobCategoryListResponseSchema, jobPostingListResponseSchema, jobPostingSchema, listCollectionsResponseSchema, listDataModelRecordsInputSchema, listDataModelRecordsResponseSchema, listEmailTemplatesInputSchema, listEmailTemplatesResponseSchema, listRowsInputSchema, listRowsResponseSchema, listSiteMarketingPagesResponseSchema, notificationPreferencesResponseSchema, notificationPreferencesUpdateInputSchema, pageViewsInputSchema, pageViewsResponseSchema, publishMarketingSectionsInputSchema, publishMarketingSectionsResponseSchema, queryRowsInputSchema, queryRowsResponseSchema, reviewWallListResponseSchema, shippingRatesInputSchema, shippingRatesResponseSchema, shippingSettingsResponseSchema, shippingTrackResponseSchema, siteAssetInputSchema, siteBrandingSchema, startVerificationInputSchema, startVerificationResponseSchema, storefrontAddonsResponseSchema, storefrontCategoriesResponseSchema, storefrontListProductsResponseSchema, storefrontProductDetailSchema, storefrontProductReviewsResponseSchema, storefrontProductsWithMetaResponseSchema, storefrontRelatedProductsResponseSchema, storefrontSessionInputSchema, storefrontSessionResponseSchema, subscriptionCheckoutInputSchema, subscriptionCheckoutResponseSchema, subscriptionOfferingSchema, subscriptionOfferingsListResponseSchema, tenantAuthExchangeInputSchema, tenantAuthExchangeResponseSchema, tenantAuthMeResponseSchema, tenantAuthPasswordResetInputSchema, tenantAuthPasswordResetResponseSchema, tenantAuthSignOutResponseSchema, tenantAuthStartInputSchema, tenantAuthStartResponseSchema, uploadSiteAssetResponseSchema, upsertDraftMarketingSchemaInputSchema, upsertDraftMarketingSchemaResponseSchema, upsertRowInputSchema, upsertRowResponseSchema, } from "./contracts.js";
3
+ import { accessRejectionFromBody, BiabApiError, isBiabPlanGateBody, } from "./errors.js";
4
+ function buildUrl(baseUrl, path, query) {
5
+ /**
6
+ * Strip any leading slash from `path` so it is treated as a relative path and
7
+ * appended to `baseUrl`'s pathname (e.g. `/api/package/v1`). Adding a leading
8
+ * slash here would make `path` absolute under the URL spec and silently drop
9
+ * the base URL's path component — see https://url.spec.whatwg.org/#concept-basic-url-parser.
10
+ */
11
+ const relativePath = path.startsWith("/") ? path.slice(1) : path;
12
+ const url = new URL(relativePath, baseUrl);
13
+ for (const [key, value] of Object.entries(query ?? {})) {
14
+ if (value == null)
15
+ continue;
16
+ if (Array.isArray(value)) {
17
+ // Repeat the param once per element so the server reads getAll(key).
18
+ for (const item of value)
19
+ url.searchParams.append(key, String(item));
20
+ }
21
+ else {
22
+ url.searchParams.set(key, String(value));
23
+ }
24
+ }
25
+ return url;
26
+ }
27
+ async function parseResponseBody(response) {
28
+ const text = await response.text();
29
+ if (!text)
30
+ return null;
31
+ try {
32
+ return JSON.parse(text);
33
+ }
34
+ catch {
35
+ return text;
36
+ }
37
+ }
38
+ /**
39
+ * Coerce a user-provided origin (URL or bare hostname) into a wire-safe
40
+ * `scheme://host` value. Returns `undefined` if input is empty.
41
+ */
42
+ function normalizeSiteOrigin(raw) {
43
+ if (!raw)
44
+ return undefined;
45
+ const trimmed = raw.trim();
46
+ if (!trimmed)
47
+ return undefined;
48
+ const withScheme = /^https?:\/\//i.test(trimmed)
49
+ ? trimmed
50
+ : `https://${trimmed}`;
51
+ try {
52
+ const url = new URL(withScheme);
53
+ return `${url.protocol}//${url.host}`;
54
+ }
55
+ catch {
56
+ return undefined;
57
+ }
58
+ }
59
+ function extractErrorMessage(body, fallback) {
60
+ if (body && typeof body === "object" && "message" in body) {
61
+ const message = body.message;
62
+ if (typeof message === "string" && message.trim()) {
63
+ return message;
64
+ }
65
+ }
66
+ return fallback;
67
+ }
68
+ /**
69
+ * Typed client for the BIAB package HTTP API.
70
+ *
71
+ * @remarks
72
+ * **Alpha:** This SDK is in alpha. Surfaces and behavior may change without semver guarantees.
73
+ * Do not rely on it for production workloads—use development, staging, or experiments only.
74
+ */
75
+ export class BiabDevClient {
76
+ baseUrl;
77
+ apiKey;
78
+ fetchImpl;
79
+ defaultHeaders;
80
+ constructor(options) {
81
+ // Trailing slash is required by the WHATWG URL parser so the relative
82
+ // `path` segments are appended to (rather than replacing) the base path.
83
+ this.baseUrl = options.baseUrl.endsWith("/")
84
+ ? options.baseUrl
85
+ : `${options.baseUrl}/`;
86
+ this.apiKey = options.apiKey;
87
+ // Bind `fetch` to its global so that calling it as a method on
88
+ // `this` (`this.fetchImpl(url, init)`) doesn't trigger the
89
+ // "Illegal invocation" error in browsers — `Window.fetch` requires
90
+ // `this === window`. `globalThis` works in Node 18+ and the browser.
91
+ this.fetchImpl =
92
+ options.fetch ??
93
+ ((input, init) => globalThis.fetch(input, init));
94
+ this.defaultHeaders = options.defaultHeaders;
95
+ this.siteOrigin = normalizeSiteOrigin(options.siteOrigin);
96
+ }
97
+ siteOrigin;
98
+ site(siteId) {
99
+ return new BiabDevSiteClient(this, siteId);
100
+ }
101
+ get emailTemplates() {
102
+ return new BiabDevEmailTemplatesClient(this);
103
+ }
104
+ get dashboard() {
105
+ return new BiabDevDashboardClient(this);
106
+ }
107
+ get storefront() {
108
+ return new BiabDevStorefrontClient(this);
109
+ }
110
+ get cart() {
111
+ return new BiabDevCartClient(this);
112
+ }
113
+ get checkout() {
114
+ return new BiabDevCheckoutClient(this);
115
+ }
116
+ get coupons() {
117
+ return new BiabDevCouponsClient(this);
118
+ }
119
+ get subscriptions() {
120
+ return new BiabDevSubscriptionsClient(this);
121
+ }
122
+ get shipping() {
123
+ return new BiabDevShippingClient(this);
124
+ }
125
+ get notifications() {
126
+ return new BiabDevNotificationsClient(this);
127
+ }
128
+ get address() {
129
+ return new BiabDevAddressClient(this);
130
+ }
131
+ get blog() {
132
+ return new BiabDevBlogClient(this);
133
+ }
134
+ get reviews() {
135
+ return new BiabDevReviewsClient(this);
136
+ }
137
+ /** Public job postings — build a careers page on your own domain. */
138
+ get hiring() {
139
+ return new BiabDevHiringClient(this);
140
+ }
141
+ get chatbot() {
142
+ return new BiabDevChatbotClient(this);
143
+ }
144
+ get auth() {
145
+ return new BiabDevAuthClient(this);
146
+ }
147
+ /**
148
+ * Customer-portal client. Pin to a specific BIAB organization id so the
149
+ * caller never sees data from any other org (recommended). Omit
150
+ * `organizationId` to fall back to the user's currently-active session
151
+ * org — only use that on first-party hosts where the host already pins
152
+ * the org.
153
+ */
154
+ customerPortal(organizationId) {
155
+ return new BiabDevCustomerPortalClient(this, organizationId ?? null);
156
+ }
157
+ async introspect() {
158
+ return this.request({
159
+ method: "POST",
160
+ path: "auth/introspect",
161
+ responseSchema: authIntrospectResponseSchema,
162
+ });
163
+ }
164
+ async request(options) {
165
+ const url = buildUrl(this.baseUrl, options.path, options.query);
166
+ const init = {
167
+ method: options.method ?? "GET",
168
+ headers: {
169
+ Accept: "application/json",
170
+ Authorization: `Bearer ${this.apiKey}`,
171
+ ...(options.body === undefined
172
+ ? {}
173
+ : { "Content-Type": "application/json" }),
174
+ ...(this.siteOrigin ? { Origin: this.siteOrigin } : {}),
175
+ ...(this.defaultHeaders ?? {}),
176
+ ...(options.headers ?? {}),
177
+ },
178
+ };
179
+ if (options.body !== undefined) {
180
+ init.body = JSON.stringify(options.body);
181
+ }
182
+ const response = await this.fetchImpl(url, init);
183
+ const body = await parseResponseBody(response);
184
+ // Access gate — 200 (reads, body-only signal) and 402 (writes)
185
+ // both surface as a typed error so consumer code is uniform.
186
+ // Three flavours: plan-required (BiabFeatureNotEntitledError),
187
+ // payment-required (BiabPaymentLapsedError), service-suspended
188
+ // (BiabServiceSuspendedError) — all extend BiabAccessRejectedError
189
+ // for "catch any" patterns. `unwrapAccess()` is the opt-in
190
+ // Result-shape escape hatch.
191
+ if (isBiabPlanGateBody(body)) {
192
+ throw accessRejectionFromBody({
193
+ body,
194
+ path: url.pathname,
195
+ status: response.status,
196
+ });
197
+ }
198
+ if (!response.ok) {
199
+ throw new BiabApiError({
200
+ status: response.status,
201
+ path: url.pathname,
202
+ message: extractErrorMessage(body, `BIAB API request failed with status ${response.status}.`),
203
+ body,
204
+ });
205
+ }
206
+ return options.responseSchema.parse(body);
207
+ }
208
+ }
209
+ export class BiabDevSiteClient {
210
+ client;
211
+ siteId;
212
+ collections;
213
+ rows;
214
+ /**
215
+ * The org's custom database — the tables from `defineDataModel()`.
216
+ * Distinct from `collections`/`rows`, which are Site Data. See
217
+ * {@link BiabDevDataModelClient}.
218
+ */
219
+ dataModel;
220
+ /**
221
+ * Read your site's visitor counts back (the counterpart to the
222
+ * `<BIABAnalytics/>` tracker, which only records). See
223
+ * {@link BiabDevAnalyticsClient}.
224
+ */
225
+ analytics;
226
+ /**
227
+ * The "Powered by BusinessDash" badge's policy + heartbeat. See
228
+ * {@link BiabDevAttributionClient}.
229
+ */
230
+ attribution;
231
+ assets;
232
+ actions;
233
+ followers;
234
+ marketingPages;
235
+ /**
236
+ * Schema-driven marketing flow (Phase 2). Pair with the SDK's
237
+ * `defineSiteMarketingSchema()` builder + the `biab-dev sync-schema`
238
+ * CLI for the authoring side; this client is the live-site read
239
+ * path (`getPageBundle`) and the upload entry point for the CLI.
240
+ */
241
+ marketing;
242
+ parallelPages;
243
+ constructor(client, siteId) {
244
+ this.client = client;
245
+ this.siteId = siteId;
246
+ this.collections = new BiabDevCollectionsClient(client, siteId);
247
+ this.rows = new BiabDevRowsClient(client, siteId);
248
+ this.dataModel = new BiabDevDataModelClient(client, siteId);
249
+ this.analytics = new BiabDevAnalyticsClient(client, siteId);
250
+ this.attribution = new BiabDevAttributionClient(client, siteId);
251
+ this.assets = new BiabDevAssetsClient(client, siteId);
252
+ this.actions = new BiabDevActionsClient(client, siteId);
253
+ this.followers = new BiabDevFollowersClient(client, siteId);
254
+ this.marketingPages = new BiabDevMarketingPagesClient(client, siteId);
255
+ this.marketing = new BiabDevMarketingClient(client, siteId);
256
+ this.parallelPages = new BiabDevParallelPagesClient(client, siteId);
257
+ }
258
+ /**
259
+ * The org's storefront branding — logo + favicon (site static media) plus
260
+ * the org name + icon. Render the masthead/favicon/og without the org
261
+ * re-supplying assets. Available even when billing has lapsed.
262
+ *
263
+ * @example
264
+ * ```ts
265
+ * const b = await client.site(SITE_ID).branding();
266
+ * <img src={b.logoUrl ?? b.orgIcon ?? ""} alt={b.orgName ?? ""} />
267
+ * ```
268
+ */
269
+ async branding() {
270
+ return this.client.request({
271
+ path: `sites/${encodeURIComponent(this.siteId)}/branding`,
272
+ responseSchema: siteBrandingSchema,
273
+ });
274
+ }
275
+ }
276
+ const parallelPageMetaSchema = z.object({
277
+ title: z.string(),
278
+ description: z.string(),
279
+ canonical: z.string().nullable(),
280
+ ogImage: z.string().nullable(),
281
+ });
282
+ export const parallelPagesListResponseSchema = z.object({
283
+ items: z.array(z.object({
284
+ id: z.string(),
285
+ key: z.string(),
286
+ routePattern: z.string(),
287
+ variables: z.array(z.object({
288
+ name: z.string(),
289
+ source: z.object({
290
+ section: z.string(),
291
+ path: z.string().optional(),
292
+ }),
293
+ slugField: z.string(),
294
+ tokenFields: z.array(z.string()),
295
+ })),
296
+ meta: z.object({
297
+ title: z.string(),
298
+ description: z.string(),
299
+ canonical: z.string().optional(),
300
+ ogImage: z.string().optional(),
301
+ }),
302
+ templateRef: z.string(),
303
+ source: z.enum(["sdk", "site_builder"]),
304
+ crawlRule: z.enum(["inherit", "index", "noindex"]),
305
+ isActive: z.boolean(),
306
+ })),
307
+ });
308
+ export const parallelPagesVariantsResponseSchema = z.object({
309
+ variants: z.array(z.record(z.string(), z.string())),
310
+ });
311
+ export const parallelPagesRenderResponseSchema = z.object({
312
+ key: z.string(),
313
+ routePattern: z.string(),
314
+ slugs: z.record(z.string(), z.string()),
315
+ meta: parallelPageMetaSchema,
316
+ body: z.unknown().nullable(),
317
+ });
318
+ /**
319
+ * Programmatic SEO ("parallel pages") client. Same shape across
320
+ * SDK-defined and site-builder-defined pages — both share the
321
+ * `org_parallel_pages` table on the server.
322
+ *
323
+ * Typical wiring in Next.js:
324
+ *
325
+ * export async function generateStaticParams() {
326
+ * const { variants } = await client
327
+ * .site(SITE_ID)
328
+ * .parallelPages
329
+ * .listVariants("repair-area");
330
+ * return variants;
331
+ * }
332
+ *
333
+ * export default async function Page({ params }) {
334
+ * const bundle = await client
335
+ * .site(SITE_ID)
336
+ * .parallelPages
337
+ * .render("repair-area", await params);
338
+ * return <RenderTemplate bundle={bundle} />;
339
+ * }
340
+ */
341
+ export class BiabDevParallelPagesClient {
342
+ client;
343
+ siteId;
344
+ constructor(client, siteId) {
345
+ this.client = client;
346
+ this.siteId = siteId;
347
+ }
348
+ async list() {
349
+ return this.client.request({
350
+ path: `sites/${encodeURIComponent(this.siteId)}/parallel-pages`,
351
+ responseSchema: parallelPagesListResponseSchema,
352
+ });
353
+ }
354
+ async listVariants(key, opts = {}) {
355
+ return this.client.request({
356
+ path: `sites/${encodeURIComponent(this.siteId)}/parallel-pages/${encodeURIComponent(key)}/variants`,
357
+ ...(opts.locale ? { query: { locale: opts.locale } } : {}),
358
+ responseSchema: parallelPagesVariantsResponseSchema,
359
+ });
360
+ }
361
+ async render(key, slugs, opts = {}) {
362
+ return this.client.request({
363
+ path: `sites/${encodeURIComponent(this.siteId)}/parallel-pages/${encodeURIComponent(key)}/render`,
364
+ query: {
365
+ ...slugs,
366
+ ...(opts.locale ? { locale: opts.locale } : {}),
367
+ },
368
+ responseSchema: parallelPagesRenderResponseSchema,
369
+ });
370
+ }
371
+ /**
372
+ * URL of the auto-generated sitemap. Consumer's `/sitemap.xml` route
373
+ * proxies straight through. Returns null when the client isn't
374
+ * configured with a base URL.
375
+ */
376
+ sitemapUrl() {
377
+ return `sites/${encodeURIComponent(this.siteId)}/sitemap.xml`;
378
+ }
379
+ /**
380
+ * URL of the auto-generated robots.txt. Consumer's `/robots.txt` route
381
+ * proxies straight through.
382
+ */
383
+ robotsUrl() {
384
+ return `sites/${encodeURIComponent(this.siteId)}/robots.txt`;
385
+ }
386
+ }
387
+ /**
388
+ * Process-wide cache for the schema-driven marketing reads, keyed by
389
+ * the response's content-version fingerprint so cache hits are safe
390
+ * across SDK consumer re-renders. Bundle entries are short-lived (the
391
+ * server already sets `Cache-Control: s-maxage=60` so an upstream CDN
392
+ * does most of the work); the cache here is the SDK-process tier
393
+ * that prerendered Next builds reuse across all generated pages.
394
+ */
395
+ const marketingBundleCache = new Map();
396
+ const marketingSchemaCache = new Map();
397
+ const marketingLocalesCache = new Map();
398
+ function bundleCacheKey(siteId, pageKey, locale) {
399
+ return `${siteId}/${pageKey}/${locale}`;
400
+ }
401
+ /**
402
+ * Manually invalidate marketing caches scoped to a site. Useful after a
403
+ * server-side mutation the SDK didn't initiate (e.g. an operator saved a
404
+ * section in the dashboard while the consumer's Next process is still
405
+ * warm).
406
+ */
407
+ export function clearMarketingCache(siteId) {
408
+ const prefix = `${siteId}/`;
409
+ for (const k of Array.from(marketingBundleCache.keys())) {
410
+ if (k.startsWith(prefix))
411
+ marketingBundleCache.delete(k);
412
+ }
413
+ marketingSchemaCache.delete(siteId);
414
+ marketingLocalesCache.delete(siteId);
415
+ }
416
+ /**
417
+ * Live-site read client for the schema-driven marketing flow.
418
+ *
419
+ * Surfaces:
420
+ * - `getPageBundle({ pageKey, locale })` — every section for a page,
421
+ * tagged ok|error per section, plus SEO + availableLocales.
422
+ * - `getPublishedSchema()` — the live schema (returns `{ schema: null }`
423
+ * until the consumer's CLI has published one).
424
+ * - `getDraftSchema()` — what's currently staged (CLI-only path).
425
+ * - `publishDraftSchema(input)` — CLI upload. Idempotent (same
426
+ * checksum = `noop: true`).
427
+ * - `getLocales()` — enabled locales + brand-token map.
428
+ */
429
+ export class BiabDevMarketingClient {
430
+ client;
431
+ siteId;
432
+ constructor(client, siteId) {
433
+ this.client = client;
434
+ this.siteId = siteId;
435
+ }
436
+ async getPageBundle(input = {}, options = {}) {
437
+ const pageKey = input.pageKey ?? "home";
438
+ const locale = input.locale ?? "en";
439
+ // reviewsLimit sizes bundle.reviews.items; different sizes are different
440
+ // payloads, so it participates in the cache key.
441
+ const cacheKey = bundleCacheKey(this.siteId, pageKey, locale) +
442
+ (input.reviewsLimit != null ? `/r${input.reviewsLimit}` : "");
443
+ if (!options.skipCache) {
444
+ const hit = marketingBundleCache.get(cacheKey);
445
+ if (hit)
446
+ return hit;
447
+ }
448
+ const response = await this.client.request({
449
+ path: `sites/${encodeURIComponent(this.siteId)}/marketing/bundle`,
450
+ query: { pageKey, locale, reviewsLimit: input.reviewsLimit },
451
+ responseSchema: getMarketingPageBundleResponseSchema,
452
+ });
453
+ marketingBundleCache.set(cacheKey, response);
454
+ return response;
455
+ }
456
+ getPageBundleCached(input = {}) {
457
+ const pageKey = input.pageKey ?? "home";
458
+ const locale = input.locale ?? "en";
459
+ return (marketingBundleCache.get(bundleCacheKey(this.siteId, pageKey, locale)) ??
460
+ null);
461
+ }
462
+ async prefetchPageBundle(input = {}) {
463
+ return this.getPageBundle(input);
464
+ }
465
+ async getPublishedSchema(options = {}) {
466
+ if (!options.skipCache) {
467
+ const hit = marketingSchemaCache.get(this.siteId);
468
+ if (hit)
469
+ return hit;
470
+ }
471
+ const response = await this.client.request({
472
+ path: `sites/${encodeURIComponent(this.siteId)}/marketing/published-schema`,
473
+ responseSchema: getPublishedMarketingSchemaResponseSchema,
474
+ });
475
+ marketingSchemaCache.set(this.siteId, response);
476
+ return response;
477
+ }
478
+ async getDraftSchema() {
479
+ // Same response shape as published-schema, just from the other slot.
480
+ // Not cached — the CLI calls this once per upload cycle.
481
+ return this.client.request({
482
+ path: `sites/${encodeURIComponent(this.siteId)}/marketing/draft-schema`,
483
+ responseSchema: getPublishedMarketingSchemaResponseSchema,
484
+ });
485
+ }
486
+ async publishDraftSchema(input) {
487
+ const body = upsertDraftMarketingSchemaInputSchema.parse(input);
488
+ return this.client.request({
489
+ method: "POST",
490
+ path: `sites/${encodeURIComponent(this.siteId)}/marketing/draft-schema`,
491
+ body,
492
+ responseSchema: upsertDraftMarketingSchemaResponseSchema,
493
+ });
494
+ }
495
+ /**
496
+ * Bulk-upsert section values. Used by `biab-dev sync-content` to push
497
+ * local JSON content to BIAB. Server caps each request at 256 items;
498
+ * larger imports are batched by the CLI. Per-item failures are
499
+ * reported in the response (`results[].ok === false`) without aborting
500
+ * the rest of the batch.
501
+ *
502
+ * The CLI's `--lax` flag sets `validate: false` for first-time imports
503
+ * of legacy content that doesn't yet match the published schema —
504
+ * useful so writers can pull existing content into BIAB and fix
505
+ * validation errors in the dashboard incrementally.
506
+ */
507
+ async publishSections(input) {
508
+ const body = publishMarketingSectionsInputSchema.parse(input);
509
+ return this.client.request({
510
+ method: "POST",
511
+ path: `sites/${encodeURIComponent(this.siteId)}/marketing/sections`,
512
+ body,
513
+ responseSchema: publishMarketingSectionsResponseSchema,
514
+ });
515
+ }
516
+ async getLocales(options = {}) {
517
+ if (!options.skipCache) {
518
+ const hit = marketingLocalesCache.get(this.siteId);
519
+ if (hit)
520
+ return hit;
521
+ }
522
+ const response = await this.client.request({
523
+ path: `sites/${encodeURIComponent(this.siteId)}/marketing/locales`,
524
+ responseSchema: getMarketingLocalesResponseSchema,
525
+ });
526
+ marketingLocalesCache.set(this.siteId, response);
527
+ return response;
528
+ }
529
+ }
530
+ /**
531
+ * Process-wide cache for marketing-page payloads, keyed by `(siteId, pageKey)`.
532
+ * Shared across `BiabDevMarketingPagesClient` instances so that
533
+ * `prefetch(pageKey)` warms the same store `get(pageKey)` reads on demand.
534
+ *
535
+ * Lives on a module-level `Map` rather than instance state because consumers
536
+ * commonly create transient `client.site(siteId)` proxies inside React
537
+ * components — instance-level caching would evict on every re-render.
538
+ */
539
+ const marketingPageCache = new Map();
540
+ function marketingPageCacheKey(siteId, pageKey) {
541
+ return `${siteId} ${pageKey}`;
542
+ }
543
+ /**
544
+ * Manually clear the SDK's cache for `(siteId, pageKey)` (or the whole
545
+ * site when `pageKey` is omitted). Useful after a server-side mutation
546
+ * that the SDK didn't initiate, or in tests.
547
+ */
548
+ export function clearMarketingPageCache(siteId, pageKey) {
549
+ if (pageKey) {
550
+ marketingPageCache.delete(marketingPageCacheKey(siteId, pageKey));
551
+ return;
552
+ }
553
+ const prefix = `${siteId} `;
554
+ for (const key of Array.from(marketingPageCache.keys())) {
555
+ if (key.startsWith(prefix))
556
+ marketingPageCache.delete(key);
557
+ }
558
+ }
559
+ /**
560
+ * Read-only access to the host's `org_site_marketing_pages` table — the JSON
561
+ * documents the Site Builder writes for marketing landing pages (e.g. the
562
+ * `home` page rendered by `custom-demo`).
563
+ *
564
+ * Requires the `collections:read` scope on the API key (the same scope used
565
+ * for collection reads, so existing keys created from the host's Site Builder
566
+ * → Developer panel work without re-issuing).
567
+ *
568
+ * The client transparently caches per-page responses across instances; pair
569
+ * `list()` (no payloads) with `get(pageKey)` / `prefetch(pageKey)` to load
570
+ * pages on demand instead of paying the full-content cost up front.
571
+ */
572
+ export class BiabDevMarketingPagesClient {
573
+ client;
574
+ siteId;
575
+ constructor(client, siteId) {
576
+ this.client = client;
577
+ this.siteId = siteId;
578
+ }
579
+ /**
580
+ * Payload-free directory of every marketing page for the site. Use this
581
+ * to render a navigation index without loading any content; call
582
+ * {@link get} or {@link prefetch} when the visitor selects a page.
583
+ */
584
+ async list() {
585
+ return this.client.request({
586
+ path: `sites/${encodeURIComponent(this.siteId)}/marketing-pages`,
587
+ responseSchema: listSiteMarketingPagesResponseSchema,
588
+ });
589
+ }
590
+ /**
591
+ * Fetches a single marketing page payload by `pageKey` (defaults to `"home"`).
592
+ * Returns `{ page: null }` when the host has not published one yet — callers
593
+ * typically render a static fallback in that case.
594
+ *
595
+ * Reads from the shared cache when present (warmed by {@link prefetch} or
596
+ * a previous `get`). Pass `{ skipCache: true }` to force a network round-trip
597
+ * (e.g. after a save the SDK didn't make itself).
598
+ */
599
+ async get(pageKey = "home", options = {}) {
600
+ const cacheKey = marketingPageCacheKey(this.siteId, pageKey);
601
+ if (!options.skipCache) {
602
+ const hit = marketingPageCache.get(cacheKey);
603
+ if (hit)
604
+ return hit;
605
+ }
606
+ const response = await this.client.request({
607
+ path: `sites/${encodeURIComponent(this.siteId)}/marketing-pages/${encodeURIComponent(pageKey)}`,
608
+ responseSchema: getSiteMarketingPageResponseSchema,
609
+ });
610
+ marketingPageCache.set(cacheKey, response);
611
+ return response;
612
+ }
613
+ /**
614
+ * Read the cached value for `pageKey` without making a network call.
615
+ * Returns `null` when the entry hasn't been fetched yet — callers can
616
+ * fall back to a synchronous static default while {@link prefetch}
617
+ * warms the cache in the background.
618
+ */
619
+ getCached(pageKey) {
620
+ return (marketingPageCache.get(marketingPageCacheKey(this.siteId, pageKey)) ??
621
+ null);
622
+ }
623
+ /**
624
+ * Warm the SDK cache for `pageKey` without rendering anything yet.
625
+ * Common pattern: call from a `<Link onPointerEnter>` / `onFocus` so
626
+ * the next page is already loaded by the time the visitor clicks.
627
+ * Subsequent `get(pageKey)` calls resolve from cache.
628
+ */
629
+ async prefetch(pageKey = "home") {
630
+ return this.get(pageKey);
631
+ }
632
+ }
633
+ export class BiabDevCollectionsClient {
634
+ client;
635
+ siteId;
636
+ constructor(client, siteId) {
637
+ this.client = client;
638
+ this.siteId = siteId;
639
+ }
640
+ async list() {
641
+ return this.client.request({
642
+ path: `sites/${encodeURIComponent(this.siteId)}/collections`,
643
+ responseSchema: listCollectionsResponseSchema,
644
+ });
645
+ }
646
+ async create(input) {
647
+ const body = createCollectionInputSchema.parse(input);
648
+ return this.client.request({
649
+ method: "POST",
650
+ path: `sites/${encodeURIComponent(this.siteId)}/collections`,
651
+ body,
652
+ responseSchema: createCollectionResponseSchema,
653
+ });
654
+ }
655
+ async get(slug) {
656
+ return this.client.request({
657
+ path: `sites/${encodeURIComponent(this.siteId)}/collections/${encodeURIComponent(slug)}`,
658
+ responseSchema: getCollectionResponseSchema,
659
+ });
660
+ }
661
+ }
662
+ export class BiabDevRowsClient {
663
+ client;
664
+ siteId;
665
+ constructor(client, siteId) {
666
+ this.client = client;
667
+ this.siteId = siteId;
668
+ }
669
+ async list(collectionSlug, input = {}) {
670
+ const query = listRowsInputSchema.parse(input);
671
+ return this.client.request({
672
+ path: `sites/${encodeURIComponent(this.siteId)}/collections/${encodeURIComponent(collectionSlug)}/rows`,
673
+ query,
674
+ responseSchema: listRowsResponseSchema,
675
+ });
676
+ }
677
+ async upsert(collectionSlug, input) {
678
+ const body = upsertRowInputSchema.parse(input);
679
+ return this.client.request({
680
+ method: "POST",
681
+ path: `sites/${encodeURIComponent(this.siteId)}/collections/${encodeURIComponent(collectionSlug)}/rows`,
682
+ body,
683
+ responseSchema: upsertRowResponseSchema,
684
+ });
685
+ }
686
+ async query(collectionSlug, input) {
687
+ const body = queryRowsInputSchema.parse(input);
688
+ return this.client.request({
689
+ method: "POST",
690
+ path: `sites/${encodeURIComponent(this.siteId)}/collections/${encodeURIComponent(collectionSlug)}/query`,
691
+ body,
692
+ responseSchema: queryRowsResponseSchema,
693
+ });
694
+ }
695
+ }
696
+ /**
697
+ * Read rows out of the org's CUSTOM DATABASE — the tables you declare with
698
+ * `defineDataModel()` and push with `biab-dev sync-data-model`.
699
+ *
700
+ * ⚠️ Not the same thing as `.collections` / `.rows`. Those read Site Data (the
701
+ * site builder's tables, `site_data_rows`). Rows seeded with
702
+ * `biab-dev sync-records` live in `org_object_records` and are reachable only
703
+ * through here — no `collections:*` call will ever return one.
704
+ *
705
+ * Requires the `metadata:read_records` scope. What a key can actually read is
706
+ * decided per object by its `visibility`:
707
+ *
708
+ * - `private` (default) — a SECRET key only. Call from a server component, a
709
+ * route handler, or at build time; a `pk_…` key gets "not found".
710
+ * - `authenticated` — a secret key, or a publishable key carrying a signed-in
711
+ * tenant session.
712
+ * - `public` — any key, including a publishable (`pk_…`) one in page JS.
713
+ *
714
+ * So a table you declared `public` is readable straight from the browser; a
715
+ * private one never is. Open a table by declaring `visibility` in your model.
716
+ *
717
+ * ```ts
718
+ * const result = await biab.site(siteId).dataModel.listRecords({
719
+ * object: TECH_OBJECT_ID, // the universalIdentifier from your config
720
+ * });
721
+ * if (!result.available) return <UpgradeCard feature={result.requiredFeature} />;
722
+ * for (const tech of result.records) {
723
+ * tech.fields.name; // scalar values, keyed by field name
724
+ * tech.relations.icon; // [{ recordId, object }] — links, not embedded rows
725
+ * }
726
+ * ```
727
+ */
728
+ export class BiabDevDataModelClient {
729
+ client;
730
+ siteId;
731
+ constructor(client, siteId) {
732
+ this.client = client;
733
+ this.siteId = siteId;
734
+ }
735
+ /**
736
+ * One page of an object's rows, newest first, with relations expanded.
737
+ *
738
+ * Relations come back as LINKS (`{ recordId, object }`), not embedded rows —
739
+ * fetch the targets with a follow-up call for the object they point at.
740
+ * Every declared relation field is present; `[]` means "no links yet", which
741
+ * is deliberately distinguishable from a field that wasn't returned.
742
+ *
743
+ * Paginate by passing the previous response's `nextCursor` back as `cursor`
744
+ * until it comes back `null`. The cursor is opaque — don't build one.
745
+ */
746
+ async listRecords(input) {
747
+ const query = listDataModelRecordsInputSchema.parse(input);
748
+ return this.client.request({
749
+ path: `sites/${encodeURIComponent(this.siteId)}/data-model/records`,
750
+ query,
751
+ responseSchema: listDataModelRecordsResponseSchema,
752
+ });
753
+ }
754
+ /**
755
+ * Every row of an object, walking `nextCursor` to exhaustion.
756
+ *
757
+ * For build-time rendering of a table you own and whose size you know. It is
758
+ * an unbounded loop over your data by definition — for anything visitor-facing
759
+ * or open-ended, page with {@link listRecords} instead.
760
+ *
761
+ * Returns the plan-gate shape unchanged if the org lacks the entitlement, so
762
+ * the same `available` branch works for both.
763
+ */
764
+ async listAllRecords(input) {
765
+ const records = [];
766
+ let cursor;
767
+ do {
768
+ const page = await this.listRecords({ ...input, cursor });
769
+ if (!page.available)
770
+ return page;
771
+ records.push(...page.records);
772
+ cursor = page.nextCursor ?? undefined;
773
+ } while (cursor);
774
+ return { available: true, records, nextCursor: null };
775
+ }
776
+ }
777
+ /**
778
+ * Read your site's visitor counts back.
779
+ *
780
+ * The `<BIABAnalytics/>` tracker (`@businessdash/sdk/react-analytics`) RECORDS
781
+ * pageviews; this READS the totals, so you can put a number on the page instead
782
+ * of only feeding the dashboard.
783
+ *
784
+ * ```ts
785
+ * // Site-wide, all time
786
+ * const all = await biab.site(siteId).analytics.pageViews();
787
+ * if (all.available) console.log(`${all.total.views} views`);
788
+ *
789
+ * // One page, last 30 days
790
+ * const home = await biab.site(siteId).analytics.pageViews({ paths: ['/'], days: 30 });
791
+ *
792
+ * // A batch of pages in one round-trip
793
+ * const list = await biab.site(siteId).analytics.pageViews({
794
+ * paths: ['/tech/typescript', '/tech/rust'],
795
+ * });
796
+ * // list.paths → [{ path, views, visitors }, …], every requested path present
797
+ * ```
798
+ *
799
+ * `visitors` approximates unique visitors (distinct daily-rotating anon id).
800
+ * Requires the `analytics.web` entitlement — an org without it gets
801
+ * `{ available: false }`, so branch on `.available`.
802
+ */
803
+ export class BiabDevAnalyticsClient {
804
+ client;
805
+ siteId;
806
+ constructor(client, siteId) {
807
+ this.client = client;
808
+ this.siteId = siteId;
809
+ }
810
+ /**
811
+ * Pageview + visitor counts. Pass `paths` for a per-path breakdown (plus a
812
+ * combined `total`), or omit them for a site-wide `total`. `days` windows the
813
+ * count; omit for all-time.
814
+ */
815
+ async pageViews(input = {}) {
816
+ const { paths, days } = pageViewsInputSchema.parse(input);
817
+ return this.client.request({
818
+ path: `sites/${encodeURIComponent(this.siteId)}/analytics/views`,
819
+ query: {
820
+ // `path` repeats once per requested path; the route reads getAll.
821
+ ...(paths && paths.length > 0 ? { path: paths } : {}),
822
+ ...(days !== undefined ? { days } : {}),
823
+ },
824
+ responseSchema: pageViewsResponseSchema,
825
+ });
826
+ }
827
+ /**
828
+ * Which AI crawlers (GPTBot, ClaudeBot, PerplexityBot, …) actually fetched
829
+ * this site's pages: fetch counts, distinct pages read, vendor, intent
830
+ * (training / search / live-assistant), and when each last came.
831
+ *
832
+ * First-party AEO visibility — recorded when crawlers hit the tracker (the
833
+ * assistant browsing agents run JS) and when your server reports the rest
834
+ * via `reportAiCrawlerHit` from `@businessdash/sdk/analytics-server`. Defaults
835
+ * to a 30-day window.
836
+ *
837
+ * ```ts
838
+ * const ai = await biab.site(siteId).analytics.aiVisibility({ days: 30 });
839
+ * if (ai.available) {
840
+ * for (const c of ai.crawlers)
841
+ * console.log(`${c.name}: ${c.fetches} fetches over ${c.distinctPages} pages`);
842
+ * }
843
+ * ```
844
+ */
845
+ async aiVisibility(input = {}) {
846
+ const { days } = aiVisibilityInputSchema.parse(input);
847
+ return this.client.request({
848
+ path: `sites/${encodeURIComponent(this.siteId)}/analytics/ai-crawlers`,
849
+ query: { ...(days !== undefined ? { days } : {}) },
850
+ responseSchema: aiVisibilityResponseSchema,
851
+ });
852
+ }
853
+ }
854
+ /**
855
+ * The "Powered by BusinessDash" attribution badge's one narrow function.
856
+ *
857
+ * `<BusinessDashFooter/>` uses this to (a) read whether the badge is required on
858
+ * this plan and (b) optionally report that it rendered. Both are scoped to your
859
+ * own site with a publishable key.
860
+ *
861
+ * ⚠️ The heartbeat is a courtesy signal, not proof of anything — BusinessDash
862
+ * verifies the badge by fetching your live domain, not by trusting this call. So
863
+ * don't build logic on the heartbeat; do honor `policy().required`.
864
+ */
865
+ export class BiabDevAttributionClient {
866
+ client;
867
+ siteId;
868
+ constructor(client, siteId) {
869
+ this.client = client;
870
+ this.siteId = siteId;
871
+ }
872
+ /**
873
+ * This site's badge policy: `{ siteId, required }`. `required` is `true` on
874
+ * plans that can't remove the badge (launch), `false` otherwise. Build the
875
+ * badge's `?ref=<siteId>` backlink from `siteId`.
876
+ */
877
+ async policy() {
878
+ return this.client.request({
879
+ path: `sites/${encodeURIComponent(this.siteId)}/attribution`,
880
+ responseSchema: attributionPolicySchema,
881
+ });
882
+ }
883
+ /**
884
+ * Report that the badge rendered on `url`. Best-effort and untrusted — it
885
+ * never throws for a bad URL (you get `{ recorded: false }`), and it's safe to
886
+ * fire-and-forget. Don't await it in a render path.
887
+ */
888
+ async heartbeat(input) {
889
+ const body = attributionHeartbeatInputSchema.parse(input);
890
+ return this.client.request({
891
+ method: "POST",
892
+ path: `sites/${encodeURIComponent(this.siteId)}/attribution/heartbeat`,
893
+ body,
894
+ responseSchema: attributionHeartbeatResponseSchema,
895
+ });
896
+ }
897
+ }
898
+ export class BiabDevAssetsClient {
899
+ client;
900
+ siteId;
901
+ constructor(client, siteId) {
902
+ this.client = client;
903
+ this.siteId = siteId;
904
+ }
905
+ async create(input) {
906
+ const body = siteAssetInputSchema.parse(input);
907
+ return this.client.request({
908
+ method: "POST",
909
+ path: `sites/${encodeURIComponent(this.siteId)}/assets`,
910
+ body,
911
+ responseSchema: uploadSiteAssetResponseSchema,
912
+ });
913
+ }
914
+ }
915
+ export class BiabDevActionsClient {
916
+ client;
917
+ siteId;
918
+ constructor(client, siteId) {
919
+ this.client = client;
920
+ this.siteId = siteId;
921
+ }
922
+ async run(actionName, input = { payload: {} }) {
923
+ const body = executeActionInputSchema.parse(input);
924
+ return this.client.request({
925
+ method: "POST",
926
+ path: `sites/${encodeURIComponent(this.siteId)}/actions/${encodeURIComponent(actionName)}`,
927
+ body,
928
+ responseSchema: executeActionResponseSchema,
929
+ });
930
+ }
931
+ }
932
+ export class BiabDevEmailTemplatesClient {
933
+ client;
934
+ constructor(client) {
935
+ this.client = client;
936
+ }
937
+ async list(input = {}) {
938
+ const query = listEmailTemplatesInputSchema.parse(input);
939
+ return this.client.request({
940
+ path: "email/templates",
941
+ query,
942
+ responseSchema: listEmailTemplatesResponseSchema,
943
+ });
944
+ }
945
+ async get(templateId) {
946
+ return this.client.request({
947
+ path: `email/templates/${templateId}`,
948
+ responseSchema: getEmailTemplateResponseSchema,
949
+ });
950
+ }
951
+ async create(input) {
952
+ const body = createEmailTemplateInputSchema.parse(input);
953
+ return this.client.request({
954
+ method: "POST",
955
+ path: "email/templates",
956
+ body,
957
+ responseSchema: createEmailTemplateResponseSchema,
958
+ });
959
+ }
960
+ async createVersion(templateId, input) {
961
+ const body = createEmailTemplateVersionInputSchema.parse(input);
962
+ return this.client.request({
963
+ method: "POST",
964
+ path: `email/templates/${templateId}/versions`,
965
+ body,
966
+ responseSchema: createEmailTemplateVersionResponseSchema,
967
+ });
968
+ }
969
+ }
970
+ export class BiabDevFollowersClient {
971
+ client;
972
+ siteId;
973
+ constructor(client, siteId) {
974
+ this.client = client;
975
+ this.siteId = siteId;
976
+ }
977
+ actionPath(action) {
978
+ return `sites/${encodeURIComponent(this.siteId)}/actions/${encodeURIComponent(action)}`;
979
+ }
980
+ async join(input) {
981
+ const payload = followerJoinInputSchema.parse(input);
982
+ const body = executeActionInputSchema.parse({ payload });
983
+ const response = await this.client.request({
984
+ method: "POST",
985
+ path: this.actionPath("followers.join"),
986
+ body,
987
+ responseSchema: executeActionResponseSchema,
988
+ });
989
+ return followerJoinResponseSchema.parse(response.result);
990
+ }
991
+ async me(input) {
992
+ const payload = followerMeInputSchema.parse(input);
993
+ const body = executeActionInputSchema.parse({ payload });
994
+ const response = await this.client.request({
995
+ method: "POST",
996
+ path: this.actionPath("followers.me"),
997
+ body,
998
+ responseSchema: executeActionResponseSchema,
999
+ });
1000
+ return followerMeResponseSchema.parse(response.result);
1001
+ }
1002
+ async edit(input) {
1003
+ const payload = followerEditInputSchema.parse(input);
1004
+ const body = executeActionInputSchema.parse({ payload });
1005
+ const response = await this.client.request({
1006
+ method: "POST",
1007
+ path: this.actionPath("followers.edit"),
1008
+ body,
1009
+ responseSchema: executeActionResponseSchema,
1010
+ });
1011
+ return followerEditResponseSchema.parse(response.result);
1012
+ }
1013
+ async leave(input) {
1014
+ const payload = followerLeaveInputSchema.parse(input);
1015
+ const body = executeActionInputSchema.parse({ payload });
1016
+ const response = await this.client.request({
1017
+ method: "POST",
1018
+ path: this.actionPath("followers.leave"),
1019
+ body,
1020
+ responseSchema: executeActionResponseSchema,
1021
+ });
1022
+ return followerLeaveResponseSchema.parse(response.result);
1023
+ }
1024
+ }
1025
+ export class BiabDevDashboardClient {
1026
+ client;
1027
+ constructor(client) {
1028
+ this.client = client;
1029
+ }
1030
+ async createSession() {
1031
+ return this.client.request({
1032
+ method: "POST",
1033
+ path: "dashboard/session",
1034
+ responseSchema: dashboardSessionResponseSchema,
1035
+ });
1036
+ }
1037
+ }
1038
+ export class BiabDevStorefrontClient {
1039
+ client;
1040
+ constructor(client) {
1041
+ this.client = client;
1042
+ }
1043
+ /**
1044
+ * Legacy embed-session mint (kept for iframe-widget consumers).
1045
+ * The programmatic methods below let consumer sites render their
1046
+ * own native product catalog using BIAB as the data layer.
1047
+ */
1048
+ async createSession(input) {
1049
+ const body = storefrontSessionInputSchema.parse(input ?? undefined);
1050
+ return this.client.request({
1051
+ method: "POST",
1052
+ path: "storefront/session",
1053
+ body: body ?? {},
1054
+ responseSchema: storefrontSessionResponseSchema,
1055
+ });
1056
+ }
1057
+ /**
1058
+ * Paginated list of live products for the calling org's storefront.
1059
+ * Cursor is an opaque integer offset — pass back the response's
1060
+ * `nextCursor` to fetch the next page. Filter by category via the
1061
+ * `categoryId` option.
1062
+ */
1063
+ async listProducts(input) {
1064
+ const query = {};
1065
+ if (input?.limit !== undefined)
1066
+ query["limit"] = input.limit;
1067
+ if (input?.cursor != null)
1068
+ query["cursor"] = input.cursor;
1069
+ if (input?.categoryId)
1070
+ query["categoryId"] = input.categoryId;
1071
+ return this.client.request({
1072
+ path: "storefront/products",
1073
+ query,
1074
+ responseSchema: storefrontListProductsResponseSchema,
1075
+ });
1076
+ }
1077
+ /**
1078
+ * Filterable listing grid with facets — the data behind a full shop page.
1079
+ * Returns enriched per-card price/ratings/badges plus `categoryCounts` and
1080
+ * the catalog-wide `priceRange` for a sidebar. Supports `search`,
1081
+ * `categoryId`, `minPriceCents`/`maxPriceCents`, `minRating` (1–5), and
1082
+ * `sort` (featured | newest | price-asc | price-desc | rating-desc).
1083
+ */
1084
+ async listProductsWithMeta(input) {
1085
+ const query = { meta: "1" };
1086
+ if (input?.search)
1087
+ query["search"] = input.search;
1088
+ if (input?.categoryId)
1089
+ query["categoryId"] = input.categoryId;
1090
+ if (input?.minPriceCents != null)
1091
+ query["minPriceCents"] = input.minPriceCents;
1092
+ if (input?.maxPriceCents != null)
1093
+ query["maxPriceCents"] = input.maxPriceCents;
1094
+ if (input?.minRating != null)
1095
+ query["minRating"] = input.minRating;
1096
+ if (input?.sort)
1097
+ query["sort"] = input.sort;
1098
+ if (input?.limit != null)
1099
+ query["limit"] = input.limit;
1100
+ if (input?.preview)
1101
+ query["preview"] = "1";
1102
+ return this.client.request({
1103
+ path: "storefront/products",
1104
+ query,
1105
+ responseSchema: storefrontProductsWithMetaResponseSchema,
1106
+ });
1107
+ }
1108
+ /** Org-scoped product categories for a storefront sidebar. */
1109
+ async listCategories() {
1110
+ return this.client.request({
1111
+ path: "storefront/categories",
1112
+ responseSchema: storefrontCategoriesResponseSchema,
1113
+ });
1114
+ }
1115
+ /**
1116
+ * Full product detail with variants + per-image rows. Returns 404
1117
+ * via `BiabApiError` if the product isn't live or doesn't belong
1118
+ * to the org bound to the API key.
1119
+ */
1120
+ async getProduct(productId) {
1121
+ return this.client.request({
1122
+ path: `storefront/products/${encodeURIComponent(productId)}`,
1123
+ responseSchema: storefrontProductDetailSchema,
1124
+ });
1125
+ }
1126
+ /** Approved reviews for a product + aggregate (`avgRating`, `totalCount`).
1127
+ * Cursor-paginated — pass back `nextCursor` for the next page. */
1128
+ async getProductReviews(productId, input) {
1129
+ const query = {};
1130
+ if (input?.limit !== undefined)
1131
+ query["limit"] = input.limit;
1132
+ if (input?.cursor != null)
1133
+ query["cursor"] = input.cursor;
1134
+ return this.client.request({
1135
+ path: `storefront/products/${encodeURIComponent(productId)}/reviews`,
1136
+ query,
1137
+ responseSchema: storefrontProductReviewsResponseSchema,
1138
+ });
1139
+ }
1140
+ /** "You may also like" — same-category / paired recommendations. */
1141
+ async getRelatedProducts(productId, input) {
1142
+ const query = {};
1143
+ if (input?.limit !== undefined)
1144
+ query["limit"] = input.limit;
1145
+ return this.client.request({
1146
+ path: `storefront/products/${encodeURIComponent(productId)}/related`,
1147
+ query,
1148
+ responseSchema: storefrontRelatedProductsResponseSchema,
1149
+ });
1150
+ }
1151
+ /** Companion / exclusive cross-sell addons for the "complete your X" rail. */
1152
+ async getProductAddons(productId) {
1153
+ return this.client.request({
1154
+ path: `storefront/products/${encodeURIComponent(productId)}/addons`,
1155
+ responseSchema: storefrontAddonsResponseSchema,
1156
+ });
1157
+ }
1158
+ }
1159
+ export class BiabDevCartClient {
1160
+ client;
1161
+ visitorToken;
1162
+ sessionToken;
1163
+ constructor(client, visitorToken = null, sessionToken = null) {
1164
+ this.client = client;
1165
+ this.visitorToken = visitorToken;
1166
+ this.sessionToken = sessionToken;
1167
+ }
1168
+ /**
1169
+ * Use an anonymous visitor token (UUID stored in the consumer's
1170
+ * own httpOnly cookie). Same shopper across page loads, no
1171
+ * sign-in required.
1172
+ */
1173
+ forVisitor(visitorToken) {
1174
+ return new BiabDevCartClient(this.client, visitorToken, null);
1175
+ }
1176
+ /**
1177
+ * Use a tenantAuth session token instead of a visitor token.
1178
+ * Carts are now keyed to the signed-in customer; persists
1179
+ * across devices.
1180
+ */
1181
+ withSession(sessionToken) {
1182
+ return new BiabDevCartClient(this.client, null, sessionToken);
1183
+ }
1184
+ headers() {
1185
+ const out = {};
1186
+ if (this.visitorToken)
1187
+ out["X-BIAB-Cart-Visitor"] = this.visitorToken;
1188
+ if (this.sessionToken)
1189
+ out["X-BIAB-Session-Token"] = this.sessionToken;
1190
+ return out;
1191
+ }
1192
+ /**
1193
+ * Legacy embed-session mint (kept for iframe-widget consumers).
1194
+ * The programmatic methods below let the consumer site render
1195
+ * its own native cart UI.
1196
+ */
1197
+ async createSession() {
1198
+ return this.client.request({
1199
+ method: "POST",
1200
+ path: "cart/session",
1201
+ responseSchema: cartSessionResponseSchema,
1202
+ });
1203
+ }
1204
+ /**
1205
+ * Read the current cart snapshot — item count, items with
1206
+ * resolved product detail (name, image, variant title, unit
1207
+ * price), subtotal, currency. Auto-creates an empty cart row
1208
+ * the first time it's called for a new visitor/session, so
1209
+ * the consumer always gets a valid snapshot.
1210
+ */
1211
+ async get() {
1212
+ return this.client.request({
1213
+ path: "cart",
1214
+ responseSchema: cartSnapshotSchema,
1215
+ headers: this.headers(),
1216
+ });
1217
+ }
1218
+ /**
1219
+ * Add a product (optionally with a variant) to the cart. If the
1220
+ * (productId, variantId) already exists, increments its quantity
1221
+ * rather than creating a duplicate line.
1222
+ */
1223
+ async addItem(input) {
1224
+ const body = cartAddItemInputSchema.parse(input);
1225
+ return this.client.request({
1226
+ method: "POST",
1227
+ path: "cart/items",
1228
+ body,
1229
+ responseSchema: cartSnapshotSchema,
1230
+ headers: this.headers(),
1231
+ });
1232
+ }
1233
+ /**
1234
+ * Set a line's quantity. Pass quantity=0 to delete the line.
1235
+ */
1236
+ async updateItem(itemId, input) {
1237
+ const body = cartUpdateItemInputSchema.parse(input);
1238
+ return this.client.request({
1239
+ method: "PATCH",
1240
+ path: `cart/items/${encodeURIComponent(itemId)}`,
1241
+ body,
1242
+ responseSchema: cartSnapshotSchema,
1243
+ headers: this.headers(),
1244
+ });
1245
+ }
1246
+ async removeItem(itemId) {
1247
+ return this.client.request({
1248
+ method: "DELETE",
1249
+ path: `cart/items/${encodeURIComponent(itemId)}`,
1250
+ responseSchema: cartSnapshotSchema,
1251
+ headers: this.headers(),
1252
+ });
1253
+ }
1254
+ /**
1255
+ * Stamp a coupon code on the cart. Validation runs at checkout —
1256
+ * stamping here is a no-op when invalid; the consumer learns
1257
+ * via the Stripe-checkout-session response whether the code
1258
+ * actually applies.
1259
+ */
1260
+ async applyCoupon(input) {
1261
+ const body = cartCouponInputSchema.parse(input);
1262
+ return this.client.request({
1263
+ method: "POST",
1264
+ path: "cart/coupon",
1265
+ body,
1266
+ responseSchema: cartSnapshotSchema,
1267
+ headers: this.headers(),
1268
+ });
1269
+ }
1270
+ async removeCoupon() {
1271
+ return this.client.request({
1272
+ method: "DELETE",
1273
+ path: "cart/coupon",
1274
+ responseSchema: cartSnapshotSchema,
1275
+ headers: this.headers(),
1276
+ });
1277
+ }
1278
+ /** Empty all items + drop the stamped coupon. Keeps the cart row itself. */
1279
+ async clear() {
1280
+ return this.client.request({
1281
+ method: "POST",
1282
+ path: "cart/clear",
1283
+ responseSchema: cartSnapshotSchema,
1284
+ headers: this.headers(),
1285
+ });
1286
+ }
1287
+ }
1288
+ export class BiabDevCheckoutClient {
1289
+ client;
1290
+ visitorToken;
1291
+ sessionToken;
1292
+ constructor(client, visitorToken = null, sessionToken = null) {
1293
+ this.client = client;
1294
+ this.visitorToken = visitorToken;
1295
+ this.sessionToken = sessionToken;
1296
+ }
1297
+ /**
1298
+ * Authenticate the start-from-cart call as an anonymous shopper
1299
+ * (visitor-token cart). Same UUID you used with `client.cart.forVisitor(...)`.
1300
+ */
1301
+ forVisitor(visitorToken) {
1302
+ return new BiabDevCheckoutClient(this.client, visitorToken, null);
1303
+ }
1304
+ /**
1305
+ * Authenticate as a signed-in customer (tenantAuth session token).
1306
+ * The Stripe Checkout Session will be created against this
1307
+ * customer's saved cart.
1308
+ */
1309
+ withSession(sessionToken) {
1310
+ return new BiabDevCheckoutClient(this.client, null, sessionToken);
1311
+ }
1312
+ headers() {
1313
+ const out = {};
1314
+ if (this.visitorToken)
1315
+ out["X-BIAB-Cart-Visitor"] = this.visitorToken;
1316
+ if (this.sessionToken)
1317
+ out["X-BIAB-Session-Token"] = this.sessionToken;
1318
+ return out;
1319
+ }
1320
+ /**
1321
+ * Legacy iframe-embed mint — gives back a tokenized URL to render
1322
+ * inside an iframe that redirects to Stripe. Kept for `<Checkout/>`
1323
+ * consumers; new code should call `start()` for native handoff.
1324
+ */
1325
+ async createSession(input) {
1326
+ const body = checkoutSessionInputSchema.parse(input);
1327
+ return this.client.request({
1328
+ method: "POST",
1329
+ path: "checkout/session",
1330
+ body,
1331
+ responseSchema: checkoutSessionResponseSchema,
1332
+ });
1333
+ }
1334
+ /**
1335
+ * Convert the caller's saved BIAB cart (resolved via visitor token
1336
+ * or session token) into a Stripe Checkout Session. Returns the
1337
+ * Stripe-hosted URL; redirect the browser there to collect payment.
1338
+ *
1339
+ * `successUrl` + `cancelUrl` may be full URLs on the consumer's own
1340
+ * domain. We append `session_id={CHECKOUT_SESSION_ID}` to the
1341
+ * success URL automatically so the success page can call
1342
+ * `getStatus(sessionId)` to confirm payment.
1343
+ */
1344
+ async start(input) {
1345
+ const body = checkoutStartInputSchema.parse(input);
1346
+ return this.client.request({
1347
+ method: "POST",
1348
+ path: "checkout/start",
1349
+ body: body ?? {},
1350
+ responseSchema: checkoutStartResponseSchema,
1351
+ headers: this.headers(),
1352
+ });
1353
+ }
1354
+ /**
1355
+ * Look up a Stripe Checkout Session by id. Returns paid / unpaid
1356
+ * status plus the customer details Stripe collected — what the
1357
+ * consumer's "thank you" page calls before rendering.
1358
+ */
1359
+ async getStatus(sessionId) {
1360
+ return this.client.request({
1361
+ path: `checkout/${encodeURIComponent(sessionId)}`,
1362
+ responseSchema: checkoutStatusResponseSchema,
1363
+ });
1364
+ }
1365
+ }
1366
+ /**
1367
+ * White-glove Google Places proxy. BIAB owns the API key — consumer
1368
+ * code never sees it. Use at the checkout delivery-address input
1369
+ * to give shoppers text-search predictions + verify deliverability
1370
+ * before submitting the order.
1371
+ *
1372
+ * Mirrors the working reference in `Resources/addressParser/`:
1373
+ * - `search(query)` — text-search predictions (uses Google
1374
+ * `places:searchText`)
1375
+ * - `getDetails(placeId)` — placeId → `GoogleAddress` shape
1376
+ * - `verify(form)` — text-searches the entered address and
1377
+ * compares component-by-component
1378
+ *
1379
+ * Three pure converters are exported alongside as static methods —
1380
+ * the same ones the reference React hooks wrap, just plain functions
1381
+ * so they work outside React too.
1382
+ *
1383
+ * @example
1384
+ * ```tsx
1385
+ * // On each keystroke (debounced):
1386
+ * const result = await client.address.search({ query: input });
1387
+ * if (!result.enabled) return; // platform not configured; show plain input
1388
+ *
1389
+ * // When user picks a prediction:
1390
+ * const detail = await client.address.getDetails(prediction.place_id);
1391
+ * if (detail.enabled && detail.address) {
1392
+ * const form = BiabDevAddressClient.googleToFormValues(detail.address);
1393
+ * reactHookForm.reset(form);
1394
+ * }
1395
+ *
1396
+ * // Before submitting the order:
1397
+ * const v = await client.address.verify(form);
1398
+ * if (!v.isVerified) showDidYouMean(v.googleAddress);
1399
+ * ```
1400
+ */
1401
+ export class BiabDevAddressClient {
1402
+ client;
1403
+ constructor(client) {
1404
+ this.client = client;
1405
+ }
1406
+ async search(input) {
1407
+ const body = addressSearchInputSchema.parse(input);
1408
+ return this.client.request({
1409
+ method: "POST",
1410
+ path: "address/search",
1411
+ body,
1412
+ responseSchema: addressSearchResponseSchema,
1413
+ });
1414
+ }
1415
+ async getDetails(placeId) {
1416
+ return this.client.request({
1417
+ path: `address/details/${encodeURIComponent(placeId)}`,
1418
+ responseSchema: addressDetailsResponseSchema,
1419
+ });
1420
+ }
1421
+ async verify(input) {
1422
+ const body = addressFormValuesSchema.parse(input);
1423
+ return this.client.request({
1424
+ method: "POST",
1425
+ path: "address/validate",
1426
+ body,
1427
+ responseSchema: addressVerifyResponseSchema,
1428
+ });
1429
+ }
1430
+ /** Convert Google's `GoogleAddress` shape into a form-values object. */
1431
+ static googleToFormValues(g) {
1432
+ return {
1433
+ address_ln1: `${g.street_number} ${g.route}`.trim(),
1434
+ address_ln2: g.subpremise ?? "",
1435
+ city: g.locality,
1436
+ state: g.administrative_area_level_1,
1437
+ zip: g.postal_code,
1438
+ country: g.country,
1439
+ };
1440
+ }
1441
+ /** Convert a form-values object into Stripe's billing-address shape. */
1442
+ static formToStripeAddress(a) {
1443
+ return {
1444
+ line1: a.address_ln1,
1445
+ line2: a.address_ln2 || null,
1446
+ city: a.city,
1447
+ state: a.state,
1448
+ postal_code: a.zip,
1449
+ country: a.country,
1450
+ };
1451
+ }
1452
+ }
1453
+ /**
1454
+ * Org-side recurring offerings (gym memberships, monthly service
1455
+ * plans, software subscriptions). Lists are public read; the
1456
+ * checkout-start endpoint hands the customer off to Stripe-hosted
1457
+ * Checkout in `mode: 'subscription'`.
1458
+ *
1459
+ * Requires the org to have synced each offering to Stripe via the
1460
+ * dashboard's "Sync to Stripe" action — otherwise `startCheckout`
1461
+ * returns 409 `subscription_not_synced`.
1462
+ */
1463
+ export class BiabDevSubscriptionsClient {
1464
+ client;
1465
+ constructor(client) {
1466
+ this.client = client;
1467
+ }
1468
+ async list() {
1469
+ return this.client.request({
1470
+ path: "subscriptions",
1471
+ responseSchema: subscriptionOfferingsListResponseSchema,
1472
+ });
1473
+ }
1474
+ async get(id) {
1475
+ return this.client.request({
1476
+ path: `subscriptions/${encodeURIComponent(id)}`,
1477
+ responseSchema: subscriptionOfferingSchema,
1478
+ });
1479
+ }
1480
+ async startCheckout(id, input) {
1481
+ const body = subscriptionCheckoutInputSchema.parse(input);
1482
+ return this.client.request({
1483
+ method: "POST",
1484
+ path: `subscriptions/${encodeURIComponent(id)}/checkout`,
1485
+ body: body ?? {},
1486
+ responseSchema: subscriptionCheckoutResponseSchema,
1487
+ });
1488
+ }
1489
+ }
1490
+ export class BiabDevCouponsClient {
1491
+ client;
1492
+ constructor(client) {
1493
+ this.client = client;
1494
+ }
1495
+ /**
1496
+ * Validate a coupon code against the org's Stripe-connected
1497
+ * account. Returns the discount shape (percent / amount /
1498
+ * duration) when valid; `{ valid: false, reason, message }`
1499
+ * otherwise so the consumer's UI can show "$10 off" before the
1500
+ * shopper proceeds to checkout.
1501
+ *
1502
+ * Source of truth is Stripe — what this endpoint validates is
1503
+ * exactly what Stripe-hosted Checkout will accept.
1504
+ *
1505
+ * @example
1506
+ * ```ts
1507
+ * const result = await client.coupons.validate("SAVE10");
1508
+ * if (result.valid) showDiscount(result.percentOff ?? result.amountOffCents);
1509
+ * else toast.error(result.message);
1510
+ * ```
1511
+ */
1512
+ async validate(code) {
1513
+ const body = couponValidateInputSchema.parse({ code });
1514
+ return this.client.request({
1515
+ method: "POST",
1516
+ path: "coupons/validate",
1517
+ body,
1518
+ responseSchema: couponValidateResponseSchema,
1519
+ });
1520
+ }
1521
+ }
1522
+ /**
1523
+ * Shipping: rate lookups + on-demand tracking. White-glove model —
1524
+ * BIAB owns the carrier integration; consumer code never sees the
1525
+ * underlying provider name. Gated on `ecommerce.shipping` so calls
1526
+ * from orgs on the Launch plan return the standard plan-gate body
1527
+ * via {@link BiabApiError} (status 403).
1528
+ *
1529
+ * @example
1530
+ * ```ts
1531
+ * const { options, requiresShipping } = await client.shipping
1532
+ * .forVisitor(visitorToken)
1533
+ * .getRates({ deliveryAddress });
1534
+ * if (!requiresShipping) skipShippingStep();
1535
+ * else {
1536
+ * const picked = options[0]; // cheapest
1537
+ * await client.checkout.forVisitor(visitorToken).start({
1538
+ * shipping: picked,
1539
+ * successUrl, cancelUrl,
1540
+ * });
1541
+ * }
1542
+ * ```
1543
+ */
1544
+ export class BiabDevShippingClient {
1545
+ client;
1546
+ visitorToken;
1547
+ sessionToken;
1548
+ constructor(client, visitorToken = null, sessionToken = null) {
1549
+ this.client = client;
1550
+ this.visitorToken = visitorToken;
1551
+ this.sessionToken = sessionToken;
1552
+ }
1553
+ forVisitor(visitorToken) {
1554
+ return new BiabDevShippingClient(this.client, visitorToken, null);
1555
+ }
1556
+ withSession(sessionToken) {
1557
+ return new BiabDevShippingClient(this.client, null, sessionToken);
1558
+ }
1559
+ headers() {
1560
+ const out = {};
1561
+ if (this.visitorToken)
1562
+ out["X-BIAB-Cart-Visitor"] = this.visitorToken;
1563
+ if (this.sessionToken)
1564
+ out["X-BIAB-Session-Token"] = this.sessionToken;
1565
+ return out;
1566
+ }
1567
+ /**
1568
+ * Fetch shipping rate options for the caller's cart + a delivery
1569
+ * address. Multi-origin + multi-parcel orders surface as a single
1570
+ * combined cost per service tier (carrier+speed). Pass the chosen
1571
+ * option directly to `client.checkout.start({ shipping })`.
1572
+ */
1573
+ async getRates(input) {
1574
+ const body = shippingRatesInputSchema.parse(input);
1575
+ return this.client.request({
1576
+ method: "POST",
1577
+ path: "shipping/rates",
1578
+ body,
1579
+ responseSchema: shippingRatesResponseSchema,
1580
+ headers: this.headers(),
1581
+ });
1582
+ }
1583
+ /**
1584
+ * On-demand tracking lookup for a known tracking number. Returns
1585
+ * the cached `status_history` we already received via webhook
1586
+ * PLUS a fresh poll against the carrier (`liveStatus`). Consumer
1587
+ * apps that don't subscribe to push notifications use this to
1588
+ * refresh "Where's my order?" pages.
1589
+ */
1590
+ async track(input) {
1591
+ return this.client.request({
1592
+ path: `shipping/track/${encodeURIComponent(input.trackingNumber)}`,
1593
+ responseSchema: shippingTrackResponseSchema,
1594
+ });
1595
+ }
1596
+ /**
1597
+ * The org's storefront shipping config — whether shipping is on, a coarse
1598
+ * "ships from" (city/state/country), the destinations it ships to, the duty
1599
+ * model, and preferred carriers. Sanitized (no keys/secrets, no precise
1600
+ * origin address). Render shipping copy without the org re-declaring it.
1601
+ *
1602
+ * @example
1603
+ * ```ts
1604
+ * const s = await client.shipping.settings();
1605
+ * if (s.enabled) show(`Ships to ${s.allowedDestinationCountries?.join(", ") ?? "anywhere"}`);
1606
+ * ```
1607
+ */
1608
+ async settings() {
1609
+ return this.client.request({
1610
+ path: "shipping/settings",
1611
+ responseSchema: shippingSettingsResponseSchema,
1612
+ });
1613
+ }
1614
+ }
1615
+ /**
1616
+ * Customer notification preferences. Per-customer-per-org channel
1617
+ * settings (in-app / email / SMS / outbound webhook). Identified by
1618
+ * the tenantAuth session token — bind with `.withSession(token)`.
1619
+ *
1620
+ * Outbound-webhook events the consumer site can subscribe to are
1621
+ * the same as the shipping fan-out keys:
1622
+ * - `order.shipped`
1623
+ * - `order.out_for_delivery`
1624
+ * - `order.delivered`
1625
+ * - `order.exception`
1626
+ * - `order.return_label_created`
1627
+ *
1628
+ * The webhook payload is JSON + signed with the customer's saved
1629
+ * `webhookSigningSecret` via `X-BIAB-Signature: <hex hmac-sha256>`.
1630
+ * Consumer's receiver verifies via:
1631
+ * crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
1632
+ */
1633
+ export class BiabDevNotificationsClient {
1634
+ client;
1635
+ sessionToken;
1636
+ constructor(client, sessionToken = null) {
1637
+ this.client = client;
1638
+ this.sessionToken = sessionToken;
1639
+ }
1640
+ withSession(sessionToken) {
1641
+ return new BiabDevNotificationsClient(this.client, sessionToken);
1642
+ }
1643
+ headers() {
1644
+ return this.sessionToken
1645
+ ? { "X-BIAB-Session-Token": this.sessionToken }
1646
+ : {};
1647
+ }
1648
+ async getPreferences() {
1649
+ return this.client.request({
1650
+ path: "notifications/preferences",
1651
+ responseSchema: customerNotificationPreferencesResponseSchema,
1652
+ headers: this.headers(),
1653
+ });
1654
+ }
1655
+ async updatePreferences(input) {
1656
+ const body = customerNotificationPreferencesUpdateInputSchema.parse(input);
1657
+ return this.client.request({
1658
+ method: "PATCH",
1659
+ path: "notifications/preferences",
1660
+ body,
1661
+ responseSchema: customerNotificationPreferencesUpdateResponseSchema,
1662
+ headers: this.headers(),
1663
+ });
1664
+ }
1665
+ /**
1666
+ * Start a verification flow for a destination the customer wants to
1667
+ * use for shipping notifications. Email: a verification link is sent
1668
+ * (15-min TTL). Phone: a 6-digit OTP is sent (5-min TTL).
1669
+ *
1670
+ * Until verified, the channel stays inert — preventing the customer
1671
+ * from spamming arbitrary destinations.
1672
+ */
1673
+ async startVerification(input) {
1674
+ const body = startVerificationInputSchema.parse(input);
1675
+ return this.client.request({
1676
+ method: "POST",
1677
+ path: "notifications/preferences/verify",
1678
+ body,
1679
+ responseSchema: startVerificationResponseSchema,
1680
+ headers: this.headers(),
1681
+ });
1682
+ }
1683
+ /**
1684
+ * Consume the verification token from the email link / SMS OTP.
1685
+ * On success: stamps `*VerifiedAt` on the prefs row + activates the
1686
+ * channel.
1687
+ */
1688
+ async confirmVerification(input) {
1689
+ const body = confirmVerificationInputSchema.parse(input);
1690
+ return this.client.request({
1691
+ method: "POST",
1692
+ path: "notifications/preferences/verify/confirm",
1693
+ body,
1694
+ responseSchema: confirmVerificationResponseSchema,
1695
+ headers: this.headers(),
1696
+ });
1697
+ }
1698
+ }
1699
+ export class BiabDevBlogClient {
1700
+ client;
1701
+ sessionToken;
1702
+ constructor(client, sessionToken = null) {
1703
+ this.client = client;
1704
+ this.sessionToken = sessionToken;
1705
+ }
1706
+ /**
1707
+ * Chainable: return a blog client whose `postComment` + future
1708
+ * write methods authenticate via the tenantAuth session token.
1709
+ * Reads (listPosts, getPost, listCategories, listTags, listComments)
1710
+ * don't require a session and work the same either way.
1711
+ *
1712
+ * @example
1713
+ * ```ts
1714
+ * await client.blog().withSession(token).postComment(slug, { content });
1715
+ * ```
1716
+ */
1717
+ withSession(sessionToken) {
1718
+ return new BiabDevBlogClient(this.client, sessionToken);
1719
+ }
1720
+ headers() {
1721
+ return this.sessionToken
1722
+ ? { "X-BIAB-Session-Token": this.sessionToken }
1723
+ : {};
1724
+ }
1725
+ /**
1726
+ * Legacy embed-session mint (kept for backwards compat with
1727
+ * embedded-iframe consumers).
1728
+ */
1729
+ async createSession(input) {
1730
+ const body = blogSessionInputSchema.parse(input ?? undefined);
1731
+ return this.client.request({
1732
+ method: "POST",
1733
+ path: "blog/session",
1734
+ body: body ?? {},
1735
+ responseSchema: blogSessionResponseSchema,
1736
+ });
1737
+ }
1738
+ /**
1739
+ * Paginated list of published blog posts for the calling org's
1740
+ * site. Cursor is an opaque number — pass back the `nextCursor`
1741
+ * from the previous response to fetch the next page.
1742
+ */
1743
+ async listPosts(input) {
1744
+ const query = {};
1745
+ if (input?.limit !== undefined)
1746
+ query["limit"] = input.limit;
1747
+ if (input?.cursor != null)
1748
+ query["cursor"] = input.cursor;
1749
+ if (input?.categoryId)
1750
+ query["categoryId"] = input.categoryId;
1751
+ return this.client.request({
1752
+ path: "blog/posts",
1753
+ query,
1754
+ responseSchema: blogListPostsResponseSchema,
1755
+ });
1756
+ }
1757
+ /**
1758
+ * Get a single post by its slug. Returns `{ post, access }`
1759
+ * where `access === 'paywall'` and `post.content === ''` for
1760
+ * posts marked `accessLevel: 'paid' | 'followers'`. The
1761
+ * consumer renders a paywall over the empty body when access
1762
+ * is restricted.
1763
+ */
1764
+ async getPost(slug) {
1765
+ return this.client.request({
1766
+ path: `blog/posts/${encodeURIComponent(slug)}`,
1767
+ responseSchema: blogPostBySlugResponseSchema,
1768
+ });
1769
+ }
1770
+ async listCategories() {
1771
+ return this.client.request({
1772
+ path: "blog/categories",
1773
+ responseSchema: blogCategoryListResponseSchema,
1774
+ });
1775
+ }
1776
+ async listTags() {
1777
+ return this.client.request({
1778
+ path: "blog/tags",
1779
+ responseSchema: blogTagListResponseSchema,
1780
+ });
1781
+ }
1782
+ async listComments(slug, input) {
1783
+ const query = {};
1784
+ if (input?.limit !== undefined)
1785
+ query["limit"] = input.limit;
1786
+ if (input?.cursor != null)
1787
+ query["cursor"] = input.cursor;
1788
+ return this.client.request({
1789
+ path: `blog/posts/${encodeURIComponent(slug)}/comments`,
1790
+ query,
1791
+ responseSchema: blogCommentListResponseSchema,
1792
+ });
1793
+ }
1794
+ /**
1795
+ * Submit a comment on a published post. Requires `withSession()` —
1796
+ * anonymous comments aren't supported in 0.9.0 (the blog_comments
1797
+ * row references a `user_id` text column that's NOT NULL). The
1798
+ * server runs the same moderation screener used by the dashboard
1799
+ * tRPC path; the response surfaces both the comment row + the
1800
+ * moderation verdict so the consumer can show "your comment is
1801
+ * pending review" when applicable.
1802
+ */
1803
+ async postComment(slug, input) {
1804
+ const body = blogCommentPostInputSchema.parse(input);
1805
+ return this.client.request({
1806
+ method: "POST",
1807
+ path: `blog/posts/${encodeURIComponent(slug)}/comments`,
1808
+ body,
1809
+ responseSchema: blogCommentPostResponseSchema,
1810
+ headers: this.headers(),
1811
+ });
1812
+ }
1813
+ /**
1814
+ * Current like state + count for a post. With `withSession()`, `liked`
1815
+ * reflects whether this visitor already liked it (filled heart on load);
1816
+ * without a session the public count is returned with `liked: false`.
1817
+ */
1818
+ async likeStatus(slug) {
1819
+ return this.client.request({
1820
+ path: `blog/posts/${encodeURIComponent(slug)}/like`,
1821
+ responseSchema: blogLikeResponseSchema,
1822
+ headers: this.headers(),
1823
+ });
1824
+ }
1825
+ /**
1826
+ * Toggle the session visitor's like on a post. Requires `withSession()`
1827
+ * (same identity as `postComment`). Returns the new like state + count.
1828
+ */
1829
+ async toggleLike(slug) {
1830
+ return this.client.request({
1831
+ method: "POST",
1832
+ path: `blog/posts/${encodeURIComponent(slug)}/like`,
1833
+ responseSchema: blogLikeResponseSchema,
1834
+ headers: this.headers(),
1835
+ });
1836
+ }
1837
+ }
1838
+ /**
1839
+ * Public reviews-wall client. Reads the org's external reviews
1840
+ * (Google + Yelp + manual + CSV) — the same store the marketing bundle's
1841
+ * `reviews` aggregate is built from. The bundle ships the first page + the
1842
+ * aggregate (count / average / histogram); use this to lazily fetch deeper
1843
+ * pages on "View all" / scroll.
1844
+ *
1845
+ * @example
1846
+ * ```ts
1847
+ * const page1 = await client.reviews.list({ limit: 10 });
1848
+ * if (page1.nextOffset != null) {
1849
+ * const page2 = await client.reviews.list({ limit: 10, offset: page1.nextOffset });
1850
+ * }
1851
+ * ```
1852
+ */
1853
+ export class BiabDevReviewsClient {
1854
+ client;
1855
+ constructor(client) {
1856
+ this.client = client;
1857
+ }
1858
+ /**
1859
+ * One page of the org's public reviews, newest-first. `limit` defaults to
1860
+ * the server default (10, max 50). Pass the previous response's
1861
+ * `nextOffset` to page forward — it's `null` once the list is exhausted.
1862
+ * `source` narrows to a single platform (e.g. `"yelp"`).
1863
+ */
1864
+ async list(input) {
1865
+ const query = {};
1866
+ if (input?.limit !== undefined)
1867
+ query["limit"] = input.limit;
1868
+ if (input?.offset !== undefined)
1869
+ query["offset"] = input.offset;
1870
+ if (input?.source)
1871
+ query["source"] = input.source;
1872
+ return this.client.request({
1873
+ path: "reviews",
1874
+ query,
1875
+ responseSchema: reviewWallListResponseSchema,
1876
+ });
1877
+ }
1878
+ }
1879
+ /**
1880
+ * Public hiring client — the org's open job postings and their categories, so
1881
+ * a consumer can render a careers page in their own design system.
1882
+ *
1883
+ * Read-only by construction: everything returned is content the org published
1884
+ * for candidates. To take applications, hand the posting's `applicationFormId`
1885
+ * to the forms surface — this client grants no submit path of its own.
1886
+ *
1887
+ * @example
1888
+ * ```ts
1889
+ * const { items } = await client.hiring.list({ limit: 20 });
1890
+ * const posting = await client.hiring.get("senior-installer");
1891
+ * if (posting.applicationFormId) {
1892
+ * const form = await client.forms.schema(posting.applicationFormId);
1893
+ * }
1894
+ * ```
1895
+ */
1896
+ export class BiabDevHiringClient {
1897
+ client;
1898
+ constructor(client) {
1899
+ this.client = client;
1900
+ }
1901
+ /**
1902
+ * One page of open postings, newest-first. `limit` defaults to the server
1903
+ * default (25, max 100). Pass the previous response's `nextOffset` to page
1904
+ * forward — it's `null` once the list is exhausted. `category` narrows to a
1905
+ * single group by its slug.
1906
+ *
1907
+ * Only postings that are published, active, have seats left, and are under
1908
+ * their application cap are returned, so a careers page never advertises a
1909
+ * role that can no longer be applied to.
1910
+ */
1911
+ async list(input) {
1912
+ const query = {};
1913
+ if (input?.limit !== undefined)
1914
+ query["limit"] = input.limit;
1915
+ if (input?.offset !== undefined)
1916
+ query["offset"] = input.offset;
1917
+ if (input?.category)
1918
+ query["category"] = input.category;
1919
+ return this.client.request({
1920
+ path: "hiring/jobs",
1921
+ query,
1922
+ responseSchema: jobPostingListResponseSchema,
1923
+ });
1924
+ }
1925
+ /**
1926
+ * One open posting by slug. Rejects with a 404 error when the posting is a
1927
+ * draft, closed, archived, fully filled, or has hit its application cap —
1928
+ * treat that as "this role is gone" rather than as a transient failure.
1929
+ */
1930
+ async get(slug) {
1931
+ return this.client.request({
1932
+ path: `hiring/jobs/${encodeURIComponent(slug)}`,
1933
+ responseSchema: jobPostingSchema,
1934
+ });
1935
+ }
1936
+ /**
1937
+ * The org's job categories with a count of open postings in each — enough to
1938
+ * render filter chips or grouped sections without fetching every posting.
1939
+ */
1940
+ async categories() {
1941
+ return this.client.request({
1942
+ path: "hiring/categories",
1943
+ responseSchema: jobCategoryListResponseSchema,
1944
+ });
1945
+ }
1946
+ }
1947
+ export class BiabDevChatbotClient {
1948
+ client;
1949
+ constructor(client) {
1950
+ this.client = client;
1951
+ }
1952
+ /** Returns the iframe URL for `<Chatbot/>`. */
1953
+ async createSession() {
1954
+ return this.client.request({
1955
+ method: "POST",
1956
+ path: "chatbot/session",
1957
+ responseSchema: chatbotSessionResponseSchema,
1958
+ });
1959
+ }
1960
+ /** Reads the org's persisted chatbot config (system prompt, KB, model). */
1961
+ async getConfig() {
1962
+ return this.client.request({
1963
+ method: "GET",
1964
+ path: "chatbot/config",
1965
+ responseSchema: chatbotConfigSchema,
1966
+ });
1967
+ }
1968
+ /**
1969
+ * Persists the org's chatbot config. Requires `chatbot:write` scope on the
1970
+ * API key — typically only used by admin tools, not embedded widgets.
1971
+ */
1972
+ async updateConfig(input) {
1973
+ const body = chatbotConfigUpdateInputSchema.parse(input);
1974
+ return this.client.request({
1975
+ method: "PATCH",
1976
+ path: "chatbot/config",
1977
+ body,
1978
+ responseSchema: chatbotConfigSchema,
1979
+ });
1980
+ }
1981
+ /**
1982
+ * Headless chat — host owns the UI. Each request is fully org-scoped: the
1983
+ * server runs the LLM with the org's saved system prompt + KB + model.
1984
+ *
1985
+ * The response includes `uiActions` (e.g. `show_form`) and `availability`
1986
+ * (front-desk presence + wait-time hint). Headless consumers should resolve
1987
+ * any `show_form` action via {@link getChatForm}, render the fields, and POST
1988
+ * back through {@link submitChatForm} (which carries the chat transcript).
1989
+ */
1990
+ async chat(input) {
1991
+ const body = chatbotChatInputSchema.parse(input);
1992
+ return this.client.request({
1993
+ method: "POST",
1994
+ path: "chatbot/chat",
1995
+ body,
1996
+ responseSchema: chatbotChatResponseSchema,
1997
+ });
1998
+ }
1999
+ /**
2000
+ * Upload a file a visitor attached in the chat. Presigns a direct-to-R2 PUT
2001
+ * (gated by the org's `chat.file_uploads` entitlement — check
2002
+ * `getConfig().fileUploadsEnabled` before offering it), uploads the bytes, and
2003
+ * returns a ref you can attach to the conversation. `sessionId` is any stable
2004
+ * per-chat id (drives the 5-files/hour cap + provenance). Throws on a refused
2005
+ * upload (entitlement, 7 MB cap, or rate limit).
2006
+ */
2007
+ async uploadFile(args) {
2008
+ const contentType = args.file.type || "application/octet-stream";
2009
+ const presigned = await this.client.request({
2010
+ method: "POST",
2011
+ path: "chatbot/upload-url",
2012
+ body: {
2013
+ sessionId: args.sessionId,
2014
+ filename: args.file.name,
2015
+ contentType,
2016
+ sizeBytes: args.file.size,
2017
+ uploaderEmail: args.uploaderEmail ?? null,
2018
+ },
2019
+ responseSchema: chatbotUploadUrlResponseSchema,
2020
+ });
2021
+ const put = await fetch(presigned.uploadUrl, {
2022
+ method: "PUT",
2023
+ headers: { "Content-Type": contentType },
2024
+ body: args.file,
2025
+ });
2026
+ if (!put.ok)
2027
+ throw new Error(`Upload failed (${put.status}).`);
2028
+ return {
2029
+ url: presigned.publicUrl,
2030
+ name: args.file.name,
2031
+ type: contentType,
2032
+ size: args.file.size,
2033
+ };
2034
+ }
2035
+ /**
2036
+ * Live front-desk availability — tier flag, staff-online presence, and
2037
+ * a wait-time hint string. Safe to poll (every 20–30s) so the SDK
2038
+ * consumer can render or hide a "Talk to a person" CTA without sending
2039
+ * a chat turn.
2040
+ */
2041
+ async getAvailability() {
2042
+ return this.client.request({
2043
+ method: "GET",
2044
+ path: "chatbot/availability",
2045
+ responseSchema: chatbotAvailabilitySchema,
2046
+ });
2047
+ }
2048
+ /**
2049
+ * Fetch a **Live** form definition to render inline on your own page.
2050
+ * A form is reachable here when it is Live (`isActive`) — independent of
2051
+ * `chatbotAccess`. Returns a 404-equivalent throw when the form is missing
2052
+ * or not Live. For a form the AI chatbot recommended via a `show_form`
2053
+ * action, use {@link getChatForm} instead (it carries the chat transcript on
2054
+ * submit).
2055
+ */
2056
+ async getForm(slug) {
2057
+ return this.client.request({
2058
+ method: "GET",
2059
+ path: `forms/${encodeURIComponent(slug)}`,
2060
+ responseSchema: chatbotFormDefinitionSchema,
2061
+ });
2062
+ }
2063
+ /**
2064
+ * Submit a **Live** form. Fans out to the same hiring/leads/inquiries CRM
2065
+ * mirrors, actions, scheduling, and staff notification as every other ingest
2066
+ * point. Gated on Live only — no `chatbotAccess` required.
2067
+ */
2068
+ async submitForm(slug, input) {
2069
+ const body = chatbotFormSubmitInputSchema.parse(input);
2070
+ return this.client.request({
2071
+ method: "POST",
2072
+ path: `forms/${encodeURIComponent(slug)}`,
2073
+ body,
2074
+ responseSchema: chatbotFormSubmitResponseSchema,
2075
+ });
2076
+ }
2077
+ /**
2078
+ * Fetch a form the AI chatbot recommended via a `show_form` UI action. This
2079
+ * additionally requires the form to be chatbot-accessible (`chatbotAccess`),
2080
+ * which a recommended form always is. Most embeds want {@link getForm}; reach
2081
+ * for this only inside the chat flow.
2082
+ */
2083
+ async getChatForm(slug) {
2084
+ return this.client.request({
2085
+ method: "GET",
2086
+ path: `chatbot/forms/${encodeURIComponent(slug)}`,
2087
+ responseSchema: chatbotFormDefinitionSchema,
2088
+ });
2089
+ }
2090
+ /**
2091
+ * Submit a chatbot-recommended form. Same CRM fan-out as {@link submitForm},
2092
+ * but on the chatbot path — pass `priorChat` so the resulting CRM inquiry can
2093
+ * include the chat transcript that led to the submission.
2094
+ */
2095
+ async submitChatForm(slug, input) {
2096
+ const body = chatbotFormSubmitInputSchema.parse(input);
2097
+ return this.client.request({
2098
+ method: "POST",
2099
+ path: `chatbot/forms/${encodeURIComponent(slug)}`,
2100
+ body,
2101
+ responseSchema: chatbotFormSubmitResponseSchema,
2102
+ });
2103
+ }
2104
+ // ── Front Desk (Batch JJ) ────────────────────────────────────────
2105
+ /**
2106
+ * Create or resume the persisted `chatSessions` row for this
2107
+ * visitor. Idempotent on `(orgId, visitorToken)`. The returned
2108
+ * `sessionId` is what you pass to `postMessage`, `pollMessages`,
2109
+ * and `requestHuman`.
2110
+ *
2111
+ * Distinct from {@link createSession} — that mints an iframe-embed
2112
+ * token. This one exposes the underlying server session so the
2113
+ * consumer can render their own UI and still reach Front Desk.
2114
+ */
2115
+ async createPersistedSession(input) {
2116
+ const body = chatbotPersistedSessionInputSchema.parse(input);
2117
+ return this.client.request({
2118
+ method: "POST",
2119
+ path: "chatbot/persisted-session",
2120
+ body,
2121
+ responseSchema: chatbotPersistedSessionResponseSchema,
2122
+ });
2123
+ }
2124
+ /**
2125
+ * Poll messages + session status. Pass `since` (ISO timestamp) on
2126
+ * subsequent polls to only fetch new rows. Recommended cadence:
2127
+ * 3–5s while the widget is open and focused.
2128
+ */
2129
+ async pollMessages(input) {
2130
+ return this.client.request({
2131
+ path: "chatbot/messages",
2132
+ query: {
2133
+ sessionId: input.sessionId,
2134
+ visitorToken: input.visitorToken,
2135
+ since: input.since,
2136
+ },
2137
+ responseSchema: chatbotMessagesResponseSchema,
2138
+ });
2139
+ }
2140
+ /**
2141
+ * Append a visitor message. Returns the updated message log so
2142
+ * the consumer can replace its local list with an authoritative
2143
+ * snapshot rather than tracking optimistic updates manually.
2144
+ */
2145
+ async postMessage(input) {
2146
+ const body = chatbotAppendMessageInputSchema.parse(input);
2147
+ return this.client.request({
2148
+ method: "POST",
2149
+ path: "chatbot/messages",
2150
+ body,
2151
+ responseSchema: chatbotMessagesResponseSchema,
2152
+ });
2153
+ }
2154
+ /**
2155
+ * Visitor presses "Talk to a person." Plan-gated on
2156
+ * `chat.front_desk` — orgs on Launch get back
2157
+ * `{ available: false, reason: 'plan_required', requiredFeature, upgradeUrl }`.
2158
+ * Orgs on Growth+ get `{ available: true, status, placeInLine, ... }`.
2159
+ *
2160
+ * Branch on `result.available` to render an upgrade CTA vs the
2161
+ * "you're in line" UI.
2162
+ *
2163
+ * @example
2164
+ * ```ts
2165
+ * const result = await client.chatbot.requestHuman({ sessionId, visitorToken });
2166
+ * if (!result.available) return <UpgradeCard feature={result.requiredFeature} />;
2167
+ * return <Queue position={result.placeInLine} eta={result.estimateLabel} />;
2168
+ * ```
2169
+ */
2170
+ async requestHuman(input) {
2171
+ const body = chatbotRequestHumanInputSchema.parse(input);
2172
+ return this.client.request({
2173
+ method: "POST",
2174
+ path: "chatbot/request-human",
2175
+ body,
2176
+ responseSchema: chatbotRequestHumanResponseSchema,
2177
+ });
2178
+ }
2179
+ }
2180
+ /**
2181
+ * Reads the active customer-portal context (organization + user) and exposes
2182
+ * profile mutations + a list of *only this user's* other customer
2183
+ * relationships.
2184
+ *
2185
+ * @remarks
2186
+ * **Privacy:** When `organizationId` is set, every request is pinned to that
2187
+ * org via the `X-BIAB-Customer-Portal-Org` header. The server-side
2188
+ * `customerPortalProcedure` will reject any access to a different org, so a
2189
+ * customer browsing a Dominoes-branded site can never accidentally see Pizza
2190
+ * Hut data even if their account is linked to both. This is the recommended
2191
+ * mode for embedding the SDK on a tenant's site or app.
2192
+ */
2193
+ /**
2194
+ * Decode a base64 string to bytes. Uses `atob` (available in browsers and
2195
+ * Node >= 16), so it needs no Buffer polyfill in browser bundles.
2196
+ */
2197
+ function decodeBase64ToBytes(base64) {
2198
+ const binary = atob(base64);
2199
+ const bytes = new Uint8Array(binary.length);
2200
+ for (let i = 0; i < binary.length; i++) {
2201
+ bytes[i] = binary.charCodeAt(i);
2202
+ }
2203
+ return bytes;
2204
+ }
2205
+ export class BiabDevCustomerPortalClient {
2206
+ client;
2207
+ organizationId;
2208
+ sessionToken;
2209
+ constructor(client, organizationId,
2210
+ /**
2211
+ * Optional tenantAuth session token. When set, requests include
2212
+ * an `X-BIAB-Session-Token` header and the BIAB customer-portal
2213
+ * routes resolve the calling user via the session payload
2214
+ * instead of the API key's user binding.
2215
+ *
2216
+ * This is the right path for consumer storefronts that don't
2217
+ * mint per-customer API keys: sign customers in via
2218
+ * `client.auth.signIn()`, store the resulting session token
2219
+ * (the SDK's `createAuthHandler` does this in an httpOnly
2220
+ * cookie automatically), then pass that token to
2221
+ * `client.customerPortal(orgId).withSession(token)` for every
2222
+ * customer-scoped call.
2223
+ */
2224
+ sessionToken = null) {
2225
+ this.client = client;
2226
+ this.organizationId = organizationId;
2227
+ this.sessionToken = sessionToken;
2228
+ }
2229
+ /**
2230
+ * Return a customer-portal client that authenticates the calling
2231
+ * user via the tenantAuth session token instead of (or in
2232
+ * addition to) the API key's user binding. Chainable.
2233
+ *
2234
+ * @example
2235
+ * ```ts
2236
+ * const session = await getTenantSessionFromCookie({ cookieValue });
2237
+ * const work = await client
2238
+ * .customerPortal(orgId)
2239
+ * .withSession(session.sessionToken)
2240
+ * .getWork();
2241
+ * ```
2242
+ */
2243
+ withSession(sessionToken) {
2244
+ return new BiabDevCustomerPortalClient(this.client, this.organizationId, sessionToken);
2245
+ }
2246
+ headers() {
2247
+ const out = {};
2248
+ if (this.organizationId) {
2249
+ out["X-BIAB-Customer-Portal-Org"] = this.organizationId;
2250
+ }
2251
+ if (this.sessionToken) {
2252
+ out["X-BIAB-Session-Token"] = this.sessionToken;
2253
+ }
2254
+ return out;
2255
+ }
2256
+ async context() {
2257
+ return this.client.request({
2258
+ path: "customer-portal/context",
2259
+ responseSchema: customerPortalContextResponseSchema,
2260
+ headers: this.headers(),
2261
+ });
2262
+ }
2263
+ async getProfile() {
2264
+ return this.client.request({
2265
+ path: "customer-portal/profile",
2266
+ responseSchema: customerPortalProfileSchema,
2267
+ headers: this.headers(),
2268
+ });
2269
+ }
2270
+ async updateProfile(input) {
2271
+ const body = customerPortalUpdateProfileInputSchema.parse(input);
2272
+ return this.client.request({
2273
+ method: "POST",
2274
+ path: "customer-portal/profile",
2275
+ body,
2276
+ responseSchema: customerPortalUpdateProfileResponseSchema,
2277
+ headers: this.headers(),
2278
+ });
2279
+ }
2280
+ async myOtherCustomerOrgs() {
2281
+ return this.client.request({
2282
+ path: "customer-portal/other-orgs",
2283
+ responseSchema: customerPortalOtherOrgsResponseSchema,
2284
+ headers: this.headers(),
2285
+ });
2286
+ }
2287
+ /**
2288
+ * Read the signed-in customer's notification preferences (per category,
2289
+ * per channel) for the bound org. The response also includes the full
2290
+ * category + channel catalog so SDK consumers can render the UI
2291
+ * without hardcoding either — both grow over time on the server side.
2292
+ *
2293
+ * @example
2294
+ * ```ts
2295
+ * const { preferences, categories, channels } =
2296
+ * await client.customerPortal().getNotificationPreferences();
2297
+ *
2298
+ * categories.map((c) => (
2299
+ * <Row category={c} state={preferences[c.id]} />
2300
+ * ));
2301
+ * ```
2302
+ */
2303
+ async getNotificationPreferences() {
2304
+ return this.client.request({
2305
+ path: "customer-portal/notification-preferences",
2306
+ responseSchema: notificationPreferencesResponseSchema,
2307
+ headers: this.headers(),
2308
+ });
2309
+ }
2310
+ /**
2311
+ * Update notification preferences. Sparse: only send the toggles you
2312
+ * want to flip — omitted keys keep their persisted (or default) value.
2313
+ * Compliance-required channels (e.g. transactional email) silently
2314
+ * stay true regardless of what's sent.
2315
+ *
2316
+ * @example
2317
+ * ```ts
2318
+ * await client.customerPortal().updateNotificationPreferences({
2319
+ * preferences: { marketing: { email: false, sms: false } },
2320
+ * });
2321
+ * ```
2322
+ */
2323
+ async updateNotificationPreferences(input) {
2324
+ const body = notificationPreferencesUpdateInputSchema.parse(input);
2325
+ return this.client.request({
2326
+ method: "POST",
2327
+ path: "customer-portal/notification-preferences",
2328
+ body,
2329
+ responseSchema: notificationPreferencesResponseSchema,
2330
+ headers: this.headers(),
2331
+ });
2332
+ }
2333
+ /**
2334
+ * Read the signed-in customer's "what's on my plate" bundle: open
2335
+ * jobs, pending quotes, unpaid invoices, contracts awaiting signature,
2336
+ * and a recent payment history, plus a roll-up summary for the
2337
+ * consumer header.
2338
+ *
2339
+ * URLs are host-less paths — resolve them against the consumer's own
2340
+ * storefront origin to build clickable links.
2341
+ *
2342
+ * @example
2343
+ * ```ts
2344
+ * const work = await client.customerPortal().getWork();
2345
+ * const { openJobCount, unpaidBalance } = work.summary;
2346
+ * work.invoices.filter((i) => i.balanceDue > 0).map(...)
2347
+ * ```
2348
+ */
2349
+ async getWork() {
2350
+ return this.client.request({
2351
+ path: "customer-portal/work",
2352
+ responseSchema: customerWorkBundleSchema,
2353
+ headers: this.headers(),
2354
+ });
2355
+ }
2356
+ /** Read a single job the signed-in customer owns. */
2357
+ async getJob(jobId) {
2358
+ return this.client.request({
2359
+ path: `customer-portal/jobs/${encodeURIComponent(jobId)}`,
2360
+ responseSchema: customerJobDetailSchema,
2361
+ headers: this.headers(),
2362
+ });
2363
+ }
2364
+ /** Read a single quote the signed-in customer owns. */
2365
+ async getQuote(quoteId) {
2366
+ return this.client.request({
2367
+ path: `customer-portal/quotes/${encodeURIComponent(quoteId)}`,
2368
+ responseSchema: customerQuoteDetailSchema,
2369
+ headers: this.headers(),
2370
+ });
2371
+ }
2372
+ /** Naming alias for {@link getQuote} — read a single quote the customer owns. */
2373
+ async viewQuote(quoteId) {
2374
+ return this.getQuote(quoteId);
2375
+ }
2376
+ /**
2377
+ * Accept a quote as the signed-in customer. Idempotent — re-accepting
2378
+ * an already-accepted quote resolves the existing state without
2379
+ * additional side effects.
2380
+ */
2381
+ async acceptQuote(quoteId) {
2382
+ return this.client.request({
2383
+ method: "POST",
2384
+ path: `customer-portal/quotes/${encodeURIComponent(quoteId)}/accept`,
2385
+ responseSchema: customerQuoteActionResponseSchema,
2386
+ headers: this.headers(),
2387
+ });
2388
+ }
2389
+ /** Reject a quote as the signed-in customer. Idempotent. */
2390
+ async rejectQuote(quoteId) {
2391
+ return this.client.request({
2392
+ method: "POST",
2393
+ path: `customer-portal/quotes/${encodeURIComponent(quoteId)}/reject`,
2394
+ responseSchema: customerQuoteActionResponseSchema,
2395
+ headers: this.headers(),
2396
+ });
2397
+ }
2398
+ /** Read a single invoice the signed-in customer owns. */
2399
+ async getInvoice(invoiceId) {
2400
+ return this.client.request({
2401
+ path: `customer-portal/invoices/${encodeURIComponent(invoiceId)}`,
2402
+ responseSchema: customerInvoiceDetailSchema,
2403
+ headers: this.headers(),
2404
+ });
2405
+ }
2406
+ /**
2407
+ * Start payment for an invoice the customer owns. Returns a Stripe-hosted
2408
+ * Checkout URL to redirect to — card entry happens entirely on Stripe, so
2409
+ * your app never touches card data (stays out of PCI scope).
2410
+ *
2411
+ * Pass your own `successUrl` / `cancelUrl` to return into your storefront,
2412
+ * or omit them to land back on the org's branded hosted invoice page.
2413
+ *
2414
+ * @example
2415
+ * ```ts
2416
+ * const { checkoutUrl } = await client
2417
+ * .customerPortal()
2418
+ * .payInvoice(invoiceId, { successUrl: "https://shop.example.com/paid" });
2419
+ * window.location.href = checkoutUrl;
2420
+ * ```
2421
+ */
2422
+ async payInvoice(invoiceId, opts) {
2423
+ const body = customerInvoicePayInputSchema.parse(opts ?? {});
2424
+ return this.client.request({
2425
+ method: "POST",
2426
+ path: `customer-portal/invoices/${encodeURIComponent(invoiceId)}/pay`,
2427
+ body,
2428
+ responseSchema: customerInvoicePayResponseSchema,
2429
+ headers: this.headers(),
2430
+ });
2431
+ }
2432
+ /**
2433
+ * Download the customer's invoice as a PDF. Returns the file metadata plus
2434
+ * the decoded `bytes` (a `Uint8Array`) — the auth headers ride along on the
2435
+ * SDK request, so this works for a signed-in customer without exposing a
2436
+ * public URL. Once paid, the PDF carries a PAID marker + payment history.
2437
+ *
2438
+ * @example Browser download
2439
+ * ```ts
2440
+ * const file = await client.customerPortal(orgId).withSession(token)
2441
+ * .downloadInvoicePdf(invoiceId);
2442
+ * const url = URL.createObjectURL(
2443
+ * new Blob([file.bytes], { type: file.contentType }),
2444
+ * );
2445
+ * const a = document.createElement("a");
2446
+ * a.href = url;
2447
+ * a.download = file.filename;
2448
+ * a.click();
2449
+ * URL.revokeObjectURL(url);
2450
+ * ```
2451
+ */
2452
+ async downloadInvoicePdf(invoiceId) {
2453
+ const res = await this.client.request({
2454
+ path: `customer-portal/invoices/${encodeURIComponent(invoiceId)}/pdf`,
2455
+ responseSchema: customerInvoicePdfSchema,
2456
+ headers: this.headers(),
2457
+ });
2458
+ return { ...res, bytes: decodeBase64ToBytes(res.base64) };
2459
+ }
2460
+ /** Read a single contract the signed-in customer owns. */
2461
+ async getContract(contractId) {
2462
+ return this.client.request({
2463
+ path: `customer-portal/contracts/${encodeURIComponent(contractId)}`,
2464
+ responseSchema: customerContractDetailSchema,
2465
+ headers: this.headers(),
2466
+ });
2467
+ }
2468
+ /**
2469
+ * Begin signing a contract the customer owns. Returns a ready, absolute
2470
+ * signing URL on the org's branded hostname — redirect the customer there
2471
+ * to BIAB's hosted e-sign page, which captures the signature and generates
2472
+ * the executed PDF. No signature payload is collected client-side, so the
2473
+ * legal record stays consistent across consumers.
2474
+ *
2475
+ * Rejects with `contract_already_signed` (409) or `contract_not_signable`
2476
+ * (409) when the contract isn't in a signable state.
2477
+ *
2478
+ * @example
2479
+ * ```ts
2480
+ * const { signingUrl } = await client
2481
+ * .customerPortal()
2482
+ * .signContract(contractId);
2483
+ * window.location.href = signingUrl;
2484
+ * ```
2485
+ */
2486
+ async signContract(contractId) {
2487
+ return this.client.request({
2488
+ method: "POST",
2489
+ path: `customer-portal/contracts/${encodeURIComponent(contractId)}/sign`,
2490
+ responseSchema: customerContractSignResponseSchema,
2491
+ headers: this.headers(),
2492
+ });
2493
+ }
2494
+ /** List reviews the signed-in customer has submitted in this org. */
2495
+ async listMyReviews() {
2496
+ return this.client.request({
2497
+ path: "customer-portal/reviews",
2498
+ responseSchema: customerReviewListResponseSchema,
2499
+ headers: this.headers(),
2500
+ });
2501
+ }
2502
+ /**
2503
+ * Referral roll-up for the signed-in customer (as an affiliate), summed
2504
+ * across every program they're enrolled in. `enrolled: false` with zeroed
2505
+ * counters when the customer holds no affiliate record. Real data appears
2506
+ * once the org's referral attribution + payout pipeline records activity.
2507
+ *
2508
+ * @example
2509
+ * ```ts
2510
+ * const stats = await client.customerPortal().myReferralStats();
2511
+ * if (stats.enrolled) showEarnings(stats.earningsCents);
2512
+ * ```
2513
+ */
2514
+ async myReferralStats() {
2515
+ return this.client.request({
2516
+ path: "customer-portal/referrals/stats",
2517
+ responseSchema: customerReferralStatsSchema,
2518
+ headers: this.headers(),
2519
+ });
2520
+ }
2521
+ /**
2522
+ * The customer's referral conversion feed (most recent first). Empty until
2523
+ * the attribution pipeline records conversions. `limit` 1–200 (default 50).
2524
+ */
2525
+ async myReferralHistory(opts) {
2526
+ const limit = opts?.limit;
2527
+ const qs = typeof limit === "number"
2528
+ ? `?limit=${encodeURIComponent(String(limit))}`
2529
+ : "";
2530
+ return this.client.request({
2531
+ path: `customer-portal/referrals/history${qs}`,
2532
+ responseSchema: customerReferralHistoryResponseSchema,
2533
+ headers: this.headers(),
2534
+ });
2535
+ }
2536
+ /** The customer's referral payout history + upcoming rows (newest first). */
2537
+ async myReferralPayouts() {
2538
+ return this.client.request({
2539
+ path: "customer-portal/referrals/payouts",
2540
+ responseSchema: customerReferralPayoutsResponseSchema,
2541
+ headers: this.headers(),
2542
+ });
2543
+ }
2544
+ /**
2545
+ * The signed-in customer's orders (newest first), each with a shipment
2546
+ * roll-up (`overallStatus`, `anyInTransit`, `latestEstimatedDelivery`) for
2547
+ * an orders/shipping dashboard list. `limit` 1–100 (default 30). Requires
2548
+ * the org's `ecommerce.shipping` entitlement.
2549
+ *
2550
+ * @example
2551
+ * ```ts
2552
+ * const { items } = await client.customerPortal().listOrders();
2553
+ * items.filter((o) => o.anyInTransit).map(renderTracker);
2554
+ * ```
2555
+ */
2556
+ async listOrders(opts) {
2557
+ const limit = opts?.limit;
2558
+ const qs = typeof limit === "number"
2559
+ ? `?limit=${encodeURIComponent(String(limit))}`
2560
+ : "";
2561
+ return this.client.request({
2562
+ path: `customer-portal/orders${qs}`,
2563
+ responseSchema: customerOrderListResponseSchema,
2564
+ headers: this.headers(),
2565
+ });
2566
+ }
2567
+ /**
2568
+ * The customer's full payment history with this org — invoice payments,
2569
+ * store-order charges, and refunds (negative amounts), newest first.
2570
+ * Powers a "Payments" tab on the customer dashboard.
2571
+ *
2572
+ * ```ts
2573
+ * const { items } = await client.customerPortal().listPayments();
2574
+ * items.map((p) => `${p.occurredAt} ${p.kind} $${p.amount}`);
2575
+ * ```
2576
+ */
2577
+ async listPayments(opts) {
2578
+ const limit = opts?.limit;
2579
+ const qs = typeof limit === "number"
2580
+ ? `?limit=${encodeURIComponent(String(limit))}`
2581
+ : "";
2582
+ return this.client.request({
2583
+ path: `customer-portal/payments${qs}`,
2584
+ responseSchema: customerPaymentHistoryResponseSchema,
2585
+ headers: this.headers(),
2586
+ });
2587
+ }
2588
+ /**
2589
+ * One order the customer owns: line items + shipments, each shipment with
2590
+ * its carrier, tracking link, status, and `statusHistory` timeline (read
2591
+ * from stored data — webhook-fresh).
2592
+ */
2593
+ async getOrder(orderId) {
2594
+ return this.client.request({
2595
+ path: `customer-portal/orders/${encodeURIComponent(orderId)}`,
2596
+ responseSchema: customerOrderDetailSchema,
2597
+ headers: this.headers(),
2598
+ });
2599
+ }
2600
+ /**
2601
+ * Open a return / refund request on an order the customer owns. Creates a
2602
+ * `requested` record and notifies the org's review queue — the org's
2603
+ * approval (not this call) triggers the actual Stripe refund. Omit `items`
2604
+ * for a whole-order request. The request then shows up under
2605
+ * `getOrder(orderId).returnRequests`.
2606
+ *
2607
+ * @example
2608
+ * ```ts
2609
+ * await portal.submitReturnRequest(orderId, {
2610
+ * reason: "damaged",
2611
+ * customerNote: "Arrived cracked.",
2612
+ * items: [{ orderItemId, quantity: 1 }],
2613
+ * });
2614
+ * ```
2615
+ */
2616
+ async submitReturnRequest(orderId, input) {
2617
+ const body = customerReturnRequestInputSchema.parse(input ?? {});
2618
+ return this.client.request({
2619
+ method: "POST",
2620
+ path: `customer-portal/orders/${encodeURIComponent(orderId)}/return-request`,
2621
+ body,
2622
+ responseSchema: customerReturnRequestCreatedSchema,
2623
+ headers: this.headers(),
2624
+ });
2625
+ }
2626
+ /**
2627
+ * List the digital downloads the customer is entitled to for a paid order
2628
+ * (P10d). Each item is a ready-to-use URL — a short-lived presigned link for
2629
+ * BIAB-hosted files (`expiresInSeconds` set) or the org's external link.
2630
+ * Returns an empty list until the order's payment settles.
2631
+ *
2632
+ * @example
2633
+ * ```ts
2634
+ * const { items } = await portal.getDownloads(orderId);
2635
+ * for (const d of items) console.log(d.productName, d.url);
2636
+ * ```
2637
+ */
2638
+ async getDownloads(orderId) {
2639
+ return this.client.request({
2640
+ path: `customer-portal/orders/${encodeURIComponent(orderId)}/downloads`,
2641
+ responseSchema: customerDownloadsResponseSchema,
2642
+ headers: this.headers(),
2643
+ });
2644
+ }
2645
+ /**
2646
+ * Track a single shipment the customer owns (resolved through their own
2647
+ * order). Returns the stored shipment plus a best-effort live carrier status
2648
+ * (`liveStatus`) when it's still in transit.
2649
+ */
2650
+ async trackShipment(shipmentId) {
2651
+ return this.client.request({
2652
+ path: `customer-portal/shipments/${encodeURIComponent(shipmentId)}/track`,
2653
+ responseSchema: customerShipmentTrackingSchema,
2654
+ headers: this.headers(),
2655
+ });
2656
+ }
2657
+ /**
2658
+ * Submit a review tied to the signed-in customer's email. Land
2659
+ * with `status: pending` so org staff can moderate before the
2660
+ * review surfaces on the public bundle. `jobId` is optional and
2661
+ * validated server-side against the customer's own jobs — a
2662
+ * stranger's id is rejected with a 403.
2663
+ */
2664
+ async submitReview(input) {
2665
+ const body = customerReviewSubmitInputSchema.parse(input);
2666
+ return this.client.request({
2667
+ method: "POST",
2668
+ path: "customer-portal/reviews",
2669
+ body,
2670
+ responseSchema: customerReviewSubmitResponseSchema,
2671
+ headers: this.headers(),
2672
+ });
2673
+ }
2674
+ /** List the comment thread on a job the customer owns. */
2675
+ async listJobComments(jobId) {
2676
+ return this.client.request({
2677
+ path: `customer-portal/jobs/${encodeURIComponent(jobId)}/comments`,
2678
+ responseSchema: customerJobCommentsListResponseSchema,
2679
+ headers: this.headers(),
2680
+ });
2681
+ }
2682
+ /** Post a customer comment on one of the customer's own jobs. */
2683
+ async postJobComment(jobId, input) {
2684
+ const body = customerJobCommentPostInputSchema.parse(input);
2685
+ return this.client.request({
2686
+ method: "POST",
2687
+ path: `customer-portal/jobs/${encodeURIComponent(jobId)}/comments`,
2688
+ body,
2689
+ responseSchema: customerJobCommentSchema,
2690
+ headers: this.headers(),
2691
+ });
2692
+ }
2693
+ /**
2694
+ * Open (or reuse) a front-desk chat session bound to the signed-in
2695
+ * customer and post their first message. Staff see the thread in
2696
+ * the chatbot inbox with `linkedContactId` set so they know who's
2697
+ * reaching out. Returns the `sessionId` + `visitorToken` so the
2698
+ * consumer can poll for staff replies or render the chatbot widget
2699
+ * over the same session via the existing chatbot embed flow.
2700
+ */
2701
+ async contactCompany(input) {
2702
+ const body = customerContactCompanyInputSchema.parse(input);
2703
+ return this.client.request({
2704
+ method: "POST",
2705
+ path: "customer-portal/contact-company",
2706
+ body,
2707
+ responseSchema: customerContactCompanyResponseSchema,
2708
+ headers: this.headers(),
2709
+ });
2710
+ }
2711
+ /**
2712
+ * Staff-on-the-way view for a job the customer owns. First-cut:
2713
+ * returns assigned-tech display names + dispatch status, the last
2714
+ * known GPS ping per tech (when available), and the most recent
2715
+ * ETA snapshot from the dispatch_offers cascade.
2716
+ *
2717
+ * `liveEtaAvailable: false` today — flips to true once the
2718
+ * dispatch travel-time recompute layer ships, at which point the
2719
+ * `eta` field switches sources without breaking this shape.
2720
+ */
2721
+ async getJobEta(jobId) {
2722
+ return this.client.request({
2723
+ path: `customer-portal/jobs/${encodeURIComponent(jobId)}/eta`,
2724
+ responseSchema: customerJobEtaResponseSchema,
2725
+ headers: this.headers(),
2726
+ });
2727
+ }
2728
+ /**
2729
+ * Unified job activity feed: structured events (status changes,
2730
+ * dispatch lifecycle, media uploads, quote/contract/invoice
2731
+ * transitions) interleaved with chat messages linked to this
2732
+ * customer's contact. One linear timeline so the customer can
2733
+ * see "what happened, then what was said, then what happened
2734
+ * next" without piecing together separate views.
2735
+ *
2736
+ * Each entry is `{ kind: 'event' | 'chat', id, at, ... }`. The
2737
+ * discriminated union means TypeScript narrows correctly when
2738
+ * you branch on `kind`.
2739
+ */
2740
+ async getJobActivity(jobId) {
2741
+ return this.client.request({
2742
+ path: `customer-portal/jobs/${encodeURIComponent(jobId)}/activity`,
2743
+ responseSchema: customerJobActivityResponseSchema,
2744
+ headers: this.headers(),
2745
+ });
2746
+ }
2747
+ }
2748
+ /**
2749
+ * Creates a {@link BiabDevClient} for the given host `baseUrl` and package API key.
2750
+ *
2751
+ * @remarks
2752
+ * **Alpha:** This package is still in alpha release. APIs may change; it is **not** intended
2753
+ * for production use yet. Prefer non-production environments until a stable major is published.
2754
+ */
2755
+ export function createBiabDevClient(options) {
2756
+ return new BiabDevClient(options);
2757
+ }
2758
+ /**
2759
+ * Per-tenant auth: hosted sign-in/sign-up scoped to the org bound to
2760
+ * the API key. Used by the SDK's `createAuthHandler` (server) and
2761
+ * `<SignIn />` / `<SignUp />` / `<SignOut />` components (client).
2762
+ */
2763
+ export class BiabDevAuthClient {
2764
+ client;
2765
+ constructor(client) {
2766
+ this.client = client;
2767
+ }
2768
+ async start(input) {
2769
+ const body = tenantAuthStartInputSchema.parse(input);
2770
+ return this.client.request({
2771
+ method: "POST",
2772
+ path: "auth/start",
2773
+ body,
2774
+ responseSchema: tenantAuthStartResponseSchema,
2775
+ });
2776
+ }
2777
+ /**
2778
+ * Ergonomic shortcut for `start({ intent: "sign-in", ... })`. Returns
2779
+ * the platform-hosted redirect URL the caller should navigate to;
2780
+ * stateless on its own — pair with `exchange()` on your callback
2781
+ * route to mint the session.
2782
+ */
2783
+ async signIn(input) {
2784
+ return this.start({ intent: "sign-in", ...input });
2785
+ }
2786
+ /**
2787
+ * Ergonomic shortcut for `start({ intent: "sign-up", ... })`. Same
2788
+ * shape as `signIn`; only the platform-hosted page that opens differs.
2789
+ */
2790
+ async signUp(input) {
2791
+ return this.start({ intent: "sign-up", ...input });
2792
+ }
2793
+ /**
2794
+ * Alias for `requestPasswordReset`. Always resolves `{ ok: true }`
2795
+ * (the server doesn't reveal whether the email matched an account).
2796
+ */
2797
+ async forgotPassword(input) {
2798
+ return this.requestPasswordReset(input);
2799
+ }
2800
+ async exchange(input) {
2801
+ const body = tenantAuthExchangeInputSchema.parse(input);
2802
+ return this.client.request({
2803
+ method: "POST",
2804
+ path: "auth/exchange",
2805
+ body,
2806
+ responseSchema: tenantAuthExchangeResponseSchema,
2807
+ });
2808
+ }
2809
+ async me(input) {
2810
+ return this.client.request({
2811
+ method: "GET",
2812
+ path: "auth/me",
2813
+ headers: { "x-biab-session": input.sessionToken },
2814
+ responseSchema: tenantAuthMeResponseSchema,
2815
+ });
2816
+ }
2817
+ async signOut() {
2818
+ return this.client.request({
2819
+ method: "POST",
2820
+ path: "auth/sign-out",
2821
+ responseSchema: tenantAuthSignOutResponseSchema,
2822
+ });
2823
+ }
2824
+ /**
2825
+ * Trigger a password-reset email for `input.email`. Always resolves
2826
+ * with `{ ok: true }` — the server intentionally does not reveal
2827
+ * whether a matching account exists. The email (when sent) contains
2828
+ * a platform-hosted reset URL; the consumer never needs to build a
2829
+ * "set new password" page.
2830
+ *
2831
+ * Server-only by design (the API key may not be exposed to the
2832
+ * browser). The matching browser-side `requestPasswordReset()` in
2833
+ * `@businessdash/sdk/react` proxies through the auth-handler.
2834
+ */
2835
+ async requestPasswordReset(input) {
2836
+ const body = tenantAuthPasswordResetInputSchema.parse(input);
2837
+ return this.client.request({
2838
+ method: "POST",
2839
+ path: "auth/password-reset",
2840
+ body,
2841
+ responseSchema: tenantAuthPasswordResetResponseSchema,
2842
+ });
2843
+ }
2844
+ }
2845
+ //# sourceMappingURL=client.js.map