@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.
- package/README.md +169 -0
- package/dist/ab-edge/index.cjs +214 -0
- package/dist/ab-edge/index.d.cts +121 -0
- package/dist/ab-edge/index.d.ts +121 -0
- package/dist/ab-edge/index.js +205 -0
- package/dist/bin/createcms.js +3082 -0
- package/dist/db.cjs +496 -0
- package/dist/db.d.cts +128 -0
- package/dist/db.d.ts +128 -0
- package/dist/db.js +488 -0
- package/dist/index.cjs +13789 -0
- package/dist/index.d.cts +10277 -0
- package/dist/index.d.ts +10277 -0
- package/dist/index.js +13737 -0
- package/dist/nanoid.cjs +50 -0
- package/dist/nanoid.d.cts +29 -0
- package/dist/nanoid.d.ts +29 -0
- package/dist/nanoid.js +47 -0
- package/dist/next/index.cjs +60 -0
- package/dist/next/index.d.cts +141 -0
- package/dist/next/index.d.ts +141 -0
- package/dist/next/index.js +58 -0
- package/dist/next/middleware.cjs +113 -0
- package/dist/next/middleware.d.cts +77 -0
- package/dist/next/middleware.d.ts +77 -0
- package/dist/next/middleware.js +111 -0
- package/dist/plugins/ab-test/analytics/upstash.cjs +345 -0
- package/dist/plugins/ab-test/analytics/upstash.d.cts +193 -0
- package/dist/plugins/ab-test/analytics/upstash.d.ts +193 -0
- package/dist/plugins/ab-test/analytics/upstash.js +343 -0
- package/dist/plugins/ab-test/client.cjs +686 -0
- package/dist/plugins/ab-test/client.d.cts +233 -0
- package/dist/plugins/ab-test/client.d.ts +233 -0
- package/dist/plugins/ab-test/client.js +684 -0
- package/dist/plugins/ab-test/index.cjs +3400 -0
- package/dist/plugins/ab-test/index.d.cts +1131 -0
- package/dist/plugins/ab-test/index.d.ts +1131 -0
- package/dist/plugins/ab-test/index.js +3367 -0
- package/dist/plugins/client.cjs +20 -0
- package/dist/plugins/client.d.cts +3 -0
- package/dist/plugins/client.d.ts +3 -0
- package/dist/plugins/client.js +3 -0
- package/dist/plugins/consent/client.cjs +315 -0
- package/dist/plugins/consent/client.d.cts +145 -0
- package/dist/plugins/consent/client.d.ts +145 -0
- package/dist/plugins/consent/client.js +313 -0
- package/dist/plugins/consent/index.cjs +267 -0
- package/dist/plugins/consent/index.d.cts +618 -0
- package/dist/plugins/consent/index.d.ts +618 -0
- package/dist/plugins/consent/index.js +258 -0
- package/dist/plugins/i18n/index.cjs +2177 -0
- package/dist/plugins/i18n/index.d.cts +562 -0
- package/dist/plugins/i18n/index.d.ts +562 -0
- package/dist/plugins/i18n/index.js +2150 -0
- package/dist/plugins/media-optimize/index.cjs +315 -0
- package/dist/plugins/media-optimize/index.d.cts +144 -0
- package/dist/plugins/media-optimize/index.d.ts +144 -0
- package/dist/plugins/media-optimize/index.js +311 -0
- package/dist/plugins/multi-tenant/index.cjs +210 -0
- package/dist/plugins/multi-tenant/index.d.cts +431 -0
- package/dist/plugins/multi-tenant/index.d.ts +431 -0
- package/dist/plugins/multi-tenant/index.js +207 -0
- package/dist/plugins/server.cjs +24 -0
- package/dist/plugins/server.d.cts +3 -0
- package/dist/plugins/server.d.ts +3 -0
- package/dist/plugins/server.js +3 -0
- package/dist/react/blocks.cjs +233 -0
- package/dist/react/blocks.d.cts +320 -0
- package/dist/react/blocks.d.ts +320 -0
- package/dist/react/blocks.js +226 -0
- package/dist/react/index.cjs +901 -0
- package/dist/react/index.d.cts +992 -0
- package/dist/react/index.d.ts +992 -0
- package/dist/react/index.js +872 -0
- package/dist/react/tracking.cjs +243 -0
- package/dist/react/tracking.d.cts +364 -0
- package/dist/react/tracking.d.ts +364 -0
- package/dist/react/tracking.js +216 -0
- package/dist/react/variant.cjs +59 -0
- package/dist/react/variant.d.cts +26 -0
- package/dist/react/variant.d.ts +26 -0
- package/dist/react/variant.js +57 -0
- package/package.json +303 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { PgDatabase } from 'drizzle-orm/pg-core';
|
|
2
|
+
import { ConsentState } from '../../consent/index.cjs';
|
|
3
|
+
|
|
4
|
+
type ColumnScalarType = 'text' | 'boolean' | 'integer' | 'timestamp' | 'jsonb' | 'tsvector';
|
|
5
|
+
type ColumnType<EnumTarget extends string = string> = ColumnScalarType | {
|
|
6
|
+
enum: EnumTarget;
|
|
7
|
+
};
|
|
8
|
+
type DefaultValue = {
|
|
9
|
+
kind: 'literal';
|
|
10
|
+
value: boolean | number | string | string[] | Record<string, unknown>;
|
|
11
|
+
} | {
|
|
12
|
+
kind: 'sql';
|
|
13
|
+
value: string;
|
|
14
|
+
};
|
|
15
|
+
type ForeignKeyAction = 'cascade' | 'restrict' | 'no action' | 'set null' | 'set default';
|
|
16
|
+
type IndexUsing = 'btree' | 'gin';
|
|
17
|
+
type ColumnDefinition<ReferenceTarget extends string = string, EnumTarget extends string = string> = {
|
|
18
|
+
type: ColumnType<EnumTarget>;
|
|
19
|
+
columnName?: string;
|
|
20
|
+
notNull?: boolean;
|
|
21
|
+
primaryKey?: boolean;
|
|
22
|
+
unique?: boolean;
|
|
23
|
+
default?: DefaultValue;
|
|
24
|
+
defaultId?: boolean;
|
|
25
|
+
defaultIdPrefix?: string;
|
|
26
|
+
defaultNow?: boolean;
|
|
27
|
+
jsonType?: string;
|
|
28
|
+
references?: {
|
|
29
|
+
table: ReferenceTarget;
|
|
30
|
+
column: string;
|
|
31
|
+
onDelete?: ForeignKeyAction;
|
|
32
|
+
onUpdate?: ForeignKeyAction;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
type TableColumns<ReferenceTarget extends string = string, EnumTarget extends string = string> = Record<string, ColumnDefinition<ReferenceTarget, EnumTarget>>;
|
|
36
|
+
type IndexDefinition<ColumnName extends string> = {
|
|
37
|
+
columns: readonly ColumnName[];
|
|
38
|
+
unique?: boolean;
|
|
39
|
+
using?: IndexUsing;
|
|
40
|
+
where?: string;
|
|
41
|
+
};
|
|
42
|
+
type CompositePrimaryKey<ColumnName extends string> = {
|
|
43
|
+
columns: readonly ColumnName[];
|
|
44
|
+
};
|
|
45
|
+
type TableLevelForeignKey<ColumnName extends string = string> = {
|
|
46
|
+
columns: readonly ColumnName[];
|
|
47
|
+
foreignTable: string;
|
|
48
|
+
foreignColumns: readonly string[];
|
|
49
|
+
name?: string;
|
|
50
|
+
onDelete?: ForeignKeyAction;
|
|
51
|
+
onUpdate?: ForeignKeyAction;
|
|
52
|
+
};
|
|
53
|
+
type TableDefinition<Columns extends TableColumns = TableColumns, ReferenceTarget extends string = string, EnumTarget extends string = string> = {
|
|
54
|
+
tableName?: string;
|
|
55
|
+
indexPrefix?: string;
|
|
56
|
+
columns: Columns;
|
|
57
|
+
indexes?: Record<string, IndexDefinition<Extract<keyof Columns, string>>>;
|
|
58
|
+
compositePrimaryKey?: CompositePrimaryKey<Extract<keyof Columns, string>>;
|
|
59
|
+
foreignKeys?: TableLevelForeignKey<Extract<keyof Columns, string>>[];
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
type DrizzleInstance = PgDatabase<any, Record<string, unknown>, any>;
|
|
63
|
+
|
|
64
|
+
/** Where an event originated — a functional block instance. */
|
|
65
|
+
type CMSEventSource = {
|
|
66
|
+
/** Stable, author-assigned instance handle (the block's `trackingId`). */
|
|
67
|
+
handle?: string;
|
|
68
|
+
/** Block type that emitted the event (e.g. `'signupForm'`). */
|
|
69
|
+
type?: string;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* The decoupled, analytics-agnostic event core. A/B attribution is **optional**
|
|
73
|
+
* (`ab`), so non-A/B events (page views, form submits) are first-class and can
|
|
74
|
+
* flow to non-A/B sinks (GA4, GTM) without inventing a fake test/variant.
|
|
75
|
+
*
|
|
76
|
+
* `ABTestEvent` is the derived view where `ab` is mandatory.
|
|
77
|
+
*/
|
|
78
|
+
type CMSEvent = {
|
|
79
|
+
/**
|
|
80
|
+
* Optional id used as the storage row key. When absent, the sink mints one.
|
|
81
|
+
* Dedup behaviour is **sink-specific**: the postgres sink dedups on it via
|
|
82
|
+
* `ON CONFLICT (id) DO NOTHING`; the upstash sink does not. (A tenant-scoped,
|
|
83
|
+
* client-supplied idempotency key — distinct from this row key — arrives with
|
|
84
|
+
* the M3 client legs.)
|
|
85
|
+
*/
|
|
86
|
+
id?: string;
|
|
87
|
+
/** Canonical event name, e.g. `'impression' | 'conversion' | 'form_submit'`. */
|
|
88
|
+
name: string;
|
|
89
|
+
/**
|
|
90
|
+
* Optional: the anonymous Pattern A path stores NO identifier (variant comes
|
|
91
|
+
* from the URL/variant-cookie). Only set for the consent-gated unique-visitor /
|
|
92
|
+
* GA4 path. Stored as NULL when absent → excluded from unique-visitor counts.
|
|
93
|
+
*/
|
|
94
|
+
visitorId?: string;
|
|
95
|
+
anonymous: boolean;
|
|
96
|
+
/** A/B attribution. Absent for non-A/B analytics events. */
|
|
97
|
+
ab?: {
|
|
98
|
+
testId: string;
|
|
99
|
+
variantId: string;
|
|
100
|
+
};
|
|
101
|
+
/** Originating functional block instance, if any. */
|
|
102
|
+
source?: CMSEventSource;
|
|
103
|
+
/**
|
|
104
|
+
* Consent state under which the event was emitted (Consent Mode v2).
|
|
105
|
+
* Forwarded to consent-aware sinks (e.g. the M5 server-MP consent block). NOTE:
|
|
106
|
+
* M1 forwards but does NOT persist this — there is no consent column yet.
|
|
107
|
+
*/
|
|
108
|
+
consent?: ConsentState;
|
|
109
|
+
/**
|
|
110
|
+
* Funnel grouping id (M4): a client-minted id shared by the attempt + success
|
|
111
|
+
* legs of ONE interaction (e.g. a <TrackedForm> submit). Lets completion_rate
|
|
112
|
+
* pair them. Distinct from any storage/dedup key — it groups, it doesn't dedup.
|
|
113
|
+
*/
|
|
114
|
+
interactionId?: string;
|
|
115
|
+
/**
|
|
116
|
+
* GA4 stitching identifiers (M5 server-MP): the client reads them from the
|
|
117
|
+
* `_ga` / `_ga_<id>` cookies and sends them ONLY when analytics_storage is
|
|
118
|
+
* granted, so the server-side ga4ServerSink can attribute the Measurement
|
|
119
|
+
* Protocol hit to the same user/session (else GA4 shows `(not set)`). Absent on
|
|
120
|
+
* the anonymous consent-free path — its presence is what gates the GA4 forward.
|
|
121
|
+
*/
|
|
122
|
+
transport?: {
|
|
123
|
+
clientId?: string;
|
|
124
|
+
sessionId?: string;
|
|
125
|
+
engagementTimeMsec?: number;
|
|
126
|
+
};
|
|
127
|
+
metadata?: Record<string, unknown>;
|
|
128
|
+
timestamp: Date;
|
|
129
|
+
};
|
|
130
|
+
type AggregatedVariantResult = {
|
|
131
|
+
variantId: string;
|
|
132
|
+
variantName: string;
|
|
133
|
+
impressions: number;
|
|
134
|
+
conversions: number;
|
|
135
|
+
uniqueVisitors: number;
|
|
136
|
+
conversionRate: number;
|
|
137
|
+
/**
|
|
138
|
+
* Funnel (M4): total distinct interaction ids (each <TrackedForm> submit mints
|
|
139
|
+
* one) = the attempts. completionRate = distinct interactions that reached the
|
|
140
|
+
* goal event / attempts (computed by getResults when a goal is set).
|
|
141
|
+
*/
|
|
142
|
+
attempts: number;
|
|
143
|
+
completionRate: number;
|
|
144
|
+
eventBreakdown: Record<string, {
|
|
145
|
+
count: number;
|
|
146
|
+
uniqueVisitors: number;
|
|
147
|
+
distinctInteractions: number;
|
|
148
|
+
}>;
|
|
149
|
+
};
|
|
150
|
+
type AggregatedResults = {
|
|
151
|
+
testId: string;
|
|
152
|
+
variants: AggregatedVariantResult[];
|
|
153
|
+
totalImpressions: number;
|
|
154
|
+
totalConversions: number;
|
|
155
|
+
};
|
|
156
|
+
type ABTestAnalyticsAdapter = {
|
|
157
|
+
/** Adapter-specific Postgres tables to merge into the plugin schema. */
|
|
158
|
+
tables?: Record<string, TableDefinition>;
|
|
159
|
+
/** Called once during plugin init. Receives the Drizzle DB instance. */
|
|
160
|
+
init?(db: DrizzleInstance): Promise<void> | void;
|
|
161
|
+
/**
|
|
162
|
+
* Record a single event. Accepts any {@link CMSEvent}: A/B-attributed events
|
|
163
|
+
* (impression/conversion) carry `ab`, non-A/B analytics events (form_submit,
|
|
164
|
+
* page_view) omit it.
|
|
165
|
+
*/
|
|
166
|
+
track(event: CMSEvent): Promise<void>;
|
|
167
|
+
/** Query aggregated results for a test. */
|
|
168
|
+
query(testId: string, options?: {
|
|
169
|
+
from?: Date;
|
|
170
|
+
to?: Date;
|
|
171
|
+
}): Promise<AggregatedResults>;
|
|
172
|
+
/** Optional batch flush (e.g. Upstash -> Postgres). */
|
|
173
|
+
flush?(testId?: string): Promise<{
|
|
174
|
+
flushed: number;
|
|
175
|
+
}>;
|
|
176
|
+
};
|
|
177
|
+
type UpstashAnalyticsOptions = {
|
|
178
|
+
url: string;
|
|
179
|
+
token: string;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Upstash Redis Stream adapter with batch flush and realtime delta publishing.
|
|
184
|
+
*
|
|
185
|
+
* Requires `@upstash/redis` and `@upstash/realtime` as peer dependencies.
|
|
186
|
+
* Events are stored in Redis Streams and flushed to Postgres on demand.
|
|
187
|
+
* Live deltas are published to `ab:live:{testId}` channels for realtime dashboards.
|
|
188
|
+
*/
|
|
189
|
+
declare function upstashAnalytics(options: UpstashAnalyticsOptions): ABTestAnalyticsAdapter & {
|
|
190
|
+
realtimeInstance: unknown;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
export { upstashAnalytics };
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { PgDatabase } from 'drizzle-orm/pg-core';
|
|
2
|
+
import { ConsentState } from '../../consent/index.js';
|
|
3
|
+
|
|
4
|
+
type ColumnScalarType = 'text' | 'boolean' | 'integer' | 'timestamp' | 'jsonb' | 'tsvector';
|
|
5
|
+
type ColumnType<EnumTarget extends string = string> = ColumnScalarType | {
|
|
6
|
+
enum: EnumTarget;
|
|
7
|
+
};
|
|
8
|
+
type DefaultValue = {
|
|
9
|
+
kind: 'literal';
|
|
10
|
+
value: boolean | number | string | string[] | Record<string, unknown>;
|
|
11
|
+
} | {
|
|
12
|
+
kind: 'sql';
|
|
13
|
+
value: string;
|
|
14
|
+
};
|
|
15
|
+
type ForeignKeyAction = 'cascade' | 'restrict' | 'no action' | 'set null' | 'set default';
|
|
16
|
+
type IndexUsing = 'btree' | 'gin';
|
|
17
|
+
type ColumnDefinition<ReferenceTarget extends string = string, EnumTarget extends string = string> = {
|
|
18
|
+
type: ColumnType<EnumTarget>;
|
|
19
|
+
columnName?: string;
|
|
20
|
+
notNull?: boolean;
|
|
21
|
+
primaryKey?: boolean;
|
|
22
|
+
unique?: boolean;
|
|
23
|
+
default?: DefaultValue;
|
|
24
|
+
defaultId?: boolean;
|
|
25
|
+
defaultIdPrefix?: string;
|
|
26
|
+
defaultNow?: boolean;
|
|
27
|
+
jsonType?: string;
|
|
28
|
+
references?: {
|
|
29
|
+
table: ReferenceTarget;
|
|
30
|
+
column: string;
|
|
31
|
+
onDelete?: ForeignKeyAction;
|
|
32
|
+
onUpdate?: ForeignKeyAction;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
type TableColumns<ReferenceTarget extends string = string, EnumTarget extends string = string> = Record<string, ColumnDefinition<ReferenceTarget, EnumTarget>>;
|
|
36
|
+
type IndexDefinition<ColumnName extends string> = {
|
|
37
|
+
columns: readonly ColumnName[];
|
|
38
|
+
unique?: boolean;
|
|
39
|
+
using?: IndexUsing;
|
|
40
|
+
where?: string;
|
|
41
|
+
};
|
|
42
|
+
type CompositePrimaryKey<ColumnName extends string> = {
|
|
43
|
+
columns: readonly ColumnName[];
|
|
44
|
+
};
|
|
45
|
+
type TableLevelForeignKey<ColumnName extends string = string> = {
|
|
46
|
+
columns: readonly ColumnName[];
|
|
47
|
+
foreignTable: string;
|
|
48
|
+
foreignColumns: readonly string[];
|
|
49
|
+
name?: string;
|
|
50
|
+
onDelete?: ForeignKeyAction;
|
|
51
|
+
onUpdate?: ForeignKeyAction;
|
|
52
|
+
};
|
|
53
|
+
type TableDefinition<Columns extends TableColumns = TableColumns, ReferenceTarget extends string = string, EnumTarget extends string = string> = {
|
|
54
|
+
tableName?: string;
|
|
55
|
+
indexPrefix?: string;
|
|
56
|
+
columns: Columns;
|
|
57
|
+
indexes?: Record<string, IndexDefinition<Extract<keyof Columns, string>>>;
|
|
58
|
+
compositePrimaryKey?: CompositePrimaryKey<Extract<keyof Columns, string>>;
|
|
59
|
+
foreignKeys?: TableLevelForeignKey<Extract<keyof Columns, string>>[];
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
type DrizzleInstance = PgDatabase<any, Record<string, unknown>, any>;
|
|
63
|
+
|
|
64
|
+
/** Where an event originated — a functional block instance. */
|
|
65
|
+
type CMSEventSource = {
|
|
66
|
+
/** Stable, author-assigned instance handle (the block's `trackingId`). */
|
|
67
|
+
handle?: string;
|
|
68
|
+
/** Block type that emitted the event (e.g. `'signupForm'`). */
|
|
69
|
+
type?: string;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* The decoupled, analytics-agnostic event core. A/B attribution is **optional**
|
|
73
|
+
* (`ab`), so non-A/B events (page views, form submits) are first-class and can
|
|
74
|
+
* flow to non-A/B sinks (GA4, GTM) without inventing a fake test/variant.
|
|
75
|
+
*
|
|
76
|
+
* `ABTestEvent` is the derived view where `ab` is mandatory.
|
|
77
|
+
*/
|
|
78
|
+
type CMSEvent = {
|
|
79
|
+
/**
|
|
80
|
+
* Optional id used as the storage row key. When absent, the sink mints one.
|
|
81
|
+
* Dedup behaviour is **sink-specific**: the postgres sink dedups on it via
|
|
82
|
+
* `ON CONFLICT (id) DO NOTHING`; the upstash sink does not. (A tenant-scoped,
|
|
83
|
+
* client-supplied idempotency key — distinct from this row key — arrives with
|
|
84
|
+
* the M3 client legs.)
|
|
85
|
+
*/
|
|
86
|
+
id?: string;
|
|
87
|
+
/** Canonical event name, e.g. `'impression' | 'conversion' | 'form_submit'`. */
|
|
88
|
+
name: string;
|
|
89
|
+
/**
|
|
90
|
+
* Optional: the anonymous Pattern A path stores NO identifier (variant comes
|
|
91
|
+
* from the URL/variant-cookie). Only set for the consent-gated unique-visitor /
|
|
92
|
+
* GA4 path. Stored as NULL when absent → excluded from unique-visitor counts.
|
|
93
|
+
*/
|
|
94
|
+
visitorId?: string;
|
|
95
|
+
anonymous: boolean;
|
|
96
|
+
/** A/B attribution. Absent for non-A/B analytics events. */
|
|
97
|
+
ab?: {
|
|
98
|
+
testId: string;
|
|
99
|
+
variantId: string;
|
|
100
|
+
};
|
|
101
|
+
/** Originating functional block instance, if any. */
|
|
102
|
+
source?: CMSEventSource;
|
|
103
|
+
/**
|
|
104
|
+
* Consent state under which the event was emitted (Consent Mode v2).
|
|
105
|
+
* Forwarded to consent-aware sinks (e.g. the M5 server-MP consent block). NOTE:
|
|
106
|
+
* M1 forwards but does NOT persist this — there is no consent column yet.
|
|
107
|
+
*/
|
|
108
|
+
consent?: ConsentState;
|
|
109
|
+
/**
|
|
110
|
+
* Funnel grouping id (M4): a client-minted id shared by the attempt + success
|
|
111
|
+
* legs of ONE interaction (e.g. a <TrackedForm> submit). Lets completion_rate
|
|
112
|
+
* pair them. Distinct from any storage/dedup key — it groups, it doesn't dedup.
|
|
113
|
+
*/
|
|
114
|
+
interactionId?: string;
|
|
115
|
+
/**
|
|
116
|
+
* GA4 stitching identifiers (M5 server-MP): the client reads them from the
|
|
117
|
+
* `_ga` / `_ga_<id>` cookies and sends them ONLY when analytics_storage is
|
|
118
|
+
* granted, so the server-side ga4ServerSink can attribute the Measurement
|
|
119
|
+
* Protocol hit to the same user/session (else GA4 shows `(not set)`). Absent on
|
|
120
|
+
* the anonymous consent-free path — its presence is what gates the GA4 forward.
|
|
121
|
+
*/
|
|
122
|
+
transport?: {
|
|
123
|
+
clientId?: string;
|
|
124
|
+
sessionId?: string;
|
|
125
|
+
engagementTimeMsec?: number;
|
|
126
|
+
};
|
|
127
|
+
metadata?: Record<string, unknown>;
|
|
128
|
+
timestamp: Date;
|
|
129
|
+
};
|
|
130
|
+
type AggregatedVariantResult = {
|
|
131
|
+
variantId: string;
|
|
132
|
+
variantName: string;
|
|
133
|
+
impressions: number;
|
|
134
|
+
conversions: number;
|
|
135
|
+
uniqueVisitors: number;
|
|
136
|
+
conversionRate: number;
|
|
137
|
+
/**
|
|
138
|
+
* Funnel (M4): total distinct interaction ids (each <TrackedForm> submit mints
|
|
139
|
+
* one) = the attempts. completionRate = distinct interactions that reached the
|
|
140
|
+
* goal event / attempts (computed by getResults when a goal is set).
|
|
141
|
+
*/
|
|
142
|
+
attempts: number;
|
|
143
|
+
completionRate: number;
|
|
144
|
+
eventBreakdown: Record<string, {
|
|
145
|
+
count: number;
|
|
146
|
+
uniqueVisitors: number;
|
|
147
|
+
distinctInteractions: number;
|
|
148
|
+
}>;
|
|
149
|
+
};
|
|
150
|
+
type AggregatedResults = {
|
|
151
|
+
testId: string;
|
|
152
|
+
variants: AggregatedVariantResult[];
|
|
153
|
+
totalImpressions: number;
|
|
154
|
+
totalConversions: number;
|
|
155
|
+
};
|
|
156
|
+
type ABTestAnalyticsAdapter = {
|
|
157
|
+
/** Adapter-specific Postgres tables to merge into the plugin schema. */
|
|
158
|
+
tables?: Record<string, TableDefinition>;
|
|
159
|
+
/** Called once during plugin init. Receives the Drizzle DB instance. */
|
|
160
|
+
init?(db: DrizzleInstance): Promise<void> | void;
|
|
161
|
+
/**
|
|
162
|
+
* Record a single event. Accepts any {@link CMSEvent}: A/B-attributed events
|
|
163
|
+
* (impression/conversion) carry `ab`, non-A/B analytics events (form_submit,
|
|
164
|
+
* page_view) omit it.
|
|
165
|
+
*/
|
|
166
|
+
track(event: CMSEvent): Promise<void>;
|
|
167
|
+
/** Query aggregated results for a test. */
|
|
168
|
+
query(testId: string, options?: {
|
|
169
|
+
from?: Date;
|
|
170
|
+
to?: Date;
|
|
171
|
+
}): Promise<AggregatedResults>;
|
|
172
|
+
/** Optional batch flush (e.g. Upstash -> Postgres). */
|
|
173
|
+
flush?(testId?: string): Promise<{
|
|
174
|
+
flushed: number;
|
|
175
|
+
}>;
|
|
176
|
+
};
|
|
177
|
+
type UpstashAnalyticsOptions = {
|
|
178
|
+
url: string;
|
|
179
|
+
token: string;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Upstash Redis Stream adapter with batch flush and realtime delta publishing.
|
|
184
|
+
*
|
|
185
|
+
* Requires `@upstash/redis` and `@upstash/realtime` as peer dependencies.
|
|
186
|
+
* Events are stored in Redis Streams and flushed to Postgres on demand.
|
|
187
|
+
* Live deltas are published to `ab:live:{testId}` channels for realtime dashboards.
|
|
188
|
+
*/
|
|
189
|
+
declare function upstashAnalytics(options: UpstashAnalyticsOptions): ABTestAnalyticsAdapter & {
|
|
190
|
+
realtimeInstance: unknown;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
export { upstashAnalytics };
|