@buildbase/sdk 0.0.60 → 0.0.62

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 (41) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +95 -0
  3. package/dist/css/styles.css +1 -1
  4. package/dist/index.cjs +1 -1
  5. package/dist/index.d.ts +84 -0
  6. package/dist/index.mjs +1 -1
  7. package/dist/mcp/index.cjs +1 -1
  8. package/dist/mcp/index.d.ts +23 -0
  9. package/dist/mcp/index.mjs +1 -1
  10. package/dist/react/chunks/SettingsDialog-CKwldz7y.mjs +2 -0
  11. package/dist/react/chunks/{SettingsDialog-DzcwBYsg.cjs → SettingsDialog-D4FCwpN7.cjs} +1 -1
  12. package/dist/react/chunks/SubscriptionDialog-DJmrU0r1.cjs +2 -0
  13. package/dist/react/chunks/SubscriptionDialog-DoWnS_JD.mjs +2 -0
  14. package/dist/react/chunks/react-Be1VMiVO.mjs +2 -0
  15. package/dist/react/chunks/react-ClieHJPH.cjs +2 -0
  16. package/dist/react/index.cjs +1 -1
  17. package/dist/react/index.d.ts +186 -26
  18. package/dist/react/index.mjs +1 -1
  19. package/dist/tracking/index.cjs +2 -0
  20. package/dist/tracking/index.d.ts +657 -0
  21. package/dist/tracking/index.mjs +2 -0
  22. package/package.json +12 -2
  23. package/dist/react/chunks/SettingsDialog-4yk6t4tT.mjs +0 -2
  24. package/dist/react/chunks/SubscriptionDialog-COw79gAi.cjs +0 -2
  25. package/dist/react/chunks/SubscriptionDialog-DLM_WA84.mjs +0 -2
  26. package/dist/react/chunks/react-D6q1fjoE.cjs +0 -2
  27. package/dist/react/chunks/react-DX2YGh4O.mjs +0 -2
  28. /package/dist/react/chunks/{ar-DHxjPtfZ.mjs → ar-CHTRXlXt.mjs} +0 -0
  29. /package/dist/react/chunks/{ar-D7uaSur2.cjs → ar-DPJVNHJV.cjs} +0 -0
  30. /package/dist/react/chunks/{de-D_xfGk1N.mjs → de-CdPeKWZ2.mjs} +0 -0
  31. /package/dist/react/chunks/{de-CBzx6NJq.cjs → de-DPd09yf-.cjs} +0 -0
  32. /package/dist/react/chunks/{es-D4-Dv2t5.mjs → es-BWGnjJKJ.mjs} +0 -0
  33. /package/dist/react/chunks/{es-WVRwuj-e.cjs → es-WdOYGt6G.cjs} +0 -0
  34. /package/dist/react/chunks/{fr-DurP7qkZ.mjs → fr-Big5DFPB.mjs} +0 -0
  35. /package/dist/react/chunks/{fr-CEpK52jT.cjs → fr-LIbCvYeh.cjs} +0 -0
  36. /package/dist/react/chunks/{hi-CVqVRSoy.cjs → hi-BoCY3pFe.cjs} +0 -0
  37. /package/dist/react/chunks/{hi-7roM83DB.mjs → hi-jArNRknR.mjs} +0 -0
  38. /package/dist/react/chunks/{ja-DT8x4SW1.mjs → ja-Cwdz0hCu.mjs} +0 -0
  39. /package/dist/react/chunks/{ja-BrDXec_v.cjs → ja-E0jqCiVq.cjs} +0 -0
  40. /package/dist/react/chunks/{zh-CH1Z55Je.mjs → zh-glRKcdia.mjs} +0 -0
  41. /package/dist/react/chunks/{zh-CdCzcD53.cjs → zh-tU-AqFYI.cjs} +0 -0
@@ -0,0 +1,657 @@
1
+ import React from 'react';
2
+
3
+ /**
4
+ * Remembering where a visitor came from, across a whole product.
5
+ *
6
+ * A person clicks an ad, lands on `abc.com`, reads for a while, then signs up
7
+ * on `app.abc.com`. The click id was in the URL of the first page and is long
8
+ * gone by the second. Unless it was stored somewhere both subdomains can read,
9
+ * the sign-up looks like it came from nowhere and the ad that paid for it gets
10
+ * no credit.
11
+ *
12
+ * Google solves this by writing `_gcl_aw` on the registrable domain, so every
13
+ * subdomain shares it. This does the same thing for the ids we carry into our
14
+ * own events and, later, into a server-side conversion upload.
15
+ *
16
+ * ── Why the domain is probed rather than computed ───────────────────────────
17
+ * "The registrable domain" cannot be derived by counting labels: `abc.co.uk`
18
+ * has three, `abc.com` has two, and a hard-coded rule gets one of them wrong.
19
+ * The Public Suffix List is the correct answer and is far too large to ship in
20
+ * a tracking bundle.
21
+ *
22
+ * So we ask the browser, which already knows: try to set a cookie on the
23
+ * broadest candidate, read it back, and keep the first that sticks. A browser
24
+ * refuses `domain=.co.uk` because it is a public suffix, so that candidate
25
+ * fails and `abc.co.uk` wins - the right answer, from the only party that has
26
+ * the list.
27
+ */
28
+ /**
29
+ * The URL parameters worth keeping.
30
+ *
31
+ * Click ids first - they are what an ad network matches a conversion against,
32
+ * and they are the reason this file exists. UTMs are reporting rather than
33
+ * billing, but they answer "which campaign" and cost nothing to carry.
34
+ *
35
+ * Click ids are handled byte-exactly: they are opaque, mixed-case, base64-ish
36
+ * strings issued by the network, so lowercasing one silently corrupts it and
37
+ * the network reports zero conversions with nothing in the logs to explain it.
38
+ */
39
+ declare const ATTRIBUTION_PARAMS: readonly ["gclid", "gbraid", "wbraid", "dclid", "fbclid", "msclkid", "li_fat_id", "twclid", "ttclid", "rdt_cid", "epik", "irclickid", "utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content", "utm_id"];
40
+ type AttributionParam = (typeof ATTRIBUTION_PARAMS)[number];
41
+ type Attribution = Partial<Record<AttributionParam, string>>;
42
+ /**
43
+ * Capture what this page arrived with, and return everything known so far.
44
+ *
45
+ * First touch wins for any given parameter. A visitor who arrives on an ad,
46
+ * comes back later through a newsletter, and only then signs up should still
47
+ * credit the ad - overwriting on the second visit would quietly reassign every
48
+ * conversion to whatever the last touch happened to be. New parameters are
49
+ * still added, so a later campaign tag enriches rather than replaces.
50
+ */
51
+ declare function captureAttribution(): Attribution;
52
+ /** What is stored, without touching the URL. */
53
+ declare function getAttribution(): Attribution;
54
+
55
+ interface PendingCheckout {
56
+ eventId: string;
57
+ planVersionId?: string;
58
+ /**
59
+ * The workspace buying. The return from Stripe fires `purchase` (or
60
+ * `checkout_abandoned`) as soon as the tags are ready, which is before the
61
+ * page has loaded its workspace - so the account the event belongs to has to
62
+ * travel with the checkout rather than be read on arrival.
63
+ */
64
+ workspaceId?: string;
65
+ /** Set instead of a plan when what was bought is a credit pack. */
66
+ creditPackageId?: string;
67
+ billingInterval?: string;
68
+ currency?: string;
69
+ /**
70
+ * The Stripe session this checkout opened. Stripe puts the same id in the
71
+ * return URL, which is how a return is recognised without the app having
72
+ * to say so.
73
+ */
74
+ sessionId?: string;
75
+ /** What is charged today, in major units - what every ad platform expects. */
76
+ value?: number;
77
+ /** A card-required trial comes back through Stripe too, and is not a sale. */
78
+ kind?: 'purchase' | 'trial_started';
79
+ /** Epoch ms, so an entry that outlived its usefulness can be ignored. */
80
+ startedAt: number;
81
+ }
82
+
83
+ /**
84
+ * The provider allowlist, as the SDK sees it.
85
+ *
86
+ * ── Why this is a copy ──────────────────────────────────────────────────────
87
+ * The server has the same table. It is duplicated here rather than fetched
88
+ * because this is the last check before a string becomes a `<script>` tag in a
89
+ * customer's application, and a check that arrives over the network from the
90
+ * thing being checked is not a check. The server is one deploy; a published
91
+ * SDK is pinned in a customer's lockfile and may be older or newer than it.
92
+ * Whichever side is stricter should win, and neither should be able to talk
93
+ * the other into loading something.
94
+ *
95
+ * Kept in step by `tag-providers.parity.test.ts`, which fails when this drifts
96
+ * from the platform's copy in a way that matters.
97
+ *
98
+ * Every rule from the server-side file holds here: patterns anchored at both
99
+ * ends, character classes only, an id is never a URL, and `scriptOrigin` comes
100
+ * from this table and is never built from anything a tenant typed.
101
+ */
102
+ type TagConsentCategory = 'analytics' | 'marketing';
103
+ interface TagProviderSpec {
104
+ /** Display name, used in the console and in generated disclosure text. */
105
+ title: string;
106
+ /** The legal entity, for the tenant's own privacy notice. */
107
+ vendor: string;
108
+ /** Link for the tenant's disclosure. */
109
+ privacyUrl: string;
110
+ /** What the console calls the id field, in the vendor's own words. */
111
+ idLabel: string;
112
+ /**
113
+ * The only shape we accept. Anchored, character classes only.
114
+ * Stored as a string rather than a RegExp so it crosses the wire to the
115
+ * console form and the SDK unchanged - one pattern, three validators.
116
+ */
117
+ idPattern: string;
118
+ /** A real-shaped example. Doubles as the console placeholder. */
119
+ idExample: string;
120
+ /** Where the script is fetched from. Never built from tenant input. */
121
+ scriptOrigin: string;
122
+ defaultConsentCategory: TagConsentCategory;
123
+ /**
124
+ * Whether the vendor honours Google Consent Mode v2. Those that do can load
125
+ * with all four signals denied and start measuring when consent arrives;
126
+ * those that do not must not be loaded at all beforehand.
127
+ */
128
+ supportsConsentMode: boolean;
129
+ /**
130
+ * True when the vendor has no fixed conversion event names and instead uses
131
+ * per-conversion ids the tenant creates in the ad platform. X, LinkedIn and
132
+ * Google Ads all work this way, so the console must collect an id per
133
+ * canonical event or their conversions simply never fire.
134
+ */
135
+ needsConversionIds: boolean;
136
+ /**
137
+ * Whether the vendor's own API accepts more than one id on a page. Mirrors
138
+ * the platform's copy; see the note there.
139
+ */
140
+ multiInstance?: boolean;
141
+ /**
142
+ * Closed set of regional hosts, when the vendor has them. The console shows
143
+ * a picker; anything not in this list is rejected.
144
+ */
145
+ hosts?: readonly string[];
146
+ }
147
+ declare const TAG_PROVIDERS: {
148
+ readonly gtm: {
149
+ readonly title: "Google Tag Manager";
150
+ readonly vendor: "Google LLC";
151
+ readonly privacyUrl: "https://policies.google.com/privacy";
152
+ readonly idLabel: "Container ID";
153
+ readonly idPattern: "^GTM-[A-Z0-9]{4,10}$";
154
+ readonly idExample: "GTM-ABC1234";
155
+ readonly scriptOrigin: "https://www.googletagmanager.com";
156
+ readonly defaultConsentCategory: "analytics";
157
+ readonly supportsConsentMode: true;
158
+ readonly needsConversionIds: false;
159
+ };
160
+ readonly ga4: {
161
+ readonly title: "Google Analytics 4";
162
+ readonly vendor: "Google LLC";
163
+ readonly privacyUrl: "https://policies.google.com/privacy";
164
+ readonly idLabel: "Measurement ID";
165
+ readonly idPattern: "^G-[A-Z0-9]{8,12}$";
166
+ readonly idExample: "G-ABCD123456";
167
+ readonly scriptOrigin: "https://www.googletagmanager.com";
168
+ readonly defaultConsentCategory: "analytics";
169
+ readonly supportsConsentMode: true;
170
+ readonly needsConversionIds: false;
171
+ };
172
+ readonly google_ads: {
173
+ readonly title: "Google Ads";
174
+ readonly vendor: "Google LLC";
175
+ readonly privacyUrl: "https://policies.google.com/privacy";
176
+ readonly idLabel: "Conversion ID";
177
+ readonly idPattern: "^AW-[0-9]{9,12}$";
178
+ readonly idExample: "AW-123456789";
179
+ readonly scriptOrigin: "https://www.googletagmanager.com";
180
+ readonly defaultConsentCategory: "marketing";
181
+ readonly supportsConsentMode: true;
182
+ readonly needsConversionIds: true;
183
+ };
184
+ readonly meta_pixel: {
185
+ readonly title: "Meta Pixel";
186
+ readonly vendor: "Meta Platforms Ireland Limited";
187
+ readonly privacyUrl: "https://www.facebook.com/privacy/policy";
188
+ readonly idLabel: "Pixel ID";
189
+ readonly idPattern: "^[0-9]{15,16}$";
190
+ readonly idExample: "123456789012345";
191
+ readonly scriptOrigin: "https://connect.facebook.net";
192
+ readonly defaultConsentCategory: "marketing";
193
+ readonly supportsConsentMode: false;
194
+ readonly needsConversionIds: false;
195
+ readonly multiInstance: true;
196
+ };
197
+ readonly x_pixel: {
198
+ readonly title: "X (Twitter) Pixel";
199
+ readonly vendor: "X Corp.";
200
+ readonly privacyUrl: "https://x.com/en/privacy";
201
+ readonly idLabel: "Pixel ID";
202
+ readonly idPattern: "^o[a-z0-9]{4,6}$";
203
+ readonly idExample: "o1a2b3";
204
+ readonly scriptOrigin: "https://static.ads-twitter.com";
205
+ readonly defaultConsentCategory: "marketing";
206
+ readonly supportsConsentMode: false;
207
+ readonly needsConversionIds: true;
208
+ readonly multiInstance: true;
209
+ };
210
+ readonly linkedin: {
211
+ readonly title: "LinkedIn Insight Tag";
212
+ readonly vendor: "LinkedIn Ireland Unlimited Company";
213
+ readonly privacyUrl: "https://www.linkedin.com/legal/privacy-policy";
214
+ readonly idLabel: "Partner ID";
215
+ readonly idPattern: "^[0-9]{6,9}$";
216
+ readonly idExample: "1234567";
217
+ readonly scriptOrigin: "https://snap.licdn.com";
218
+ readonly defaultConsentCategory: "marketing";
219
+ readonly supportsConsentMode: false;
220
+ readonly needsConversionIds: true;
221
+ readonly multiInstance: true;
222
+ };
223
+ readonly tiktok: {
224
+ readonly title: "TikTok Pixel";
225
+ readonly vendor: "TikTok Information Technologies UK Limited";
226
+ readonly privacyUrl: "https://www.tiktok.com/legal/privacy-policy";
227
+ readonly idLabel: "Pixel ID";
228
+ readonly idPattern: "^[A-Z0-9]{20}$";
229
+ readonly idExample: "C4A1B2C3D4E5F6G7H8I9";
230
+ readonly scriptOrigin: "https://analytics.tiktok.com";
231
+ readonly defaultConsentCategory: "marketing";
232
+ readonly supportsConsentMode: false;
233
+ readonly needsConversionIds: false;
234
+ readonly multiInstance: true;
235
+ };
236
+ readonly reddit: {
237
+ readonly title: "Reddit Pixel";
238
+ readonly vendor: "Reddit, Inc.";
239
+ readonly privacyUrl: "https://www.reddit.com/policies/privacy-policy";
240
+ readonly idLabel: "Advertiser ID";
241
+ readonly idPattern: "^t2_[a-z0-9]{4,12}$";
242
+ readonly idExample: "t2_abc123";
243
+ readonly scriptOrigin: "https://www.redditstatic.com";
244
+ readonly defaultConsentCategory: "marketing";
245
+ readonly supportsConsentMode: false;
246
+ readonly needsConversionIds: false;
247
+ readonly multiInstance: true;
248
+ };
249
+ readonly bing_uet: {
250
+ readonly title: "Microsoft Advertising (UET)";
251
+ readonly vendor: "Microsoft Corporation";
252
+ readonly privacyUrl: "https://privacy.microsoft.com/en-us/privacystatement";
253
+ readonly idLabel: "Tag ID";
254
+ readonly idPattern: "^[0-9]{7,9}$";
255
+ readonly idExample: "12345678";
256
+ readonly scriptOrigin: "https://bat.bing.com";
257
+ readonly defaultConsentCategory: "marketing";
258
+ readonly supportsConsentMode: false;
259
+ readonly needsConversionIds: false;
260
+ readonly multiInstance: true;
261
+ };
262
+ readonly clarity: {
263
+ readonly title: "Microsoft Clarity";
264
+ readonly vendor: "Microsoft Corporation";
265
+ readonly privacyUrl: "https://privacy.microsoft.com/en-us/privacystatement";
266
+ readonly idLabel: "Project ID";
267
+ readonly idPattern: "^[a-z0-9]{10}$";
268
+ readonly idExample: "ab12cd34ef";
269
+ readonly scriptOrigin: "https://www.clarity.ms";
270
+ readonly defaultConsentCategory: "analytics";
271
+ readonly supportsConsentMode: false;
272
+ readonly needsConversionIds: false;
273
+ };
274
+ readonly hotjar: {
275
+ readonly title: "Hotjar";
276
+ readonly vendor: "Hotjar Limited";
277
+ readonly privacyUrl: "https://www.hotjar.com/legal/policies/privacy/";
278
+ readonly idLabel: "Site ID";
279
+ readonly idPattern: "^[0-9]{6,9}$";
280
+ readonly idExample: "1234567";
281
+ readonly scriptOrigin: "https://static.hotjar.com";
282
+ readonly defaultConsentCategory: "analytics";
283
+ readonly supportsConsentMode: false;
284
+ readonly needsConversionIds: false;
285
+ };
286
+ readonly posthog: {
287
+ readonly title: "PostHog";
288
+ readonly vendor: "PostHog, Inc.";
289
+ readonly privacyUrl: "https://posthog.com/privacy";
290
+ readonly idLabel: "Project API key";
291
+ readonly idPattern: "^phc_[A-Za-z0-9]{32,48}$";
292
+ readonly idExample: "phc_abcdefghijklmnopqrstuvwxyz012345";
293
+ readonly scriptOrigin: "https://us-assets.i.posthog.com";
294
+ readonly defaultConsentCategory: "analytics";
295
+ readonly supportsConsentMode: false;
296
+ readonly needsConversionIds: false;
297
+ readonly hosts: readonly ["us.i.posthog.com", "eu.i.posthog.com"];
298
+ };
299
+ readonly plausible: {
300
+ readonly title: "Plausible Analytics";
301
+ readonly vendor: "Plausible Insights OÜ";
302
+ readonly privacyUrl: "https://plausible.io/privacy";
303
+ readonly idLabel: "Domain";
304
+ readonly idPattern: "^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$";
305
+ readonly idExample: "example.com";
306
+ readonly scriptOrigin: "https://plausible.io";
307
+ readonly defaultConsentCategory: "analytics";
308
+ readonly supportsConsentMode: false;
309
+ readonly needsConversionIds: false;
310
+ };
311
+ readonly ahrefs: {
312
+ readonly title: "Ahrefs Analytics";
313
+ readonly vendor: "Ahrefs Pte. Ltd.";
314
+ readonly privacyUrl: "https://ahrefs.com/privacy";
315
+ readonly idLabel: "Analytics key";
316
+ readonly idPattern: "^[A-Za-z0-9+/=]{20,64}$";
317
+ readonly idExample: "AbCdEfGhIjKlMnOpQrStUv==";
318
+ readonly scriptOrigin: "https://analytics.ahrefs.com";
319
+ readonly defaultConsentCategory: "analytics";
320
+ readonly supportsConsentMode: false;
321
+ readonly needsConversionIds: false;
322
+ };
323
+ };
324
+ type TagProviderId = keyof typeof TAG_PROVIDERS;
325
+ declare const TAG_PROVIDER_IDS: TagProviderId[];
326
+ declare function isTagProviderId(value: unknown): value is TagProviderId;
327
+ /**
328
+ * Whether `id` is a valid identifier for `provider`.
329
+ *
330
+ * Called on write in the console, again on write at the API, and a third time
331
+ * by the SDK on read. The triple-check is deliberate: the SDK validates
332
+ * because a document could have been written by a migration or edited directly
333
+ * in Mongo, and the SDK is the one that turns a string into a script tag.
334
+ */
335
+ declare function isValidProviderId(provider: TagProviderId, id: unknown): boolean;
336
+ /**
337
+ * Every origin a tenant's app may need in its own Content-Security-Policy.
338
+ *
339
+ * Published in the docs so a customer running a CSP knows what to allow. We do
340
+ * not set their policy for them - it is their app - but they should not have
341
+ * to discover this list one console error at a time.
342
+ */
343
+ declare const TAG_SCRIPT_ORIGINS: readonly string[];
344
+
345
+ /** One script, as the platform's `/v1/public/:orgId/tracking` returns it. */
346
+ interface PublicTrackingScript {
347
+ key: string;
348
+ provider: string;
349
+ providerId?: string;
350
+ host?: string;
351
+ src?: string;
352
+ dataAttributes?: Record<string, string>;
353
+ consentCategory: TagConsentCategory;
354
+ /** Keyed by event name - ours or the customer's own. */
355
+ conversionIds?: Record<string, string>;
356
+ }
357
+ /**
358
+ * One row a consent banner can render.
359
+ *
360
+ * Derived from what is actually installed, never from the org's whole library:
361
+ * a banner that names a vendor the page did not load is asking for consent
362
+ * under false pretences.
363
+ */
364
+ interface TrackingConsentEntry {
365
+ key: string;
366
+ name: string;
367
+ vendor: string;
368
+ category: TagConsentCategory;
369
+ privacyUrl?: string;
370
+ }
371
+ interface PublicTrackingConfig {
372
+ enabled: boolean;
373
+ scripts: PublicTrackingScript[];
374
+ consent: {
375
+ manifest: TrackingConsentEntry[];
376
+ };
377
+ dataLayerName?: string;
378
+ }
379
+ /** What the visitor has agreed to. `null` until they have been asked. */
380
+ interface ConsentState {
381
+ analytics: boolean;
382
+ marketing: boolean;
383
+ }
384
+ /**
385
+ * The events the SDK fires on the app's behalf.
386
+ *
387
+ * Deliberately only the moments BuildBase itself observes. A customer's own
388
+ * events go through `track()` and reach the data layer, but are never mapped
389
+ * onto a vendor's standard event - guessing that someone's `upgraded_seat` is
390
+ * Meta's `Purchase` is how a conversion report becomes fiction.
391
+ */
392
+ type CanonicalEventName = 'sign_up' | 'login' | 'trial_started' | 'checkout_started' | 'purchase' | 'subscription_created' | 'subscription_upgraded' | 'lead' | 'workspace_created';
393
+ /** Who fires a given event: the SDK, or the app. */
394
+ type EventOwnership = 'auto' | 'manual';
395
+ /** Per-call options for `track`. */
396
+ interface TrackOptions {
397
+ /**
398
+ * Use this id instead of generating one.
399
+ *
400
+ * The id is what joins a browser event to a later server-side send of the
401
+ * same occurrence, so a caller that has already written it somewhere the
402
+ * server will read it back - Stripe checkout metadata is the case this
403
+ * exists for - has to be able to fire the event with that same id.
404
+ */
405
+ eventId?: string;
406
+ }
407
+ interface TrackingEvent {
408
+ name: CanonicalEventName | string;
409
+ params: Record<string, unknown>;
410
+ /** Shared with any later server-side send, so the two can be deduplicated. */
411
+ eventId: string;
412
+ handledBy: EventOwnership;
413
+ }
414
+ interface TrackingEventsConfig {
415
+ /** Default `auto`: the SDK fires to every installed script. */
416
+ mode?: EventOwnership;
417
+ /** Per-event override, for the few a customer wants to own. */
418
+ overrides?: Partial<Record<CanonicalEventName, EventOwnership>>;
419
+ }
420
+ interface TrackingOptions {
421
+ /** Set false to fetch nothing and render nothing. */
422
+ enabled?: boolean;
423
+ /**
424
+ * `auto` (default) sets Consent Mode v2 to denied and waits for
425
+ * `setConsent`. `granted` is the app asserting it has a lawful basis
426
+ * already - documented as their assertion, not ours. `denied` keeps
427
+ * everything off.
428
+ */
429
+ consent?: 'auto' | 'granted' | 'denied';
430
+ /**
431
+ * Fire a `page_view` on SPA navigation. Turn it off when the app's GTM
432
+ * container already does it - double-counted pageviews is the single most
433
+ * likely support ticket this feature generates.
434
+ */
435
+ autoPageview?: boolean;
436
+ events?: TrackingEventsConfig;
437
+ /**
438
+ * Called for every canonical event. Returning `true` tells the SDK the app
439
+ * handled this occurrence and suppresses its own fire.
440
+ */
441
+ onEvent?: (event: TrackingEvent) => boolean | void;
442
+ /**
443
+ * Fields added to every event, including the ones the SDK fires itself.
444
+ *
445
+ * The events BuildBase owns - a signup, a checkout, a purchase - carry what
446
+ * BuildBase knows, which is never the thing a particular business builds
447
+ * its funnel on. A plan intent chosen before signup, an experiment variant,
448
+ * an industry: without somewhere to put them, the only way to get one onto
449
+ * a signup was to take the event over as `manual` and hand-write the
450
+ * fan-out to every vendor, which is a bad trade for one field.
451
+ *
452
+ * Use `setProperties` for anything not known at mount.
453
+ */
454
+ properties?: Record<string, unknown>;
455
+ /**
456
+ * The last word on what an event carries, called just before it is sent.
457
+ *
458
+ * Returns fields to merge in - which may overwrite anything already there,
459
+ * including ours. Use it for values that depend on the event: a plan on
460
+ * `checkout_started`, a source on `sign_up`. To suppress an event entirely,
461
+ * return `true` from `onEvent` instead and send nothing.
462
+ *
463
+ * It cannot rename an event: the name is what every vendor mapping is keyed
464
+ * on, and renaming one here would silently unmap it.
465
+ */
466
+ enrich?: (event: {
467
+ name: string;
468
+ params: Record<string, unknown>;
469
+ }) => Record<string, unknown> | void;
470
+ /** Log what was installed, skipped and fired. */
471
+ debug?: boolean;
472
+ }
473
+
474
+ interface TrackingContextValue {
475
+ /** What is installed, for a banner to render. Empty until config loads. */
476
+ consent: {
477
+ manifest: TrackingConsentEntry[];
478
+ state: ConsentState | null;
479
+ set: (state: ConsentState) => void;
480
+ };
481
+ /** Fire an event. Canonical names map to every vendor; others go to the data layer. */
482
+ track: (name: string, params?: Record<string, unknown>, options?: TrackOptions) => void;
483
+ /**
484
+ * Attach a user id to every vendor that supports one, and optionally the
485
+ * traits a funnel segments on - a plan, a company size, an experiment arm.
486
+ * Never an email or a name: every ad network forbids it in these fields.
487
+ */
488
+ setUser: (userId: string, traits?: Record<string, unknown>) => void;
489
+ /**
490
+ * Add fields to every event from here on. For anything not known when the
491
+ * provider mounted: an experiment assignment, a tenant tier fetched after
492
+ * login. Merged, so calling it twice keeps both, and setting a key to
493
+ * `undefined` takes it back - which is what to do on sign-out, so the next
494
+ * person's events do not carry the last person's.
495
+ */
496
+ setProperties: (properties: Record<string, unknown>) => void;
497
+ /**
498
+ * What `properties` and `setProperties` currently amount to.
499
+ *
500
+ * Read by the checkout hooks, which stamp them onto the Stripe session so
501
+ * that a conversion sent later from our servers carries the same fields the
502
+ * browser would have sent. Without it a custom field lands on whichever of
503
+ * the two hits the vendor happens to keep.
504
+ */
505
+ getProperties: () => Record<string, unknown>;
506
+ /**
507
+ * Report a checkout that has come back from Stripe, correcting its value
508
+ * against what was actually charged. Used by the return leg and by
509
+ * `useCheckoutCompleted`; both spend the same parked checkout, so whichever
510
+ * runs first is the one that reports.
511
+ */
512
+ completeCheckout: (pending: PendingCheckout, extraParams?: Record<string, unknown>) => Promise<void>;
513
+ /** Which providers are loaded, keyed by script key. */
514
+ installed: Record<string, string>;
515
+ /**
516
+ * True once the org's configuration has been fetched and the scripts it
517
+ * allows are installed. Anything fired before this reaches `onEvent` and no
518
+ * vendor, so a caller that owns a once-per-session event waits for it.
519
+ */
520
+ ready: boolean;
521
+ /**
522
+ * Where this visitor came from - click ids and campaign params, captured on
523
+ * the first page they landed on anywhere in the product and shared across
524
+ * subdomains. Rides on every event so a sign-up on `app.abc.com` can still
525
+ * be credited to the ad that was clicked on `abc.com`.
526
+ */
527
+ attribution: Attribution;
528
+ }
529
+ declare function useTracking(): TrackingContextValue;
530
+ interface BuildBaseTrackingProps extends TrackingOptions {
531
+ /** The BuildBase API base URL. */
532
+ serverUrl: string;
533
+ /** The organization id. */
534
+ orgId: string;
535
+ /**
536
+ * The auth client id for *this* app. Required: a client is what an app is,
537
+ * and it is what decides which scripts load here rather than on the org's
538
+ * other properties.
539
+ */
540
+ clientId: string;
541
+ /**
542
+ * A GA4 identity handed over from another domain, so this page measures as
543
+ * the same user rather than a new one.
544
+ *
545
+ * For the hosted sign-in pages, which are on a different registrable domain
546
+ * from the app the visitor came from. GA4's `_ga` cookie cannot cross that
547
+ * boundary, so without this the sign-in step lands under somebody who never
548
+ * visited the app and the funnel cannot be built at all.
549
+ *
550
+ * `signIn()` sends it with the auth request; the pages read it back and pass
551
+ * it here. Ordinary apps never set this.
552
+ */
553
+ identity?: {
554
+ gaClientId?: string;
555
+ gaSessionId?: string;
556
+ };
557
+ children?: React.ReactNode;
558
+ }
559
+ /**
560
+ * Installs the org's configured tracking scripts into this app.
561
+ *
562
+ * Mountable on its own (a marketing site that wants tags and nothing else) and
563
+ * mounted internally by `SaaSOSProvider`, which additionally knows about
564
+ * signup, login and checkout and fires those events for you.
565
+ */
566
+ declare const BuildBaseTracking: React.FC<BuildBaseTrackingProps>;
567
+ /**
568
+ * Fire `purchase` for a checkout the customer has just come back from paying.
569
+ *
570
+ * Call it on the page Stripe returns to, and nowhere else - the SDK has no
571
+ * way to recognise that page, because its URL is the app's own `successUrl`.
572
+ *
573
+ * It is a no-op when no checkout is in flight, so it is safe on a page a
574
+ * customer might also reach directly, and it consumes what it reads, so a
575
+ * refresh cannot count the sale twice. The event carries the same id that was
576
+ * written onto the Stripe subscription as `bb_event_id`.
577
+ *
578
+ * @example
579
+ * ```tsx
580
+ * export default function BillingSuccess() {
581
+ * useCheckoutCompleted({ value: 79, currency: 'USD' });
582
+ * return <p>Thanks - your plan is active.</p>;
583
+ * }
584
+ * ```
585
+ */
586
+ declare function useCheckoutCompleted(params?: Record<string, unknown>): void;
587
+
588
+ /**
589
+ * What each vendor calls the events we fire.
590
+ *
591
+ * `null` means the vendor has no counterpart and we deliberately send nothing.
592
+ * No ad network treats a returning login as a conversion, and reporting one
593
+ * inflates every campaign that ever touched an existing customer. An upgrade
594
+ * is not mapped to `Purchase` for the same reason - it double-counts against
595
+ * the original subscription.
596
+ *
597
+ * X, LinkedIn and Google Ads take no event name at all: they take a
598
+ * per-conversion id the tenant created in the ad platform, which is why
599
+ * `conversionIds` rides along on the script config.
600
+ */
601
+ declare const EVENT_MAPPINGS: Record<CanonicalEventName, {
602
+ ga4: string | null;
603
+ meta: string | null;
604
+ tiktok: string | null;
605
+ reddit: string | null;
606
+ }>;
607
+ declare function isCanonicalEvent(name: string): name is CanonicalEventName;
608
+
609
+ /**
610
+ * The steps between a visit and a paying, retained customer that happen
611
+ * inside BuildBase's own UI.
612
+ *
613
+ * `sign_up` and `purchase` say that someone arrived and that someone paid.
614
+ * They cannot say where the rest went: who opened the plan picker and closed
615
+ * it, who picked a plan and backed out of Stripe, who kept hitting a feature
616
+ * their plan does not include. Those moments happen in the settings modal, the
617
+ * plan picker and the gates - components the SDK renders - so the SDK is the
618
+ * only thing that can report them, and it does it for every app without a line
619
+ * of code on their side.
620
+ *
621
+ * Product events, never conversions. None of them map to a Meta, TikTok or
622
+ * Reddit event, so no ad network sees them; they reach the data layer and the
623
+ * vendors that take arbitrary names - GA4, PostHog, Clarity, Hotjar, Plausible.
624
+ * Google Ads, X and LinkedIn hear one only if the tenant attaches a conversion
625
+ * id to that exact name in the console.
626
+ *
627
+ * The names are frozen once released. They become column values in somebody's
628
+ * warehouse and step names in somebody's saved funnel, and a rename breaks both
629
+ * without an error.
630
+ */
631
+ declare const FUNNEL_EVENTS: {
632
+ readonly sign_in_started: "The sign-in / sign-up button was pressed.";
633
+ readonly pricing_viewed: "A <PricingPage> finished loading its plans.";
634
+ readonly pricing_plan_selected: "A plan was chosen on a <PricingPage>.";
635
+ readonly workspace_switcher_opened: "The workspace switcher was opened.";
636
+ readonly workspace_settings_opened: "The workspace settings modal was opened.";
637
+ readonly workspace_settings_section_viewed: "A different section of the settings modal was opened.";
638
+ readonly plan_picker_opened: "The plan picker was shown.";
639
+ readonly billing_interval_changed: "Monthly / quarterly / yearly was switched in the plan picker.";
640
+ readonly plan_selected: "A plan was chosen in the plan picker.";
641
+ readonly plan_picker_dismissed: "The plan picker was closed without choosing a plan.";
642
+ readonly checkout_abandoned: "The customer came back from Stripe without paying.";
643
+ readonly billing_portal_opened: "Stripe’s billing portal was opened.";
644
+ readonly subscription_cancel_started: "The cancel-subscription confirmation was opened.";
645
+ readonly subscription_cancelled: "A subscription was cancelled.";
646
+ readonly subscription_resumed: "A cancelled subscription was resumed.";
647
+ readonly feature_gate_shown: "A feature gate rendered its \"not on your plan\" side.";
648
+ readonly quota_limit_reached: "A quota gate rendered because a quota is used up.";
649
+ readonly quota_threshold_reached: "A usage-threshold gate rendered.";
650
+ readonly plan_gate_shown: "A plan gate rendered its \"needs another plan\" side.";
651
+ readonly subscription_notice_shown: "A billing notice (trial ending, payment failed…) was shown.";
652
+ readonly sdk_error: "An SDK operation failed (component, action and code only).";
653
+ };
654
+ type FunnelEventName = keyof typeof FUNNEL_EVENTS;
655
+
656
+ export { ATTRIBUTION_PARAMS, BuildBaseTracking, EVENT_MAPPINGS, FUNNEL_EVENTS, TAG_PROVIDERS, TAG_PROVIDER_IDS, TAG_SCRIPT_ORIGINS, captureAttribution, getAttribution, isCanonicalEvent, isTagProviderId, isValidProviderId, useCheckoutCompleted, useTracking };
657
+ export type { Attribution, AttributionParam, BuildBaseTrackingProps, CanonicalEventName, ConsentState, EventOwnership, FunnelEventName, PublicTrackingConfig, PublicTrackingScript, TagConsentCategory, TagProviderId, TagProviderSpec, TrackOptions, TrackingConsentEntry, TrackingContextValue, TrackingEvent, TrackingEventsConfig, TrackingOptions };