@businessdash/sdk 0.9.60 → 0.9.80

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/README.md +991 -295
  2. package/assets/biab-sw.js +92 -0
  3. package/dist/account-core/index.d.ts +126 -0
  4. package/dist/account-core/index.d.ts.map +1 -0
  5. package/dist/account-core/index.js +191 -0
  6. package/dist/account-core/index.js.map +1 -0
  7. package/dist/account.d.ts +11 -0
  8. package/dist/account.d.ts.map +1 -0
  9. package/dist/account.js +11 -0
  10. package/dist/account.js.map +1 -0
  11. package/dist/auth-handler.d.ts +1 -1
  12. package/dist/auth-links.d.ts +33 -0
  13. package/dist/auth-links.d.ts.map +1 -0
  14. package/dist/auth-links.js +64 -0
  15. package/dist/auth-links.js.map +1 -0
  16. package/dist/blog-core/index.d.ts +112 -0
  17. package/dist/blog-core/index.d.ts.map +1 -0
  18. package/dist/blog-core/index.js +330 -0
  19. package/dist/blog-core/index.js.map +1 -0
  20. package/dist/blog.d.ts +11 -0
  21. package/dist/blog.d.ts.map +1 -0
  22. package/dist/blog.js +11 -0
  23. package/dist/blog.js.map +1 -0
  24. package/dist/chat-core/index.d.ts +97 -0
  25. package/dist/chat-core/index.d.ts.map +1 -0
  26. package/dist/chat-core/index.js +237 -0
  27. package/dist/chat-core/index.js.map +1 -0
  28. package/dist/chat.d.ts +11 -0
  29. package/dist/chat.d.ts.map +1 -0
  30. package/dist/chat.js +11 -0
  31. package/dist/chat.js.map +1 -0
  32. package/dist/checkout-core/index.d.ts +65 -0
  33. package/dist/checkout-core/index.d.ts.map +1 -0
  34. package/dist/checkout-core/index.js +159 -0
  35. package/dist/checkout-core/index.js.map +1 -0
  36. package/dist/checkout.d.ts +10 -0
  37. package/dist/checkout.d.ts.map +1 -0
  38. package/dist/checkout.js +10 -0
  39. package/dist/checkout.js.map +1 -0
  40. package/dist/cli.js +133 -25
  41. package/dist/cli.js.map +1 -1
  42. package/dist/client.d.ts +622 -1
  43. package/dist/client.d.ts.map +1 -1
  44. package/dist/client.js +888 -1
  45. package/dist/client.js.map +1 -1
  46. package/dist/consent-core/index.cjs +107 -0
  47. package/dist/consent-core/index.d.ts +103 -0
  48. package/dist/consent-core/index.d.ts.map +1 -0
  49. package/dist/consent-core/index.js +157 -0
  50. package/dist/consent-core/index.js.map +1 -0
  51. package/dist/contracts.d.ts +3526 -767
  52. package/dist/contracts.d.ts.map +1 -1
  53. package/dist/contracts.js +571 -4
  54. package/dist/contracts.js.map +1 -1
  55. package/dist/data-model-schema.d.ts +1 -1
  56. package/dist/data-model-schema.d.ts.map +1 -1
  57. package/dist/element.d.ts +30 -0
  58. package/dist/element.d.ts.map +1 -1
  59. package/dist/element.js +56 -0
  60. package/dist/element.js.map +1 -1
  61. package/dist/embed-protocol.d.ts +86 -0
  62. package/dist/embed-protocol.d.ts.map +1 -0
  63. package/dist/embed-protocol.js +60 -0
  64. package/dist/embed-protocol.js.map +1 -0
  65. package/dist/framework-support.d.ts +122 -0
  66. package/dist/framework-support.d.ts.map +1 -0
  67. package/dist/framework-support.js +323 -0
  68. package/dist/framework-support.js.map +1 -0
  69. package/dist/index.cjs +1355 -24
  70. package/dist/legal-core/index.cjs +115 -0
  71. package/dist/legal-core/index.d.ts +160 -0
  72. package/dist/legal-core/index.d.ts.map +1 -0
  73. package/dist/legal-core/index.js +220 -0
  74. package/dist/legal-core/index.js.map +1 -0
  75. package/dist/marketing-core/index.d.ts +66 -0
  76. package/dist/marketing-core/index.d.ts.map +1 -0
  77. package/dist/marketing-core/index.js +154 -0
  78. package/dist/marketing-core/index.js.map +1 -0
  79. package/dist/marketing.d.ts +7 -0
  80. package/dist/marketing.d.ts.map +1 -0
  81. package/dist/marketing.js +7 -0
  82. package/dist/marketing.js.map +1 -0
  83. package/dist/mcp-adapters/index.d.ts +135 -0
  84. package/dist/mcp-adapters/index.d.ts.map +1 -0
  85. package/dist/mcp-adapters/index.js +187 -0
  86. package/dist/mcp-adapters/index.js.map +1 -0
  87. package/dist/mcp-adapters.d.ts +7 -0
  88. package/dist/mcp-adapters.d.ts.map +1 -0
  89. package/dist/mcp-adapters.js +7 -0
  90. package/dist/mcp-adapters.js.map +1 -0
  91. package/dist/model3d-core/index.cjs +95 -0
  92. package/dist/model3d-core/index.d.ts +167 -0
  93. package/dist/model3d-core/index.d.ts.map +1 -0
  94. package/dist/model3d-core/index.js +158 -0
  95. package/dist/model3d-core/index.js.map +1 -0
  96. package/dist/notifications-core/index.cjs +315 -0
  97. package/dist/notifications-core/index.d.ts +229 -0
  98. package/dist/notifications-core/index.d.ts.map +1 -0
  99. package/dist/notifications-core/index.js +445 -0
  100. package/dist/notifications-core/index.js.map +1 -0
  101. package/dist/portal-core/index.d.ts +451 -0
  102. package/dist/portal-core/index.d.ts.map +1 -0
  103. package/dist/portal-core/index.js +819 -0
  104. package/dist/portal-core/index.js.map +1 -0
  105. package/dist/portal.d.ts +7 -0
  106. package/dist/portal.d.ts.map +1 -0
  107. package/dist/portal.js +7 -0
  108. package/dist/portal.js.map +1 -0
  109. package/dist/push-core/index.cjs +171 -0
  110. package/dist/push-core/index.d.ts +149 -0
  111. package/dist/push-core/index.d.ts.map +1 -0
  112. package/dist/push-core/index.js +231 -0
  113. package/dist/push-core/index.js.map +1 -0
  114. package/dist/react-marketing.d.ts +1 -17
  115. package/dist/react-marketing.d.ts.map +1 -1
  116. package/dist/react-marketing.js +4 -0
  117. package/dist/react-marketing.js.map +1 -1
  118. package/dist/react.d.ts +294 -101
  119. package/dist/react.d.ts.map +1 -1
  120. package/dist/react.js +302 -246
  121. package/dist/react.js.map +1 -1
  122. package/dist/seed-core/index.d.ts +354 -0
  123. package/dist/seed-core/index.d.ts.map +1 -0
  124. package/dist/seed-core/index.js +420 -0
  125. package/dist/seed-core/index.js.map +1 -0
  126. package/dist/seed.d.ts +7 -0
  127. package/dist/seed.d.ts.map +1 -0
  128. package/dist/seed.js +7 -0
  129. package/dist/seed.js.map +1 -0
  130. package/dist/seo-core/blog.d.ts +94 -0
  131. package/dist/seo-core/blog.d.ts.map +1 -0
  132. package/dist/seo-core/blog.js +149 -0
  133. package/dist/seo-core/blog.js.map +1 -0
  134. package/dist/seo-core/commerce.d.ts +64 -0
  135. package/dist/seo-core/commerce.d.ts.map +1 -0
  136. package/dist/seo-core/commerce.js +87 -0
  137. package/dist/seo-core/commerce.js.map +1 -0
  138. package/dist/seo-core/index.cjs +751 -0
  139. package/dist/seo-core/index.d.ts +226 -0
  140. package/dist/seo-core/index.d.ts.map +1 -0
  141. package/dist/seo-core/index.js +455 -0
  142. package/dist/seo-core/index.js.map +1 -0
  143. package/dist/seo-core/local.d.ts +181 -0
  144. package/dist/seo-core/local.d.ts.map +1 -0
  145. package/dist/seo-core/local.js +193 -0
  146. package/dist/seo-core/local.js.map +1 -0
  147. package/dist/sitemap-core/index.cjs +167 -0
  148. package/dist/sitemap-core/index.d.ts +155 -0
  149. package/dist/sitemap-core/index.d.ts.map +1 -0
  150. package/dist/sitemap-core/index.js +195 -0
  151. package/dist/sitemap-core/index.js.map +1 -0
  152. package/dist/social-core/index.d.ts +74 -0
  153. package/dist/social-core/index.d.ts.map +1 -0
  154. package/dist/social-core/index.js +72 -0
  155. package/dist/social-core/index.js.map +1 -0
  156. package/dist/social.d.ts +7 -0
  157. package/dist/social.d.ts.map +1 -0
  158. package/dist/social.js +7 -0
  159. package/dist/social.js.map +1 -0
  160. package/dist/solid-compat.d.ts +204 -0
  161. package/dist/solid-compat.d.ts.map +1 -0
  162. package/dist/solid-compat.js +221 -0
  163. package/dist/solid-compat.js.map +1 -0
  164. package/dist/store-core/index.d.ts +141 -0
  165. package/dist/store-core/index.d.ts.map +1 -0
  166. package/dist/store-core/index.js +277 -0
  167. package/dist/store-core/index.js.map +1 -0
  168. package/dist/store.d.ts +12 -0
  169. package/dist/store.d.ts.map +1 -0
  170. package/dist/store.js +12 -0
  171. package/dist/store.js.map +1 -0
  172. package/dist/vanilla.d.ts +111 -0
  173. package/dist/vanilla.d.ts.map +1 -1
  174. package/dist/vanilla.js +206 -0
  175. package/dist/vanilla.js.map +1 -1
  176. package/dist/verify-core/index.d.ts +98 -0
  177. package/dist/verify-core/index.d.ts.map +1 -0
  178. package/dist/verify-core/index.js +186 -0
  179. package/dist/verify-core/index.js.map +1 -0
  180. package/dist/verify.d.ts +8 -0
  181. package/dist/verify.d.ts.map +1 -0
  182. package/dist/verify.js +8 -0
  183. package/dist/verify.js.map +1 -0
  184. package/openapi.json +4516 -1311
  185. package/package.json +128 -11
  186. package/src/account-core/index.ts +309 -0
  187. package/src/account.ts +11 -0
  188. package/src/angular/headless.ts +519 -0
  189. package/src/angular/index.ts +3 -0
  190. package/src/auth-links.ts +76 -0
  191. package/src/blog-core/index.ts +423 -0
  192. package/src/blog.ts +11 -0
  193. package/src/chat-core/index.ts +335 -0
  194. package/src/chat.ts +11 -0
  195. package/src/checkout-core/index.ts +225 -0
  196. package/src/checkout.ts +10 -0
  197. package/src/cli.ts +184 -28
  198. package/src/client.ts +1097 -0
  199. package/src/consent-core/index.ts +213 -0
  200. package/src/contracts.ts +768 -11
  201. package/src/data-model-schema.ts +1 -1
  202. package/src/element.ts +102 -0
  203. package/src/embed-protocol.ts +100 -0
  204. package/src/framework-support.ts +440 -0
  205. package/src/legal-core/index.ts +276 -0
  206. package/src/marketing-core/index.ts +209 -0
  207. package/src/marketing.ts +7 -0
  208. package/src/mcp-adapters/index.ts +264 -0
  209. package/src/mcp-adapters.ts +7 -0
  210. package/src/model3d-core/index.ts +248 -0
  211. package/src/notifications-core/index.ts +650 -0
  212. package/src/portal-core/index.ts +1564 -0
  213. package/src/portal.ts +7 -0
  214. package/src/push-core/index.ts +335 -0
  215. package/src/qwik.tsx +602 -2
  216. package/src/react-marketing.tsx +5 -0
  217. package/src/react.tsx +434 -319
  218. package/src/seed-core/index.ts +735 -0
  219. package/src/seed.ts +7 -0
  220. package/src/seo-core/blog.ts +216 -0
  221. package/src/seo-core/commerce.ts +138 -0
  222. package/src/seo-core/index.ts +605 -0
  223. package/src/seo-core/local.ts +333 -0
  224. package/src/sitemap-core/index.ts +296 -0
  225. package/src/social-core/index.ts +103 -0
  226. package/src/social.ts +7 -0
  227. package/src/solid-compat.tsx +347 -0
  228. package/src/solid-surfaces.tsx +1311 -0
  229. package/src/solid.tsx +82 -60
  230. package/src/store-core/index.ts +426 -0
  231. package/src/store.ts +12 -0
  232. package/src/svelte/SocialLinks.svelte +84 -0
  233. package/src/svelte/headless.ts +435 -0
  234. package/src/svelte/index.ts +9 -0
  235. package/src/vanilla.ts +367 -0
  236. package/src/verify-core/index.ts +288 -0
  237. package/src/verify.ts +8 -0
  238. package/src/vue/headless.ts +527 -0
  239. package/src/vue/index.ts +3 -0
@@ -0,0 +1,213 @@
1
+ /**
2
+ * `@businessdash/sdk/consent` — tracking consent, per org.
3
+ *
4
+ * ## The rule that shapes everything here
5
+ *
6
+ * Consent is given to a **controller**, and each org is a separate controller.
7
+ * It does not travel between them in either direction:
8
+ *
9
+ * - A visitor who refuses at org 2 has said nothing to org 1. Org 1 keeps
10
+ * collecting.
11
+ * - A visitor who consents at org 1 has NOT consented at org 2. Org 2 may not
12
+ * start collecting on the strength of it.
13
+ *
14
+ * The second half is the one that gets skipped, and it is the one with legal
15
+ * teeth: acting on another controller's consent is processing without a lawful
16
+ * basis.
17
+ *
18
+ * That is why the visitor key lives in a **first-party cookie on the org's own
19
+ * domain**, and why every call carries the org's own API key. Two sites cannot
20
+ * see each other's cookies, so the isolation is enforced by the browser rather
21
+ * than by us remembering to scope a query.
22
+ *
23
+ * ## Gate analytics on it
24
+ *
25
+ * ```ts
26
+ * const consent = await loadConsent({ client });
27
+ * if (consent.allows('analytics')) {
28
+ * initBiabAnalytics({ siteId, baseUrl, apiKey });
29
+ * }
30
+ * if (consent.mustAsk) showYourBanner(consent);
31
+ * ```
32
+ *
33
+ * `initBiabAnalytics` is deliberately NOT made to call this itself. A gate that
34
+ * fires implicitly is one nobody can see in review, and the decision of what
35
+ * happens before consent — render nothing, render a placeholder, collect
36
+ * essential-only — belongs to the site, not to us.
37
+ *
38
+ * @module
39
+ */
40
+
41
+ export type ConsentCategory = "analytics" | "marketing" | "personalisation";
42
+
43
+ /** Why consent resolved the way it did. Useful in an org's audit view. */
44
+ export type ConsentReason =
45
+ | "browser_signal"
46
+ | "explicit"
47
+ | "stale_notice"
48
+ | "default_allow"
49
+ | "default_deny";
50
+
51
+ export type ConsentState = {
52
+ /** Categories this org uses at all. Render one banner row per entry. */
53
+ categories: ConsentCategory[];
54
+ noticeText: string | null;
55
+ noticeVersion: number;
56
+ /** What may be used right now. */
57
+ allowed: ConsentCategory[];
58
+ /** True when the visitor has not answered and a banner should show. */
59
+ mustAsk: boolean;
60
+ reason: ConsentReason;
61
+ /** `allowed.includes(category)`, spelled as a question. */
62
+ allows(category: ConsentCategory): boolean;
63
+ };
64
+
65
+ /** The subset of the SDK client this needs. */
66
+ export type ConsentClientLike = {
67
+ request(input: {
68
+ method?: string;
69
+ path: string;
70
+ body?: unknown;
71
+ headers?: Record<string, string>;
72
+ responseSchema: unknown;
73
+ }): Promise<unknown>;
74
+ };
75
+
76
+ const VISITOR_COOKIE = "biab_visitor";
77
+ const VISITOR_HEADER = "x-biab-visitor";
78
+
79
+ /**
80
+ * The visitor's id for THIS site, minted on first need.
81
+ *
82
+ * A first-party cookie on the org's own domain, which is what makes the
83
+ * per-org isolation structural: the same person visiting two orgs gets two
84
+ * unrelated keys, because two domains cannot read each other's cookies. There
85
+ * is no cross-site identifier here and deliberately no way to add one.
86
+ *
87
+ * Returns null on the server, where there is no visitor to identify.
88
+ */
89
+ export function visitorKey(): string | null {
90
+ if (typeof document === "undefined") return null;
91
+
92
+ const found = document.cookie
93
+ .split(";")
94
+ .map((part) => part.trim())
95
+ .find((part) => part.startsWith(`${VISITOR_COOKIE}=`));
96
+ if (found) return decodeURIComponent(found.slice(VISITOR_COOKIE.length + 1));
97
+
98
+ const key =
99
+ typeof crypto !== "undefined" && "randomUUID" in crypto
100
+ ? crypto.randomUUID()
101
+ : `v${Date.now()}${Math.floor(Math.random() * 1e9)}`;
102
+
103
+ // SameSite=Lax and no Secure flag would leak on http; Secure is set unless
104
+ // we are plainly on localhost, where it would stop the cookie working at all.
105
+ const secure = location.protocol === "https:" ? "; Secure" : "";
106
+ // biome-ignore lint/suspicious/noDocumentCookie: the suggested CookieStore
107
+ // API has no Safari or Firefox support. A consent banner has to work in
108
+ // every browser a visitor might arrive in — that is the entire point of it —
109
+ // so the universally supported API is the correct one here.
110
+ document.cookie = `${VISITOR_COOKIE}=${encodeURIComponent(key)}; Path=/; Max-Age=31536000; SameSite=Lax${secure}`;
111
+ return key;
112
+ }
113
+
114
+ /** True when the browser is signalling a global opt-out (DNT or GPC). */
115
+ export function browserOptOut(): boolean {
116
+ if (typeof navigator === "undefined") return false;
117
+ const nav = navigator as Navigator & {
118
+ doNotTrack?: string;
119
+ globalPrivacyControl?: boolean;
120
+ };
121
+ if (nav.globalPrivacyControl === true) return true;
122
+ const dnt =
123
+ nav.doNotTrack ??
124
+ (typeof window === "undefined"
125
+ ? undefined
126
+ : (window as Window & { doNotTrack?: string }).doNotTrack);
127
+ return dnt === "1" || dnt === "yes";
128
+ }
129
+
130
+ function toState(raw: {
131
+ categories?: string[];
132
+ noticeText?: string | null;
133
+ noticeVersion?: number;
134
+ allowed?: string[];
135
+ mustAsk?: boolean;
136
+ reason?: string;
137
+ }): ConsentState {
138
+ const categories = (raw.categories ?? []) as ConsentCategory[];
139
+ const allowed = (raw.allowed ?? []) as ConsentCategory[];
140
+ return {
141
+ categories,
142
+ noticeText: raw.noticeText ?? null,
143
+ noticeVersion: raw.noticeVersion ?? 1,
144
+ allowed,
145
+ mustAsk: raw.mustAsk ?? true,
146
+ reason: (raw.reason ?? "default_deny") as ConsentReason,
147
+ allows: (category) => allowed.includes(category),
148
+ };
149
+ }
150
+
151
+ /**
152
+ * What this visitor currently allows, at this org.
153
+ *
154
+ * On any failure this returns a **deny-everything** state rather than throwing.
155
+ * A consent gate that fails open collects data nobody agreed to; one that fails
156
+ * closed loses some analytics. Only one of those is a compliance incident.
157
+ */
158
+ export async function loadConsent(options: {
159
+ client: ConsentClientLike;
160
+ }): Promise<ConsentState> {
161
+ const key = visitorKey();
162
+ try {
163
+ const raw = (await options.client.request({
164
+ path: "consent",
165
+ ...(key ? { headers: { [VISITOR_HEADER]: key } } : {}),
166
+ responseSchema: { parse: (v: unknown) => v },
167
+ })) as Parameters<typeof toState>[0];
168
+ return toState(raw);
169
+ } catch {
170
+ return {
171
+ categories: [],
172
+ noticeText: null,
173
+ noticeVersion: 1,
174
+ allowed: [],
175
+ mustAsk: false,
176
+ reason: "default_deny",
177
+ allows: () => false,
178
+ };
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Record the visitor's answer.
184
+ *
185
+ * `granted: []` is a real answer — "asked, refused everything" — and is stored
186
+ * as one, so the banner does not reappear on the next page.
187
+ */
188
+ export async function recordConsent(options: {
189
+ client: ConsentClientLike;
190
+ granted: ConsentCategory[];
191
+ noticeVersion: number;
192
+ source?: "banner" | "settings";
193
+ }): Promise<{ ok: boolean }> {
194
+ const key = visitorKey();
195
+ if (!key) return { ok: false };
196
+
197
+ try {
198
+ await options.client.request({
199
+ method: "POST",
200
+ path: "consent",
201
+ body: {
202
+ granted: options.granted,
203
+ noticeVersion: options.noticeVersion,
204
+ source: options.source ?? "banner",
205
+ },
206
+ headers: { [VISITOR_HEADER]: key },
207
+ responseSchema: { parse: (v: unknown) => v },
208
+ });
209
+ return { ok: true };
210
+ } catch {
211
+ return { ok: false };
212
+ }
213
+ }