@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.
- package/README.md +991 -295
- package/assets/biab-sw.js +92 -0
- package/dist/account-core/index.d.ts +126 -0
- package/dist/account-core/index.d.ts.map +1 -0
- package/dist/account-core/index.js +191 -0
- package/dist/account-core/index.js.map +1 -0
- package/dist/account.d.ts +11 -0
- package/dist/account.d.ts.map +1 -0
- package/dist/account.js +11 -0
- package/dist/account.js.map +1 -0
- package/dist/auth-handler.d.ts +1 -1
- package/dist/auth-links.d.ts +33 -0
- package/dist/auth-links.d.ts.map +1 -0
- package/dist/auth-links.js +64 -0
- package/dist/auth-links.js.map +1 -0
- package/dist/blog-core/index.d.ts +112 -0
- package/dist/blog-core/index.d.ts.map +1 -0
- package/dist/blog-core/index.js +330 -0
- package/dist/blog-core/index.js.map +1 -0
- package/dist/blog.d.ts +11 -0
- package/dist/blog.d.ts.map +1 -0
- package/dist/blog.js +11 -0
- package/dist/blog.js.map +1 -0
- package/dist/chat-core/index.d.ts +97 -0
- package/dist/chat-core/index.d.ts.map +1 -0
- package/dist/chat-core/index.js +237 -0
- package/dist/chat-core/index.js.map +1 -0
- package/dist/chat.d.ts +11 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +11 -0
- package/dist/chat.js.map +1 -0
- package/dist/checkout-core/index.d.ts +65 -0
- package/dist/checkout-core/index.d.ts.map +1 -0
- package/dist/checkout-core/index.js +159 -0
- package/dist/checkout-core/index.js.map +1 -0
- package/dist/checkout.d.ts +10 -0
- package/dist/checkout.d.ts.map +1 -0
- package/dist/checkout.js +10 -0
- package/dist/checkout.js.map +1 -0
- package/dist/cli.js +133 -25
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +622 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +888 -1
- package/dist/client.js.map +1 -1
- package/dist/consent-core/index.cjs +107 -0
- package/dist/consent-core/index.d.ts +103 -0
- package/dist/consent-core/index.d.ts.map +1 -0
- package/dist/consent-core/index.js +157 -0
- package/dist/consent-core/index.js.map +1 -0
- package/dist/contracts.d.ts +3526 -767
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +571 -4
- package/dist/contracts.js.map +1 -1
- package/dist/data-model-schema.d.ts +1 -1
- package/dist/data-model-schema.d.ts.map +1 -1
- package/dist/element.d.ts +30 -0
- package/dist/element.d.ts.map +1 -1
- package/dist/element.js +56 -0
- package/dist/element.js.map +1 -1
- package/dist/embed-protocol.d.ts +86 -0
- package/dist/embed-protocol.d.ts.map +1 -0
- package/dist/embed-protocol.js +60 -0
- package/dist/embed-protocol.js.map +1 -0
- package/dist/framework-support.d.ts +122 -0
- package/dist/framework-support.d.ts.map +1 -0
- package/dist/framework-support.js +323 -0
- package/dist/framework-support.js.map +1 -0
- package/dist/index.cjs +1355 -24
- package/dist/legal-core/index.cjs +115 -0
- package/dist/legal-core/index.d.ts +160 -0
- package/dist/legal-core/index.d.ts.map +1 -0
- package/dist/legal-core/index.js +220 -0
- package/dist/legal-core/index.js.map +1 -0
- package/dist/marketing-core/index.d.ts +66 -0
- package/dist/marketing-core/index.d.ts.map +1 -0
- package/dist/marketing-core/index.js +154 -0
- package/dist/marketing-core/index.js.map +1 -0
- package/dist/marketing.d.ts +7 -0
- package/dist/marketing.d.ts.map +1 -0
- package/dist/marketing.js +7 -0
- package/dist/marketing.js.map +1 -0
- package/dist/mcp-adapters/index.d.ts +135 -0
- package/dist/mcp-adapters/index.d.ts.map +1 -0
- package/dist/mcp-adapters/index.js +187 -0
- package/dist/mcp-adapters/index.js.map +1 -0
- package/dist/mcp-adapters.d.ts +7 -0
- package/dist/mcp-adapters.d.ts.map +1 -0
- package/dist/mcp-adapters.js +7 -0
- package/dist/mcp-adapters.js.map +1 -0
- package/dist/model3d-core/index.cjs +95 -0
- package/dist/model3d-core/index.d.ts +167 -0
- package/dist/model3d-core/index.d.ts.map +1 -0
- package/dist/model3d-core/index.js +158 -0
- package/dist/model3d-core/index.js.map +1 -0
- package/dist/notifications-core/index.cjs +315 -0
- package/dist/notifications-core/index.d.ts +229 -0
- package/dist/notifications-core/index.d.ts.map +1 -0
- package/dist/notifications-core/index.js +445 -0
- package/dist/notifications-core/index.js.map +1 -0
- package/dist/portal-core/index.d.ts +451 -0
- package/dist/portal-core/index.d.ts.map +1 -0
- package/dist/portal-core/index.js +819 -0
- package/dist/portal-core/index.js.map +1 -0
- package/dist/portal.d.ts +7 -0
- package/dist/portal.d.ts.map +1 -0
- package/dist/portal.js +7 -0
- package/dist/portal.js.map +1 -0
- package/dist/push-core/index.cjs +171 -0
- package/dist/push-core/index.d.ts +149 -0
- package/dist/push-core/index.d.ts.map +1 -0
- package/dist/push-core/index.js +231 -0
- package/dist/push-core/index.js.map +1 -0
- package/dist/react-marketing.d.ts +1 -17
- package/dist/react-marketing.d.ts.map +1 -1
- package/dist/react-marketing.js +4 -0
- package/dist/react-marketing.js.map +1 -1
- package/dist/react.d.ts +294 -101
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +302 -246
- package/dist/react.js.map +1 -1
- package/dist/seed-core/index.d.ts +354 -0
- package/dist/seed-core/index.d.ts.map +1 -0
- package/dist/seed-core/index.js +420 -0
- package/dist/seed-core/index.js.map +1 -0
- package/dist/seed.d.ts +7 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +7 -0
- package/dist/seed.js.map +1 -0
- package/dist/seo-core/blog.d.ts +94 -0
- package/dist/seo-core/blog.d.ts.map +1 -0
- package/dist/seo-core/blog.js +149 -0
- package/dist/seo-core/blog.js.map +1 -0
- package/dist/seo-core/commerce.d.ts +64 -0
- package/dist/seo-core/commerce.d.ts.map +1 -0
- package/dist/seo-core/commerce.js +87 -0
- package/dist/seo-core/commerce.js.map +1 -0
- package/dist/seo-core/index.cjs +751 -0
- package/dist/seo-core/index.d.ts +226 -0
- package/dist/seo-core/index.d.ts.map +1 -0
- package/dist/seo-core/index.js +455 -0
- package/dist/seo-core/index.js.map +1 -0
- package/dist/seo-core/local.d.ts +181 -0
- package/dist/seo-core/local.d.ts.map +1 -0
- package/dist/seo-core/local.js +193 -0
- package/dist/seo-core/local.js.map +1 -0
- package/dist/sitemap-core/index.cjs +167 -0
- package/dist/sitemap-core/index.d.ts +155 -0
- package/dist/sitemap-core/index.d.ts.map +1 -0
- package/dist/sitemap-core/index.js +195 -0
- package/dist/sitemap-core/index.js.map +1 -0
- package/dist/social-core/index.d.ts +74 -0
- package/dist/social-core/index.d.ts.map +1 -0
- package/dist/social-core/index.js +72 -0
- package/dist/social-core/index.js.map +1 -0
- package/dist/social.d.ts +7 -0
- package/dist/social.d.ts.map +1 -0
- package/dist/social.js +7 -0
- package/dist/social.js.map +1 -0
- package/dist/solid-compat.d.ts +204 -0
- package/dist/solid-compat.d.ts.map +1 -0
- package/dist/solid-compat.js +221 -0
- package/dist/solid-compat.js.map +1 -0
- package/dist/store-core/index.d.ts +141 -0
- package/dist/store-core/index.d.ts.map +1 -0
- package/dist/store-core/index.js +277 -0
- package/dist/store-core/index.js.map +1 -0
- package/dist/store.d.ts +12 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +12 -0
- package/dist/store.js.map +1 -0
- package/dist/vanilla.d.ts +111 -0
- package/dist/vanilla.d.ts.map +1 -1
- package/dist/vanilla.js +206 -0
- package/dist/vanilla.js.map +1 -1
- package/dist/verify-core/index.d.ts +98 -0
- package/dist/verify-core/index.d.ts.map +1 -0
- package/dist/verify-core/index.js +186 -0
- package/dist/verify-core/index.js.map +1 -0
- package/dist/verify.d.ts +8 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +8 -0
- package/dist/verify.js.map +1 -0
- package/openapi.json +4516 -1311
- package/package.json +128 -11
- package/src/account-core/index.ts +309 -0
- package/src/account.ts +11 -0
- package/src/angular/headless.ts +519 -0
- package/src/angular/index.ts +3 -0
- package/src/auth-links.ts +76 -0
- package/src/blog-core/index.ts +423 -0
- package/src/blog.ts +11 -0
- package/src/chat-core/index.ts +335 -0
- package/src/chat.ts +11 -0
- package/src/checkout-core/index.ts +225 -0
- package/src/checkout.ts +10 -0
- package/src/cli.ts +184 -28
- package/src/client.ts +1097 -0
- package/src/consent-core/index.ts +213 -0
- package/src/contracts.ts +768 -11
- package/src/data-model-schema.ts +1 -1
- package/src/element.ts +102 -0
- package/src/embed-protocol.ts +100 -0
- package/src/framework-support.ts +440 -0
- package/src/legal-core/index.ts +276 -0
- package/src/marketing-core/index.ts +209 -0
- package/src/marketing.ts +7 -0
- package/src/mcp-adapters/index.ts +264 -0
- package/src/mcp-adapters.ts +7 -0
- package/src/model3d-core/index.ts +248 -0
- package/src/notifications-core/index.ts +650 -0
- package/src/portal-core/index.ts +1564 -0
- package/src/portal.ts +7 -0
- package/src/push-core/index.ts +335 -0
- package/src/qwik.tsx +602 -2
- package/src/react-marketing.tsx +5 -0
- package/src/react.tsx +434 -319
- package/src/seed-core/index.ts +735 -0
- package/src/seed.ts +7 -0
- package/src/seo-core/blog.ts +216 -0
- package/src/seo-core/commerce.ts +138 -0
- package/src/seo-core/index.ts +605 -0
- package/src/seo-core/local.ts +333 -0
- package/src/sitemap-core/index.ts +296 -0
- package/src/social-core/index.ts +103 -0
- package/src/social.ts +7 -0
- package/src/solid-compat.tsx +347 -0
- package/src/solid-surfaces.tsx +1311 -0
- package/src/solid.tsx +82 -60
- package/src/store-core/index.ts +426 -0
- package/src/store.ts +12 -0
- package/src/svelte/SocialLinks.svelte +84 -0
- package/src/svelte/headless.ts +435 -0
- package/src/svelte/index.ts +9 -0
- package/src/vanilla.ts +367 -0
- package/src/verify-core/index.ts +288 -0
- package/src/verify.ts +8 -0
- package/src/vue/headless.ts +527 -0
- 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
|
+
}
|