@createcms/core 0.1.1

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 (83) hide show
  1. package/README.md +169 -0
  2. package/dist/ab-edge/index.cjs +214 -0
  3. package/dist/ab-edge/index.d.cts +121 -0
  4. package/dist/ab-edge/index.d.ts +121 -0
  5. package/dist/ab-edge/index.js +205 -0
  6. package/dist/bin/createcms.js +3082 -0
  7. package/dist/db.cjs +496 -0
  8. package/dist/db.d.cts +128 -0
  9. package/dist/db.d.ts +128 -0
  10. package/dist/db.js +488 -0
  11. package/dist/index.cjs +13789 -0
  12. package/dist/index.d.cts +10277 -0
  13. package/dist/index.d.ts +10277 -0
  14. package/dist/index.js +13737 -0
  15. package/dist/nanoid.cjs +50 -0
  16. package/dist/nanoid.d.cts +29 -0
  17. package/dist/nanoid.d.ts +29 -0
  18. package/dist/nanoid.js +47 -0
  19. package/dist/next/index.cjs +60 -0
  20. package/dist/next/index.d.cts +141 -0
  21. package/dist/next/index.d.ts +141 -0
  22. package/dist/next/index.js +58 -0
  23. package/dist/next/middleware.cjs +113 -0
  24. package/dist/next/middleware.d.cts +77 -0
  25. package/dist/next/middleware.d.ts +77 -0
  26. package/dist/next/middleware.js +111 -0
  27. package/dist/plugins/ab-test/analytics/upstash.cjs +345 -0
  28. package/dist/plugins/ab-test/analytics/upstash.d.cts +193 -0
  29. package/dist/plugins/ab-test/analytics/upstash.d.ts +193 -0
  30. package/dist/plugins/ab-test/analytics/upstash.js +343 -0
  31. package/dist/plugins/ab-test/client.cjs +686 -0
  32. package/dist/plugins/ab-test/client.d.cts +233 -0
  33. package/dist/plugins/ab-test/client.d.ts +233 -0
  34. package/dist/plugins/ab-test/client.js +684 -0
  35. package/dist/plugins/ab-test/index.cjs +3400 -0
  36. package/dist/plugins/ab-test/index.d.cts +1131 -0
  37. package/dist/plugins/ab-test/index.d.ts +1131 -0
  38. package/dist/plugins/ab-test/index.js +3367 -0
  39. package/dist/plugins/client.cjs +20 -0
  40. package/dist/plugins/client.d.cts +3 -0
  41. package/dist/plugins/client.d.ts +3 -0
  42. package/dist/plugins/client.js +3 -0
  43. package/dist/plugins/consent/client.cjs +315 -0
  44. package/dist/plugins/consent/client.d.cts +145 -0
  45. package/dist/plugins/consent/client.d.ts +145 -0
  46. package/dist/plugins/consent/client.js +313 -0
  47. package/dist/plugins/consent/index.cjs +267 -0
  48. package/dist/plugins/consent/index.d.cts +618 -0
  49. package/dist/plugins/consent/index.d.ts +618 -0
  50. package/dist/plugins/consent/index.js +258 -0
  51. package/dist/plugins/i18n/index.cjs +2177 -0
  52. package/dist/plugins/i18n/index.d.cts +562 -0
  53. package/dist/plugins/i18n/index.d.ts +562 -0
  54. package/dist/plugins/i18n/index.js +2150 -0
  55. package/dist/plugins/media-optimize/index.cjs +315 -0
  56. package/dist/plugins/media-optimize/index.d.cts +144 -0
  57. package/dist/plugins/media-optimize/index.d.ts +144 -0
  58. package/dist/plugins/media-optimize/index.js +311 -0
  59. package/dist/plugins/multi-tenant/index.cjs +210 -0
  60. package/dist/plugins/multi-tenant/index.d.cts +431 -0
  61. package/dist/plugins/multi-tenant/index.d.ts +431 -0
  62. package/dist/plugins/multi-tenant/index.js +207 -0
  63. package/dist/plugins/server.cjs +24 -0
  64. package/dist/plugins/server.d.cts +3 -0
  65. package/dist/plugins/server.d.ts +3 -0
  66. package/dist/plugins/server.js +3 -0
  67. package/dist/react/blocks.cjs +233 -0
  68. package/dist/react/blocks.d.cts +320 -0
  69. package/dist/react/blocks.d.ts +320 -0
  70. package/dist/react/blocks.js +226 -0
  71. package/dist/react/index.cjs +901 -0
  72. package/dist/react/index.d.cts +992 -0
  73. package/dist/react/index.d.ts +992 -0
  74. package/dist/react/index.js +872 -0
  75. package/dist/react/tracking.cjs +243 -0
  76. package/dist/react/tracking.d.cts +364 -0
  77. package/dist/react/tracking.d.ts +364 -0
  78. package/dist/react/tracking.js +216 -0
  79. package/dist/react/variant.cjs +59 -0
  80. package/dist/react/variant.d.cts +26 -0
  81. package/dist/react/variant.d.ts +26 -0
  82. package/dist/react/variant.js +57 -0
  83. package/package.json +303 -0
@@ -0,0 +1,233 @@
1
+ import { WritableAtom } from 'nanostores';
2
+ import { ConsentState } from '../consent/index.cjs';
3
+ export { ConsentPurpose, ConsentSignal, ConsentState } from '../consent/index.cjs';
4
+
5
+ type CMSFetch = (path: string, options?: {
6
+ method?: string;
7
+ body?: unknown;
8
+ query?: unknown;
9
+ /**
10
+ * Forwarded to the underlying `fetch` (better-call → @better-fetch → native
11
+ * `fetch`). Set for fire-and-forget analytics beacons (the A/B event ingest)
12
+ * so the POST is NOT cancelled when the page unloads/navigates mid-request.
13
+ */
14
+ keepalive?: boolean;
15
+ }) => Promise<unknown>;
16
+ interface CMSClientStore {
17
+ notify: (signal: string) => void;
18
+ listen: (signal: string, listener: () => void) => void;
19
+ atoms: Record<string, WritableAtom<unknown>>;
20
+ }
21
+
22
+ type AggregatedVariantResult = {
23
+ variantId: string;
24
+ variantName: string;
25
+ impressions: number;
26
+ conversions: number;
27
+ uniqueVisitors: number;
28
+ conversionRate: number;
29
+ /**
30
+ * Funnel (M4): total distinct interaction ids (each <TrackedForm> submit mints
31
+ * one) = the attempts. completionRate = distinct interactions that reached the
32
+ * goal event / attempts (computed by getResults when a goal is set).
33
+ */
34
+ attempts: number;
35
+ completionRate: number;
36
+ eventBreakdown: Record<string, {
37
+ count: number;
38
+ uniqueVisitors: number;
39
+ distinctInteractions: number;
40
+ }>;
41
+ };
42
+ type AggregatedResults = {
43
+ testId: string;
44
+ variants: AggregatedVariantResult[];
45
+ totalImpressions: number;
46
+ totalConversions: number;
47
+ };
48
+
49
+ /**
50
+ * The client-side event a sink receives. Decoupled from the server `CMSEvent`
51
+ * (no `timestamp`/storage `id` — those are minted server-side): this is what the
52
+ * browser knows at fire time.
53
+ */
54
+ type ClientCMSEvent = {
55
+ /** Canonical event name, e.g. `'impression' | 'conversion' | 'form_submit'`. */
56
+ name: string;
57
+ /**
58
+ * A/B attribution by the SERVER-served branch (Pattern A: the variant came
59
+ * from the URL). `branchId` is what the client has; the store leg resolves it
60
+ * to a `variantId` server-side. Absent for non-A/B analytics events.
61
+ */
62
+ ab?: {
63
+ testId: string;
64
+ branchId?: string;
65
+ variantId?: string;
66
+ };
67
+ /** Identity — only on the consent-gated unique-visitor path; never anonymous. */
68
+ visitorId?: string;
69
+ /** True when no identifier is attached (the consent-free aggregate path). */
70
+ anonymous: boolean;
71
+ /** Originating functional block instance (the `trackingId` + block type). */
72
+ source?: {
73
+ handle?: string;
74
+ type?: string;
75
+ };
76
+ /** Funnel grouping id (M4): shared by the attempt + success legs of one interaction. */
77
+ interactionId?: string;
78
+ /** GA4 stitching ids (M5): set only when consent is granted; the store leg
79
+ * forwards them so the server-MP can attribute the hit. */
80
+ transport?: {
81
+ clientId?: string;
82
+ sessionId?: string;
83
+ engagementTimeMsec?: number;
84
+ };
85
+ /**
86
+ * Consent Mode v2 state at fire time (M5). Stamped ALONGSIDE `transport` (only
87
+ * when analytics_storage is granted), so the store leg can relay it to the
88
+ * server, where `buildGa4Payload` needs it to authorize the server-MP forward.
89
+ * Absent on the consent-free anonymous path — its absence is exactly what keeps
90
+ * the server's denied-consent guard from dropping the anonymous aggregate count.
91
+ */
92
+ consent?: ConsentState;
93
+ /** Scalar event params (GA4 wire params). */
94
+ params?: Record<string, string | number | boolean>;
95
+ metadata?: Record<string, unknown>;
96
+ };
97
+
98
+ type ABTestContext = {
99
+ key: string;
100
+ anonymous?: boolean;
101
+ };
102
+ type CachedAssignment = {
103
+ variantId: string;
104
+ branchId: string;
105
+ assignedAt: number;
106
+ };
107
+ type ABTestClientOptions = {
108
+ /**
109
+ * Drop the client-side dataLayer/GTM sink (M5). Enable this when the SAME
110
+ * goals are forwarded server-side via the plugin's `ga4` (server-MP) config —
111
+ * otherwise GA4 double-counts (one hit from the browser push + one from the
112
+ * server). With it set, the consent-free anonymous A/B store leg still fires;
113
+ * only the `window.dataLayer.push` leg is removed.
114
+ */
115
+ disableDataLayerSink?: boolean;
116
+ };
117
+ declare function abTestClient(options?: ABTestClientOptions): {
118
+ id: "abTest";
119
+ $ERROR_CODES: {
120
+ readonly AB_TEST_NOT_FOUND: {
121
+ readonly status: 404;
122
+ readonly message: "A/B test not found";
123
+ };
124
+ readonly AB_TEST_INVALID_STATUS: {
125
+ readonly status: 400;
126
+ readonly message: "Invalid status transition for this A/B test";
127
+ };
128
+ readonly AB_TEST_WEIGHTS_INVALID: {
129
+ readonly status: 400;
130
+ readonly message: "Variant weights must sum to 100";
131
+ };
132
+ readonly AB_TEST_DUPLICATE_RUNNING: {
133
+ readonly status: 409;
134
+ readonly message: "Another test is already running for this root";
135
+ };
136
+ readonly AB_TEST_CROSS_EMBED_CONFLICT: {
137
+ readonly status: 409;
138
+ readonly message: "Cannot run: a co-rendering root (an embedded reusable block or its host page) already has a running test — at most one A/B axis may vary per render";
139
+ };
140
+ readonly AB_TEST_BRANCH_NOT_PUBLISHED: {
141
+ readonly status: 400;
142
+ readonly message: "All variant branches must be published";
143
+ };
144
+ readonly AB_TEST_NO_CONTEXT: {
145
+ readonly status: 400;
146
+ readonly message: "No visitor context set. Call identify() first.";
147
+ };
148
+ readonly AB_TEST_FLUSH_NOT_SUPPORTED: {
149
+ readonly status: 400;
150
+ readonly message: "Flush is not supported by the current analytics adapter";
151
+ };
152
+ readonly AB_TEST_VARIANT_NOT_FOUND: {
153
+ readonly status: 404;
154
+ readonly message: "A/B test variant not found";
155
+ };
156
+ readonly AB_TEST_TRACKING_ID_MISSING: {
157
+ readonly status: 400;
158
+ readonly message: "A functional block (one that declares events) is missing its trackingId — every such block must have a non-empty trackingId before the branch can be published";
159
+ };
160
+ readonly AB_TEST_TRACKING_ID_DUPLICATE: {
161
+ readonly status: 400;
162
+ readonly message: "Duplicate trackingId in this branch — each functional block must have a unique trackingId";
163
+ };
164
+ readonly AB_TEST_TRACKING_ID_DRIFT: {
165
+ readonly status: 409;
166
+ readonly message: "trackingId drift across A/B variant branches — the set of functional trackingIds must be identical across all variant branches of a root, so a chosen goal exists in every arm";
167
+ };
168
+ };
169
+ init(_$fetch: CMSFetch, _$store: CMSClientStore): Promise<{
170
+ context: {
171
+ [x: string]: null;
172
+ };
173
+ }>;
174
+ getActions($fetch: CMSFetch, _$store: CMSClientStore, baseURL: string): {
175
+ abTest: {
176
+ /**
177
+ * Tell the CMS about the visitor's consent (Consent Mode v2) — a real
178
+ * decision (treated like a Consent Mode `update`). Optional: the gate
179
+ * also auto-reads Consent Mode commands off the dataLayer. That
180
+ * auto-read is best-effort (a `push`-hook fast path plus a re-scan
181
+ * poll); when running GTM, calling this from the CMP's Consent Mode
182
+ * update callback is the most reliable path.
183
+ */
184
+ setConsent(consent: Partial<ConsentState>): void;
185
+ /** Read the current resolved consent state. */
186
+ getConsent(): ConsentState;
187
+ /**
188
+ * Report the impression for a SERVER-rendered variant (AB_FANOUT
189
+ * Pattern A). Call with the served branch (the `/<branchId>/` URL
190
+ * segment). ANONYMOUS + consent-free: sends no visitor id, not
191
+ * consent-gated, deduped per session via sessionStorage. Reach it from
192
+ * the variant route via {@link useImpression}.
193
+ */
194
+ recordImpression: (testId: string, branchId: string) => void;
195
+ /**
196
+ * React hook: fire the Pattern A impression once per (testId, branchId)
197
+ * on mount. Render a tiny `'use client'` beacon from the variant-coded
198
+ * route: `cmsClient.abTest.useImpression(testId, branchId)`.
199
+ */
200
+ useImpression(testId: string, branchId: string): void;
201
+ /**
202
+ * Fire a raw client event through the M3a sink pipeline (the SAME
203
+ * sinks + consent gate as recordImpression — so consent state never
204
+ * diverges). The M3c `<TrackingRuntimeProvider>` wires this as its
205
+ * `dispatch`; functional blocks reach it via `useTrackedBlock().fire`.
206
+ * Anonymous aggregate legs are consent-free; the GA4/gtm leg is gated.
207
+ */
208
+ dispatchEvent(event: ClientCMSEvent): void;
209
+ identify(ctx: ABTestContext): void;
210
+ getVariant(testId: string): Promise<CachedAssignment>;
211
+ reset(): void;
212
+ /**
213
+ * React hook for live dashboard results.
214
+ * Connects to the auto-registered realtime SSE route.
215
+ */
216
+ useLiveResults(opts: {
217
+ testId: string;
218
+ initial: AggregatedResults;
219
+ }): {
220
+ results: AggregatedResults;
221
+ isLive: boolean;
222
+ };
223
+ };
224
+ };
225
+ pathMethods: {
226
+ '/abTest/assignVariant': "POST";
227
+ '/abTest/trackEvent': "POST";
228
+ '/abTest/getResults': "GET";
229
+ };
230
+ };
231
+
232
+ export { abTestClient };
233
+ export type { ABTestClientOptions };
@@ -0,0 +1,233 @@
1
+ import { WritableAtom } from 'nanostores';
2
+ import { ConsentState } from '../consent/index.js';
3
+ export { ConsentPurpose, ConsentSignal, ConsentState } from '../consent/index.js';
4
+
5
+ type CMSFetch = (path: string, options?: {
6
+ method?: string;
7
+ body?: unknown;
8
+ query?: unknown;
9
+ /**
10
+ * Forwarded to the underlying `fetch` (better-call → @better-fetch → native
11
+ * `fetch`). Set for fire-and-forget analytics beacons (the A/B event ingest)
12
+ * so the POST is NOT cancelled when the page unloads/navigates mid-request.
13
+ */
14
+ keepalive?: boolean;
15
+ }) => Promise<unknown>;
16
+ interface CMSClientStore {
17
+ notify: (signal: string) => void;
18
+ listen: (signal: string, listener: () => void) => void;
19
+ atoms: Record<string, WritableAtom<unknown>>;
20
+ }
21
+
22
+ type AggregatedVariantResult = {
23
+ variantId: string;
24
+ variantName: string;
25
+ impressions: number;
26
+ conversions: number;
27
+ uniqueVisitors: number;
28
+ conversionRate: number;
29
+ /**
30
+ * Funnel (M4): total distinct interaction ids (each <TrackedForm> submit mints
31
+ * one) = the attempts. completionRate = distinct interactions that reached the
32
+ * goal event / attempts (computed by getResults when a goal is set).
33
+ */
34
+ attempts: number;
35
+ completionRate: number;
36
+ eventBreakdown: Record<string, {
37
+ count: number;
38
+ uniqueVisitors: number;
39
+ distinctInteractions: number;
40
+ }>;
41
+ };
42
+ type AggregatedResults = {
43
+ testId: string;
44
+ variants: AggregatedVariantResult[];
45
+ totalImpressions: number;
46
+ totalConversions: number;
47
+ };
48
+
49
+ /**
50
+ * The client-side event a sink receives. Decoupled from the server `CMSEvent`
51
+ * (no `timestamp`/storage `id` — those are minted server-side): this is what the
52
+ * browser knows at fire time.
53
+ */
54
+ type ClientCMSEvent = {
55
+ /** Canonical event name, e.g. `'impression' | 'conversion' | 'form_submit'`. */
56
+ name: string;
57
+ /**
58
+ * A/B attribution by the SERVER-served branch (Pattern A: the variant came
59
+ * from the URL). `branchId` is what the client has; the store leg resolves it
60
+ * to a `variantId` server-side. Absent for non-A/B analytics events.
61
+ */
62
+ ab?: {
63
+ testId: string;
64
+ branchId?: string;
65
+ variantId?: string;
66
+ };
67
+ /** Identity — only on the consent-gated unique-visitor path; never anonymous. */
68
+ visitorId?: string;
69
+ /** True when no identifier is attached (the consent-free aggregate path). */
70
+ anonymous: boolean;
71
+ /** Originating functional block instance (the `trackingId` + block type). */
72
+ source?: {
73
+ handle?: string;
74
+ type?: string;
75
+ };
76
+ /** Funnel grouping id (M4): shared by the attempt + success legs of one interaction. */
77
+ interactionId?: string;
78
+ /** GA4 stitching ids (M5): set only when consent is granted; the store leg
79
+ * forwards them so the server-MP can attribute the hit. */
80
+ transport?: {
81
+ clientId?: string;
82
+ sessionId?: string;
83
+ engagementTimeMsec?: number;
84
+ };
85
+ /**
86
+ * Consent Mode v2 state at fire time (M5). Stamped ALONGSIDE `transport` (only
87
+ * when analytics_storage is granted), so the store leg can relay it to the
88
+ * server, where `buildGa4Payload` needs it to authorize the server-MP forward.
89
+ * Absent on the consent-free anonymous path — its absence is exactly what keeps
90
+ * the server's denied-consent guard from dropping the anonymous aggregate count.
91
+ */
92
+ consent?: ConsentState;
93
+ /** Scalar event params (GA4 wire params). */
94
+ params?: Record<string, string | number | boolean>;
95
+ metadata?: Record<string, unknown>;
96
+ };
97
+
98
+ type ABTestContext = {
99
+ key: string;
100
+ anonymous?: boolean;
101
+ };
102
+ type CachedAssignment = {
103
+ variantId: string;
104
+ branchId: string;
105
+ assignedAt: number;
106
+ };
107
+ type ABTestClientOptions = {
108
+ /**
109
+ * Drop the client-side dataLayer/GTM sink (M5). Enable this when the SAME
110
+ * goals are forwarded server-side via the plugin's `ga4` (server-MP) config —
111
+ * otherwise GA4 double-counts (one hit from the browser push + one from the
112
+ * server). With it set, the consent-free anonymous A/B store leg still fires;
113
+ * only the `window.dataLayer.push` leg is removed.
114
+ */
115
+ disableDataLayerSink?: boolean;
116
+ };
117
+ declare function abTestClient(options?: ABTestClientOptions): {
118
+ id: "abTest";
119
+ $ERROR_CODES: {
120
+ readonly AB_TEST_NOT_FOUND: {
121
+ readonly status: 404;
122
+ readonly message: "A/B test not found";
123
+ };
124
+ readonly AB_TEST_INVALID_STATUS: {
125
+ readonly status: 400;
126
+ readonly message: "Invalid status transition for this A/B test";
127
+ };
128
+ readonly AB_TEST_WEIGHTS_INVALID: {
129
+ readonly status: 400;
130
+ readonly message: "Variant weights must sum to 100";
131
+ };
132
+ readonly AB_TEST_DUPLICATE_RUNNING: {
133
+ readonly status: 409;
134
+ readonly message: "Another test is already running for this root";
135
+ };
136
+ readonly AB_TEST_CROSS_EMBED_CONFLICT: {
137
+ readonly status: 409;
138
+ readonly message: "Cannot run: a co-rendering root (an embedded reusable block or its host page) already has a running test — at most one A/B axis may vary per render";
139
+ };
140
+ readonly AB_TEST_BRANCH_NOT_PUBLISHED: {
141
+ readonly status: 400;
142
+ readonly message: "All variant branches must be published";
143
+ };
144
+ readonly AB_TEST_NO_CONTEXT: {
145
+ readonly status: 400;
146
+ readonly message: "No visitor context set. Call identify() first.";
147
+ };
148
+ readonly AB_TEST_FLUSH_NOT_SUPPORTED: {
149
+ readonly status: 400;
150
+ readonly message: "Flush is not supported by the current analytics adapter";
151
+ };
152
+ readonly AB_TEST_VARIANT_NOT_FOUND: {
153
+ readonly status: 404;
154
+ readonly message: "A/B test variant not found";
155
+ };
156
+ readonly AB_TEST_TRACKING_ID_MISSING: {
157
+ readonly status: 400;
158
+ readonly message: "A functional block (one that declares events) is missing its trackingId — every such block must have a non-empty trackingId before the branch can be published";
159
+ };
160
+ readonly AB_TEST_TRACKING_ID_DUPLICATE: {
161
+ readonly status: 400;
162
+ readonly message: "Duplicate trackingId in this branch — each functional block must have a unique trackingId";
163
+ };
164
+ readonly AB_TEST_TRACKING_ID_DRIFT: {
165
+ readonly status: 409;
166
+ readonly message: "trackingId drift across A/B variant branches — the set of functional trackingIds must be identical across all variant branches of a root, so a chosen goal exists in every arm";
167
+ };
168
+ };
169
+ init(_$fetch: CMSFetch, _$store: CMSClientStore): Promise<{
170
+ context: {
171
+ [x: string]: null;
172
+ };
173
+ }>;
174
+ getActions($fetch: CMSFetch, _$store: CMSClientStore, baseURL: string): {
175
+ abTest: {
176
+ /**
177
+ * Tell the CMS about the visitor's consent (Consent Mode v2) — a real
178
+ * decision (treated like a Consent Mode `update`). Optional: the gate
179
+ * also auto-reads Consent Mode commands off the dataLayer. That
180
+ * auto-read is best-effort (a `push`-hook fast path plus a re-scan
181
+ * poll); when running GTM, calling this from the CMP's Consent Mode
182
+ * update callback is the most reliable path.
183
+ */
184
+ setConsent(consent: Partial<ConsentState>): void;
185
+ /** Read the current resolved consent state. */
186
+ getConsent(): ConsentState;
187
+ /**
188
+ * Report the impression for a SERVER-rendered variant (AB_FANOUT
189
+ * Pattern A). Call with the served branch (the `/<branchId>/` URL
190
+ * segment). ANONYMOUS + consent-free: sends no visitor id, not
191
+ * consent-gated, deduped per session via sessionStorage. Reach it from
192
+ * the variant route via {@link useImpression}.
193
+ */
194
+ recordImpression: (testId: string, branchId: string) => void;
195
+ /**
196
+ * React hook: fire the Pattern A impression once per (testId, branchId)
197
+ * on mount. Render a tiny `'use client'` beacon from the variant-coded
198
+ * route: `cmsClient.abTest.useImpression(testId, branchId)`.
199
+ */
200
+ useImpression(testId: string, branchId: string): void;
201
+ /**
202
+ * Fire a raw client event through the M3a sink pipeline (the SAME
203
+ * sinks + consent gate as recordImpression — so consent state never
204
+ * diverges). The M3c `<TrackingRuntimeProvider>` wires this as its
205
+ * `dispatch`; functional blocks reach it via `useTrackedBlock().fire`.
206
+ * Anonymous aggregate legs are consent-free; the GA4/gtm leg is gated.
207
+ */
208
+ dispatchEvent(event: ClientCMSEvent): void;
209
+ identify(ctx: ABTestContext): void;
210
+ getVariant(testId: string): Promise<CachedAssignment>;
211
+ reset(): void;
212
+ /**
213
+ * React hook for live dashboard results.
214
+ * Connects to the auto-registered realtime SSE route.
215
+ */
216
+ useLiveResults(opts: {
217
+ testId: string;
218
+ initial: AggregatedResults;
219
+ }): {
220
+ results: AggregatedResults;
221
+ isLive: boolean;
222
+ };
223
+ };
224
+ };
225
+ pathMethods: {
226
+ '/abTest/assignVariant': "POST";
227
+ '/abTest/trackEvent': "POST";
228
+ '/abTest/getResults': "GET";
229
+ };
230
+ };
231
+
232
+ export { abTestClient };
233
+ export type { ABTestClientOptions };