@drip-apex/sdk 0.1.0
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/LICENSE +21 -0
- package/README.md +46 -0
- package/dist/_headers +3 -0
- package/dist/bundle-report.json +564 -0
- package/dist/drip.debug.js +2 -0
- package/dist/drip.debug.js.map +1 -0
- package/dist/drip.heatmap.js +2 -0
- package/dist/drip.heatmap.js.map +1 -0
- package/dist/drip.js +2 -0
- package/dist/drip.js.map +1 -0
- package/dist/drip.live.js +2 -0
- package/dist/drip.live.js.map +1 -0
- package/dist/editor.js +480 -0
- package/dist/editor.js.map +7 -0
- package/dist/index.mjs +645 -0
- package/dist/index.mjs.map +7 -0
- package/dist/metafiles/drip.debug.meta.json +973 -0
- package/dist/metafiles/drip.heatmap.meta.json +918 -0
- package/dist/metafiles/drip.live.meta.json +918 -0
- package/dist/metafiles/drip.meta.json +973 -0
- package/dist/metafiles/editor.meta.json +321 -0
- package/dist/metafiles/index.meta.json +1039 -0
- package/dist/metafiles/index.mjs.meta.json +78 -0
- package/dist/react/index.js +16973 -0
- package/dist/react/index.js.map +7 -0
- package/dist/sri.json +27 -0
- package/dist/types/analytics.d.ts +15 -0
- package/dist/types/antiflicker.d.ts +14 -0
- package/dist/types/assignment-store.d.ts +30 -0
- package/dist/types/audit.d.ts +9 -0
- package/dist/types/bucket.d.ts +2 -0
- package/dist/types/bucketing-cleanup.d.ts +5 -0
- package/dist/types/commerce-gift.d.ts +41 -0
- package/dist/types/commerce-price.d.ts +22 -0
- package/dist/types/commerce-shipping.d.ts +28 -0
- package/dist/types/css.d.ts +5 -0
- package/dist/types/cwv.d.ts +10 -0
- package/dist/types/data-layer-normalize.d.ts +1 -0
- package/dist/types/debug.d.ts +23 -0
- package/dist/types/define.d.ts +87 -0
- package/dist/types/devtools.d.ts +32 -0
- package/dist/types/dom-trigger.d.ts +3 -0
- package/dist/types/editor/api.d.ts +37 -0
- package/dist/types/editor/artifacts.d.ts +15 -0
- package/dist/types/editor/command-runtime.d.ts +48 -0
- package/dist/types/editor/controller.d.ts +97 -0
- package/dist/types/editor/device-preview.d.ts +23 -0
- package/dist/types/editor/index.d.ts +9 -0
- package/dist/types/editor/operator.d.ts +39 -0
- package/dist/types/editor/picker.d.ts +23 -0
- package/dist/types/editor/selector.d.ts +2 -0
- package/dist/types/editor/types.d.ts +55 -0
- package/dist/types/editor/view-kit.d.ts +33 -0
- package/dist/types/editor/view.d.ts +5 -0
- package/dist/types/editor-bootstrap.d.ts +37 -0
- package/dist/types/exclusion-groups.d.ts +59 -0
- package/dist/types/exposure-signals.d.ts +7 -0
- package/dist/types/flags.d.ts +65 -0
- package/dist/types/goals.d.ts +16 -0
- package/dist/types/guards.d.ts +39 -0
- package/dist/types/handover.d.ts +10 -0
- package/dist/types/hash.d.ts +19 -0
- package/dist/types/heatmap.d.ts +8 -0
- package/dist/types/hideflicker.d.ts +15 -0
- package/dist/types/hydration.d.ts +11 -0
- package/dist/types/index.d.ts +144 -0
- package/dist/types/js.d.ts +6 -0
- package/dist/types/module.d.ts +4 -0
- package/dist/types/money.d.ts +14 -0
- package/dist/types/mutate.d.ts +34 -0
- package/dist/types/observer-mute.d.ts +3 -0
- package/dist/types/pii-keys.d.ts +1 -0
- package/dist/types/prerequisites.d.ts +12 -0
- package/dist/types/qa-assistant-state.d.ts +68 -0
- package/dist/types/qa-assistant-view.d.ts +6 -0
- package/dist/types/qa-assistant.d.ts +9 -0
- package/dist/types/qa-toolbar.d.ts +19 -0
- package/dist/types/react/index.d.ts +84 -0
- package/dist/types/reassert-sentinel.d.ts +6 -0
- package/dist/types/router.d.ts +9 -0
- package/dist/types/runtime-tools.d.ts +50 -0
- package/dist/types/runtime.d.ts +22 -0
- package/dist/types/safe-regex.d.ts +6 -0
- package/dist/types/sanitize.d.ts +5 -0
- package/dist/types/semantic.d.ts +7 -0
- package/dist/types/shopify-cart-events.d.ts +7 -0
- package/dist/types/shopify-cart.d.ts +62 -0
- package/dist/types/signals.d.ts +31 -0
- package/dist/types/surface-signals.d.ts +6 -0
- package/dist/types/targeting-diagnostics.d.ts +5 -0
- package/dist/types/targeting.d.ts +50 -0
- package/dist/types/test-fixtures/commerce.d.ts +12 -0
- package/dist/types/track.d.ts +79 -0
- package/dist/types/types.d.ts +859 -0
- package/dist/types/wait.d.ts +7 -0
- package/package.json +74 -0
- package/src/analytics.ts +146 -0
- package/src/antiflicker.test.ts +127 -0
- package/src/antiflicker.ts +147 -0
- package/src/assignment-store.test.ts +171 -0
- package/src/assignment-store.ts +208 -0
- package/src/audit.test.ts +17 -0
- package/src/audit.ts +362 -0
- package/src/bucket.test.ts +65 -0
- package/src/bucket.ts +18 -0
- package/src/bucketing-cleanup.test.ts +30 -0
- package/src/bucketing-cleanup.ts +72 -0
- package/src/bucketing-parity.test.ts +41 -0
- package/src/commerce-analytics-lifecycle.test.ts +1416 -0
- package/src/commerce-config-lifecycle.test.ts +274 -0
- package/src/commerce-epoch-lifecycle.test.ts +62 -0
- package/src/commerce-gift.test.ts +1293 -0
- package/src/commerce-gift.ts +979 -0
- package/src/commerce-offers-display.test.ts +154 -0
- package/src/commerce-price.test.ts +1145 -0
- package/src/commerce-price.ts +941 -0
- package/src/commerce-revocation-lifecycle.test.ts +78 -0
- package/src/commerce-shipping.test.ts +630 -0
- package/src/commerce-shipping.ts +447 -0
- package/src/config-fetch.test.ts +325 -0
- package/src/css.ts +54 -0
- package/src/cwv.test.ts +95 -0
- package/src/cwv.ts +129 -0
- package/src/data-layer-normalize.ts +21 -0
- package/src/debug.test.ts +83 -0
- package/src/debug.ts +138 -0
- package/src/define.test.ts +327 -0
- package/src/define.ts +208 -0
- package/src/devtools.ts +582 -0
- package/src/dom-trigger.test.ts +70 -0
- package/src/dom-trigger.ts +55 -0
- package/src/editor/api.test.ts +154 -0
- package/src/editor/api.ts +185 -0
- package/src/editor/artifacts.test.ts +57 -0
- package/src/editor/artifacts.ts +115 -0
- package/src/editor/command-runtime.test.ts +225 -0
- package/src/editor/command-runtime.ts +572 -0
- package/src/editor/controller.test.ts +371 -0
- package/src/editor/controller.ts +596 -0
- package/src/editor/device-preview.test.ts +60 -0
- package/src/editor/device-preview.ts +120 -0
- package/src/editor/index.test.ts +161 -0
- package/src/editor/index.ts +169 -0
- package/src/editor/operator.test.ts +148 -0
- package/src/editor/operator.ts +256 -0
- package/src/editor/picker.test.ts +58 -0
- package/src/editor/picker.ts +152 -0
- package/src/editor/selector.test.ts +44 -0
- package/src/editor/selector.ts +110 -0
- package/src/editor/types.ts +92 -0
- package/src/editor/view-kit.ts +162 -0
- package/src/editor/view.test.ts +535 -0
- package/src/editor/view.ts +1395 -0
- package/src/editor-bootstrap.test.ts +116 -0
- package/src/editor-bootstrap.ts +213 -0
- package/src/event-schema-contract.test.ts +35 -0
- package/src/exclusion-groups.test.ts +383 -0
- package/src/exclusion-groups.ts +270 -0
- package/src/exposure-signals.test.ts +182 -0
- package/src/exposure-signals.ts +110 -0
- package/src/flags.test.ts +322 -0
- package/src/flags.ts +201 -0
- package/src/goals.test.ts +42 -0
- package/src/goals.ts +200 -0
- package/src/guards.test.ts +249 -0
- package/src/guards.ts +250 -0
- package/src/handover-cache-order.test.ts +32 -0
- package/src/handover.test.ts +349 -0
- package/src/handover.ts +309 -0
- package/src/hash.ts +214 -0
- package/src/heatmap.test.ts +305 -0
- package/src/heatmap.ts +373 -0
- package/src/hideflicker.ts +21 -0
- package/src/hydration.test.ts +252 -0
- package/src/hydration.ts +203 -0
- package/src/index.ts +7513 -0
- package/src/init-empty.test.ts +5572 -0
- package/src/js.test.ts +26 -0
- package/src/js.ts +61 -0
- package/src/module.ts +50 -0
- package/src/money.test.ts +35 -0
- package/src/money.ts +53 -0
- package/src/mutate.test.ts +304 -0
- package/src/mutate.ts +469 -0
- package/src/observer-mute.test.ts +88 -0
- package/src/observer-mute.ts +45 -0
- package/src/page-trigger.test.ts +84 -0
- package/src/personalization.test.ts +68 -0
- package/src/pii-keys.ts +17 -0
- package/src/prerequisites.test.ts +39 -0
- package/src/prerequisites.ts +94 -0
- package/src/qa-assistant-state.test.ts +471 -0
- package/src/qa-assistant-state.ts +505 -0
- package/src/qa-assistant-view.test.ts +185 -0
- package/src/qa-assistant-view.ts +768 -0
- package/src/qa-assistant.ts +40 -0
- package/src/qa-toolbar.ts +444 -0
- package/src/react/index.test.tsx +177 -0
- package/src/react/index.tsx +252 -0
- package/src/reassert-sentinel.test.ts +314 -0
- package/src/reassert-sentinel.ts +191 -0
- package/src/router.test.ts +78 -0
- package/src/router.ts +92 -0
- package/src/runtime-tools.test.ts +112 -0
- package/src/runtime-tools.ts +403 -0
- package/src/runtime.test.ts +217 -0
- package/src/runtime.ts +268 -0
- package/src/safe-regex.ts +35 -0
- package/src/sanitize.ts +34 -0
- package/src/semantic.ts +245 -0
- package/src/shopify-cart-events.test.ts +76 -0
- package/src/shopify-cart-events.ts +115 -0
- package/src/shopify-cart.test.ts +2823 -0
- package/src/shopify-cart.ts +1256 -0
- package/src/signals.ts +226 -0
- package/src/surface-signals.test.ts +83 -0
- package/src/surface-signals.ts +165 -0
- package/src/targeting-diagnostics.ts +560 -0
- package/src/targeting.detailed.test.ts +443 -0
- package/src/targeting.test.ts +1054 -0
- package/src/targeting.ts +1024 -0
- package/src/test-fixtures/commerce.ts +65 -0
- package/src/track.test.ts +817 -0
- package/src/track.ts +519 -0
- package/src/types.ts +991 -0
- package/src/wait.test.ts +96 -0
- package/src/wait.ts +238 -0
package/src/define.ts
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* defineExperiment — typed experiment definitions with validation.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* const hero = defineExperiment({
|
|
6
|
+
* slug: "hero-banner-v2",
|
|
7
|
+
* name: "Hero Banner V2",
|
|
8
|
+
* variations: [
|
|
9
|
+
* { id: "control", weight: 0.5, mutations: [], isControl: true },
|
|
10
|
+
* { id: "variant-a", weight: 0.5, mutations: [{ selector: ".hero h1", action: "text", text: "New headline" }] },
|
|
11
|
+
* ],
|
|
12
|
+
* targeting: { url: [{ type: "simple", pattern: "/" }] },
|
|
13
|
+
* goals: [{ id: "cta-click", type: "click", selector: ".hero-cta" }],
|
|
14
|
+
* });
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { Experiment, Goal, Mutation, Targeting, Variation } from "./types";
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Input types — looser than runtime types for developer ergonomics
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
export interface ExperimentDefinition {
|
|
24
|
+
/** Unique slug for GitOps upsert. Required for code-first experiments. */
|
|
25
|
+
slug: string;
|
|
26
|
+
/** Human-readable name. */
|
|
27
|
+
name: string;
|
|
28
|
+
/** Traffic allocation percentage (0–1). Default: 1. */
|
|
29
|
+
trafficAllocation?: number;
|
|
30
|
+
/** Targeting rules. */
|
|
31
|
+
targeting?: Targeting;
|
|
32
|
+
/** Hash attribute override (default: visitor cookie). */
|
|
33
|
+
hashAttribute?: string;
|
|
34
|
+
/** Variation definitions. At least two required. */
|
|
35
|
+
variations: VariationDefinition[];
|
|
36
|
+
/** Goal definitions for conversion tracking. */
|
|
37
|
+
goals?: GoalDefinition[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface VariationDefinition {
|
|
41
|
+
/** Variation identifier (used as slug within the experiment). */
|
|
42
|
+
id: string;
|
|
43
|
+
/** Display name. Falls back to id if omitted. */
|
|
44
|
+
name?: string;
|
|
45
|
+
/** Traffic weight (0–1). All weights should sum to 1. */
|
|
46
|
+
weight: number;
|
|
47
|
+
/** DOM mutations to apply. Default: []. */
|
|
48
|
+
mutations?: Mutation[];
|
|
49
|
+
/** Optional typed value returned by feature-flag helpers. */
|
|
50
|
+
value?: unknown;
|
|
51
|
+
/** Custom CSS injected when this variation is active. */
|
|
52
|
+
css?: string;
|
|
53
|
+
/** Custom JavaScript executed when this variation is active. */
|
|
54
|
+
js?: string;
|
|
55
|
+
/** Reset JavaScript executed on SPA navigation away. */
|
|
56
|
+
resetJs?: string;
|
|
57
|
+
/** External CSS artifact URL loaded when this variation is active. */
|
|
58
|
+
externalCssUrl?: string;
|
|
59
|
+
/** External JS artifact URL loaded when this variation is active. */
|
|
60
|
+
externalJsUrl?: string;
|
|
61
|
+
/** Mark as control variation. Exactly one variation should be control. */
|
|
62
|
+
isControl?: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface GoalDefinition {
|
|
66
|
+
/** Goal identifier (used as slug). */
|
|
67
|
+
id: string;
|
|
68
|
+
/** Goal type. */
|
|
69
|
+
type: "click" | "pageview" | "custom" | "revenue";
|
|
70
|
+
/** CSS selector for click/element goals. */
|
|
71
|
+
selector?: string;
|
|
72
|
+
/** URL pattern for pageview goals. */
|
|
73
|
+
urlPattern?: string;
|
|
74
|
+
/** Only count once per session. Default: false. */
|
|
75
|
+
countOnce?: boolean;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
// Validation errors
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
export class ExperimentDefinitionError extends Error {
|
|
83
|
+
public readonly issues: string[];
|
|
84
|
+
|
|
85
|
+
constructor(slug: string, issues: string[]) {
|
|
86
|
+
super(`Invalid experiment "${slug}": ${issues.join("; ")}`);
|
|
87
|
+
this.name = "ExperimentDefinitionError";
|
|
88
|
+
this.issues = issues;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// Validation
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
function validate(def: ExperimentDefinition): string[] {
|
|
97
|
+
const issues: string[] = [];
|
|
98
|
+
|
|
99
|
+
if (!def.slug || typeof def.slug !== "string") {
|
|
100
|
+
issues.push("slug is required and must be a non-empty string");
|
|
101
|
+
} else if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(def.slug) && def.slug.length > 1) {
|
|
102
|
+
issues.push("slug must be lowercase alphanumeric with hyphens (e.g. 'hero-banner-v2')");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!def.name || typeof def.name !== "string") {
|
|
106
|
+
issues.push("name is required");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!Array.isArray(def.variations) || def.variations.length < 2) {
|
|
110
|
+
issues.push("at least 2 variations are required");
|
|
111
|
+
} else {
|
|
112
|
+
const controls = def.variations.filter((v) => v.isControl);
|
|
113
|
+
if (controls.length === 0) {
|
|
114
|
+
issues.push("exactly one variation must be marked as control (isControl: true)");
|
|
115
|
+
} else if (controls.length > 1) {
|
|
116
|
+
issues.push("only one variation can be the control");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const totalWeight = def.variations.reduce((sum, v) => sum + v.weight, 0);
|
|
120
|
+
if (Math.abs(totalWeight - 1) > 0.01) {
|
|
121
|
+
issues.push(`variation weights should sum to 1 (got ${totalWeight.toFixed(3)})`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const ids = new Set<string>();
|
|
125
|
+
for (const v of def.variations) {
|
|
126
|
+
if (!v.id) issues.push("every variation must have an id");
|
|
127
|
+
if (ids.has(v.id)) issues.push(`duplicate variation id: "${v.id}"`);
|
|
128
|
+
ids.add(v.id);
|
|
129
|
+
if (v.weight < 0 || v.weight > 1) issues.push(`variation "${v.id}" weight must be between 0 and 1`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (def.trafficAllocation !== undefined) {
|
|
134
|
+
if (def.trafficAllocation < 0 || def.trafficAllocation > 1) {
|
|
135
|
+
issues.push("trafficAllocation must be between 0 and 1");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (def.goals) {
|
|
140
|
+
const goalIds = new Set<string>();
|
|
141
|
+
for (const g of def.goals) {
|
|
142
|
+
if (!g.id) issues.push("every goal must have an id");
|
|
143
|
+
if (goalIds.has(g.id)) issues.push(`duplicate goal id: "${g.id}"`);
|
|
144
|
+
goalIds.add(g.id);
|
|
145
|
+
|
|
146
|
+
if (g.type === "click" && !g.selector) {
|
|
147
|
+
issues.push(`click goal "${g.id}" requires a selector`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return issues;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
// defineExperiment
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Define a typed, validated experiment configuration.
|
|
161
|
+
*
|
|
162
|
+
* Returns a frozen Experiment object ready for use with `init({ experiments: [...] })`.
|
|
163
|
+
* Throws `ExperimentDefinitionError` if the definition is invalid.
|
|
164
|
+
*/
|
|
165
|
+
export function defineExperiment(def: ExperimentDefinition): Experiment & { slug: string } {
|
|
166
|
+
const issues = validate(def);
|
|
167
|
+
if (issues.length > 0) {
|
|
168
|
+
throw new ExperimentDefinitionError(def.slug ?? "(unnamed)", issues);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const experiment: Experiment & { slug: string } = {
|
|
172
|
+
id: def.slug, // slug is used as the id for code-first experiments
|
|
173
|
+
slug: def.slug,
|
|
174
|
+
name: def.name,
|
|
175
|
+
trafficAllocation: def.trafficAllocation ?? 1,
|
|
176
|
+
targeting: def.targeting,
|
|
177
|
+
hashAttribute: def.hashAttribute,
|
|
178
|
+
variations: def.variations.map((v) => ({
|
|
179
|
+
id: v.id,
|
|
180
|
+
name: v.name,
|
|
181
|
+
weight: v.weight,
|
|
182
|
+
mutations: v.mutations ?? [],
|
|
183
|
+
value: v.value,
|
|
184
|
+
css: v.css,
|
|
185
|
+
js: v.js,
|
|
186
|
+
resetJs: v.resetJs,
|
|
187
|
+
externalCssUrl: v.externalCssUrl,
|
|
188
|
+
externalJsUrl: v.externalJsUrl,
|
|
189
|
+
isControl: v.isControl ?? false,
|
|
190
|
+
})),
|
|
191
|
+
goals: def.goals?.map((g) => ({
|
|
192
|
+
id: g.id,
|
|
193
|
+
type: g.type,
|
|
194
|
+
selector: g.selector,
|
|
195
|
+
urlPattern: g.urlPattern,
|
|
196
|
+
countOnce: g.countOnce,
|
|
197
|
+
})),
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
return Object.freeze(experiment) as Experiment & { slug: string };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Define multiple experiments at once. Validates all, throws on first invalid.
|
|
205
|
+
*/
|
|
206
|
+
export function defineExperiments(defs: ExperimentDefinition[]): Array<Experiment & { slug: string }> {
|
|
207
|
+
return defs.map(defineExperiment);
|
|
208
|
+
}
|