@drip-apex/sdk 0.1.0 → 0.2.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/README.md +5 -0
- package/dist/bundle-report.json +584 -226
- package/dist/define.cjs +123 -0
- package/dist/define.cjs.map +7 -0
- package/dist/define.mjs +102 -0
- package/dist/define.mjs.map +7 -0
- package/dist/drip.cjs +19262 -0
- package/dist/drip.cjs.map +7 -0
- package/dist/drip.debug.js +1 -1
- package/dist/drip.debug.js.map +1 -1
- package/dist/drip.heatmap.js +1 -1
- package/dist/drip.heatmap.js.map +1 -1
- package/dist/drip.js +1 -1
- package/dist/drip.js.map +1 -1
- package/dist/drip.live.js +1 -1
- package/dist/drip.live.js.map +1 -1
- package/dist/drip.mjs +19246 -0
- package/dist/drip.mjs.map +7 -0
- package/dist/drip.replay-recorder.js +2 -0
- package/dist/drip.replay-recorder.js.map +1 -0
- package/dist/editor.js +147 -142
- package/dist/editor.js.map +4 -4
- package/dist/flags.cjs +1788 -0
- package/dist/flags.cjs.map +7 -0
- package/dist/flags.mjs +1765 -0
- package/dist/flags.mjs.map +7 -0
- package/dist/index.mjs +12 -0
- package/dist/index.mjs.map +2 -2
- package/dist/metafiles/define.cjs.meta.json +28 -0
- package/dist/metafiles/define.mjs.meta.json +32 -0
- package/dist/metafiles/drip.cjs.meta.json +3193 -0
- package/dist/metafiles/drip.debug.meta.json +2859 -574
- package/dist/metafiles/drip.heatmap.meta.json +2732 -460
- package/dist/metafiles/drip.live.meta.json +2732 -460
- package/dist/metafiles/drip.meta.json +2859 -574
- package/dist/metafiles/drip.mjs.meta.json +3235 -0
- package/dist/metafiles/drip.replay-recorder.meta.json +82 -0
- package/dist/metafiles/editor.meta.json +67 -27
- package/dist/metafiles/flags.cjs.meta.json +220 -0
- package/dist/metafiles/flags.mjs.meta.json +225 -0
- package/dist/metafiles/index.meta.json +2717 -491
- package/dist/metafiles/index.mjs.meta.json +14 -4
- package/dist/react/index.js +16621 -13539
- package/dist/react/index.js.map +4 -4
- package/dist/sri.json +20 -15
- package/dist/types/activation-runtime.d.ts +91 -0
- package/dist/types/analytics-runtime.d.ts +166 -0
- package/dist/types/antiflicker.d.ts +9 -3
- package/dist/types/assignment-cookie-sync.d.ts +31 -0
- package/dist/types/assignment-runtime.d.ts +257 -0
- package/dist/types/assignment-store.d.ts +4 -8
- package/dist/types/bucket.d.ts +1 -2
- package/dist/types/compact-store.d.ts +1 -0
- package/dist/types/config-runtime.d.ts +313 -0
- package/dist/types/consent-evaluation.d.ts +27 -0
- package/dist/types/css.d.ts +5 -3
- package/dist/types/debug-loader.d.ts +2 -0
- package/dist/types/decide-assignments.d.ts +31 -0
- package/dist/types/delivery-timing.d.ts +8 -0
- package/dist/types/early-interaction-capture.d.ts +79 -0
- package/dist/types/early-reassert.d.ts +32 -0
- package/dist/types/edge-runtime.d.ts +11 -0
- package/dist/types/editor/api.d.ts +3 -0
- package/dist/types/editor/controller.d.ts +3 -1
- package/dist/types/editor/publication-result.d.ts +11 -0
- package/dist/types/editor/types.d.ts +2 -1
- package/dist/types/editor/view-kit.d.ts +1 -1
- package/dist/types/endpoint.d.ts +8 -0
- package/dist/types/exclusion-groups.d.ts +4 -7
- package/dist/types/flags.d.ts +2 -0
- package/dist/types/goal-eligibility.d.ts +4 -0
- package/dist/types/goals.d.ts +10 -1
- package/dist/types/guards.d.ts +7 -7
- package/dist/types/handover.d.ts +12 -1
- package/dist/types/hash.d.ts +8 -4
- package/dist/types/hydration.d.ts +2 -1
- package/dist/types/index.d.ts +24 -128
- package/dist/types/init-test-helpers.d.ts +48 -0
- package/dist/types/inline-bootstrap.d.ts +26 -0
- package/dist/types/js.d.ts +3 -1
- package/dist/types/money.d.ts +12 -0
- package/dist/types/mutate.d.ts +45 -6
- package/dist/types/performance.d.ts +2 -0
- package/dist/types/persistence-decoders.d.ts +12 -0
- package/dist/types/public-api.d.ts +209 -0
- package/dist/types/qa-assistant-state.d.ts +34 -0
- package/dist/types/react/index.d.ts +28 -3
- package/dist/types/reassert-sentinel.d.ts +6 -0
- package/dist/types/redirect.d.ts +48 -0
- package/dist/types/replay-recorder-entry.d.ts +1 -0
- package/dist/types/replay-signal-state.d.ts +6 -0
- package/dist/types/replay-signals.d.ts +13 -0
- package/dist/types/replay.d.ts +110 -0
- package/dist/types/router.d.ts +12 -2
- package/dist/types/runtime-state.d.ts +166 -0
- package/dist/types/sanitize.d.ts +1 -0
- package/dist/types/shadow.d.ts +3 -0
- package/dist/types/shopify-cart.d.ts +25 -1
- package/dist/types/storefront-vitals.d.ts +24 -0
- package/dist/types/targeting-diagnostics.d.ts +1 -0
- package/dist/types/targeting.d.ts +16 -2
- package/dist/types/track.d.ts +15 -1
- package/dist/types/types.d.ts +115 -9
- package/dist/types/wait.d.ts +14 -1
- package/dist/types/weekday-schedule.d.ts +3 -0
- package/package.json +17 -8
- package/src/activation-runtime.ts +2737 -0
- package/src/analytics-runtime.ts +2175 -0
- package/src/antiflicker.test.ts +216 -44
- package/src/antiflicker.ts +103 -28
- package/src/artifact-concurrency.test.ts +1091 -0
- package/src/assignment-cookie-sync.test.ts +202 -0
- package/src/assignment-cookie-sync.ts +189 -0
- package/src/assignment-runtime.ts +3204 -0
- package/src/assignment-store.test.ts +56 -0
- package/src/assignment-store.ts +7 -38
- package/src/bucket.ts +3 -18
- package/src/commerce-analytics-lifecycle.test.ts +75 -15
- package/src/commerce-config-lifecycle.test.ts +81 -1
- package/src/commerce-price-preserve.test.ts +133 -0
- package/src/commerce-price.test.ts +97 -0
- package/src/commerce-price.ts +74 -20
- package/src/commerce-shipping.test.ts +53 -0
- package/src/commerce-shipping.ts +5 -11
- package/src/compact-store.ts +15 -0
- package/src/config-fetch.test.ts +480 -9
- package/src/config-runtime.ts +2070 -0
- package/src/consent-evaluation-cookiescript.test.ts +50 -0
- package/src/consent-evaluation.ts +138 -0
- package/src/css.test.ts +30 -0
- package/src/css.ts +120 -6
- package/src/cwv.test.ts +60 -77
- package/src/debug-loader.test.ts +214 -0
- package/src/debug-loader.ts +41 -0
- package/src/decide-assignments.test.ts +196 -0
- package/src/decide-assignments.ts +204 -0
- package/src/delivery-timing.test.ts +104 -0
- package/src/delivery-timing.ts +89 -0
- package/src/early-interaction-capture.test.ts +451 -0
- package/src/early-interaction-capture.ts +230 -0
- package/src/early-reassert.test.ts +207 -0
- package/src/early-reassert.ts +152 -0
- package/src/edge-adoption.test.ts +435 -0
- package/src/edge-runtime.ts +30 -0
- package/src/editor/api.test.ts +109 -0
- package/src/editor/api.ts +34 -3
- package/src/editor/controller.test.ts +297 -14
- package/src/editor/controller.ts +69 -5
- package/src/editor/index.test.ts +2 -0
- package/src/editor/index.ts +1 -0
- package/src/editor/publication-result.ts +58 -0
- package/src/editor/types.ts +10 -1
- package/src/editor/view-kit.ts +2 -1
- package/src/editor/view.test.ts +108 -0
- package/src/editor/view.ts +49 -7
- package/src/endpoint.test.ts +15 -0
- package/src/endpoint.ts +10 -0
- package/src/exclusion-groups.ts +8 -18
- package/src/flags.test.ts +88 -1
- package/src/flags.ts +29 -29
- package/src/global-runtime-reconciler.test.ts +271 -0
- package/src/goal-eligibility.test.ts +29 -0
- package/src/goal-eligibility.ts +17 -0
- package/src/goals.test.ts +144 -1
- package/src/goals.ts +97 -18
- package/src/guards.test.ts +99 -1
- package/src/guards.ts +34 -15
- package/src/handover-cache-order.test.ts +6 -3
- package/src/handover.test.ts +154 -8
- package/src/handover.ts +122 -12
- package/src/hash.ts +29 -54
- package/src/holdout.test.ts +26 -0
- package/src/hydration.test.ts +73 -2
- package/src/hydration.ts +18 -9
- package/src/index.ts +130 -7287
- package/src/init-analytics.test.ts +1694 -0
- package/src/init-anti-flicker.test.ts +1211 -0
- package/src/init-cart-attributes.test.ts +2003 -0
- package/src/init-config-resolution.test.ts +999 -0
- package/src/init-consent.test.ts +971 -0
- package/src/init-empty.test.ts +11 -5406
- package/src/init-integrations.test.ts +846 -0
- package/src/init-page-activation.test.ts +5064 -0
- package/src/init-qa-loading.test.ts +740 -0
- package/src/init-redirects.test.ts +2156 -0
- package/src/init-test-helpers.ts +287 -0
- package/src/inline-bootstrap.test.ts +184 -0
- package/src/inline-bootstrap.ts +73 -0
- package/src/js.test.ts +51 -1
- package/src/js.ts +86 -2
- package/src/money.test.ts +55 -1
- package/src/money.ts +104 -2
- package/src/money.web-parity.contract.test.ts +85 -0
- package/src/mutate.test.ts +474 -10
- package/src/mutate.ts +739 -230
- package/src/page-trigger.test.ts +218 -8
- package/src/performance.ts +10 -0
- package/src/persistence-decoders.ts +18 -0
- package/src/public-api.ts +1828 -0
- package/src/qa-assistant-state.test.ts +249 -3
- package/src/qa-assistant-state.ts +146 -6
- package/src/qa-assistant-view.test.ts +220 -2
- package/src/qa-assistant-view.ts +102 -5
- package/src/react/index.test.tsx +37 -0
- package/src/react/index.tsx +19 -3
- package/src/reassert-sentinel.test.ts +155 -1
- package/src/reassert-sentinel.ts +168 -8
- package/src/redirect.test.ts +541 -0
- package/src/redirect.ts +572 -0
- package/src/replay-recorder-entry.ts +115 -0
- package/src/replay-signal-state.ts +100 -0
- package/src/replay-signals.test.ts +249 -0
- package/src/replay-signals.ts +151 -0
- package/src/replay.test.ts +764 -0
- package/src/replay.ts +514 -0
- package/src/router.test.ts +61 -2
- package/src/router.ts +50 -12
- package/src/runtime-state.ts +507 -0
- package/src/runtime.test.ts +174 -7
- package/src/runtime.ts +88 -14
- package/src/sanitize.test.ts +34 -0
- package/src/sanitize.ts +23 -12
- package/src/shadow.test.ts +187 -0
- package/src/shadow.ts +99 -0
- package/src/shopify-cart.test.ts +733 -23
- package/src/shopify-cart.ts +402 -49
- package/src/storefront-vitals.test.ts +217 -0
- package/src/storefront-vitals.ts +206 -0
- package/src/surface-signals.ts +6 -0
- package/src/targeting-conformance.test.ts +9 -0
- package/src/targeting-diagnostics.ts +38 -8
- package/src/targeting-parity-corpus.json +559 -0
- package/src/targeting-parity-corpus.test.ts +18 -0
- package/src/targeting.detailed.test.ts +105 -0
- package/src/targeting.test.ts +211 -0
- package/src/targeting.ts +88 -207
- package/src/track.test.ts +147 -0
- package/src/track.ts +125 -26
- package/src/types.ts +120 -13
- package/src/wait.test.ts +23 -0
- package/src/wait.ts +129 -13
- package/src/weekday-delivery.test.ts +56 -0
- package/src/weekday-schedule-parity-corpus.json +92 -0
- package/src/weekday-schedule.test.ts +69 -0
- package/src/weekday-schedule.ts +20 -0
package/dist/define.cjs
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/define.ts
|
|
21
|
+
var define_exports = {};
|
|
22
|
+
__export(define_exports, {
|
|
23
|
+
ExperimentDefinitionError: () => ExperimentDefinitionError,
|
|
24
|
+
defineExperiment: () => defineExperiment,
|
|
25
|
+
defineExperiments: () => defineExperiments
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(define_exports);
|
|
28
|
+
var ExperimentDefinitionError = class extends Error {
|
|
29
|
+
constructor(slug, issues) {
|
|
30
|
+
super(`Invalid experiment "${slug}": ${issues.join("; ")}`);
|
|
31
|
+
this.name = "ExperimentDefinitionError";
|
|
32
|
+
this.issues = issues;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
function validate(def) {
|
|
36
|
+
const issues = [];
|
|
37
|
+
if (!def.slug || typeof def.slug !== "string") {
|
|
38
|
+
issues.push("slug is required and must be a non-empty string");
|
|
39
|
+
} else if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(def.slug) && def.slug.length > 1) {
|
|
40
|
+
issues.push("slug must be lowercase alphanumeric with hyphens (e.g. 'hero-banner-v2')");
|
|
41
|
+
}
|
|
42
|
+
if (!def.name || typeof def.name !== "string") {
|
|
43
|
+
issues.push("name is required");
|
|
44
|
+
}
|
|
45
|
+
if (!Array.isArray(def.variations) || def.variations.length < 2) {
|
|
46
|
+
issues.push("at least 2 variations are required");
|
|
47
|
+
} else {
|
|
48
|
+
const controls = def.variations.filter((v) => v.isControl);
|
|
49
|
+
if (controls.length === 0) {
|
|
50
|
+
issues.push("exactly one variation must be marked as control (isControl: true)");
|
|
51
|
+
} else if (controls.length > 1) {
|
|
52
|
+
issues.push("only one variation can be the control");
|
|
53
|
+
}
|
|
54
|
+
const totalWeight = def.variations.reduce((sum, v) => sum + v.weight, 0);
|
|
55
|
+
if (Math.abs(totalWeight - 1) > 0.01) {
|
|
56
|
+
issues.push(`variation weights should sum to 1 (got ${totalWeight.toFixed(3)})`);
|
|
57
|
+
}
|
|
58
|
+
const ids = /* @__PURE__ */ new Set();
|
|
59
|
+
for (const v of def.variations) {
|
|
60
|
+
if (!v.id) issues.push("every variation must have an id");
|
|
61
|
+
if (ids.has(v.id)) issues.push(`duplicate variation id: "${v.id}"`);
|
|
62
|
+
ids.add(v.id);
|
|
63
|
+
if (v.weight < 0 || v.weight > 1) issues.push(`variation "${v.id}" weight must be between 0 and 1`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (def.trafficAllocation !== void 0) {
|
|
67
|
+
if (def.trafficAllocation < 0 || def.trafficAllocation > 1) {
|
|
68
|
+
issues.push("trafficAllocation must be between 0 and 1");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (def.goals) {
|
|
72
|
+
const goalIds = /* @__PURE__ */ new Set();
|
|
73
|
+
for (const g of def.goals) {
|
|
74
|
+
if (!g.id) issues.push("every goal must have an id");
|
|
75
|
+
if (goalIds.has(g.id)) issues.push(`duplicate goal id: "${g.id}"`);
|
|
76
|
+
goalIds.add(g.id);
|
|
77
|
+
if (g.type === "click" && !g.selector) {
|
|
78
|
+
issues.push(`click goal "${g.id}" requires a selector`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return issues;
|
|
83
|
+
}
|
|
84
|
+
function defineExperiment(def) {
|
|
85
|
+
const issues = validate(def);
|
|
86
|
+
if (issues.length > 0) {
|
|
87
|
+
throw new ExperimentDefinitionError(def.slug ?? "(unnamed)", issues);
|
|
88
|
+
}
|
|
89
|
+
const experiment = {
|
|
90
|
+
id: def.slug,
|
|
91
|
+
// slug is used as the id for code-first experiments
|
|
92
|
+
slug: def.slug,
|
|
93
|
+
name: def.name,
|
|
94
|
+
trafficAllocation: def.trafficAllocation ?? 1,
|
|
95
|
+
targeting: def.targeting,
|
|
96
|
+
hashAttribute: def.hashAttribute,
|
|
97
|
+
variations: def.variations.map((v) => ({
|
|
98
|
+
id: v.id,
|
|
99
|
+
name: v.name,
|
|
100
|
+
weight: v.weight,
|
|
101
|
+
mutations: v.mutations ?? [],
|
|
102
|
+
value: v.value,
|
|
103
|
+
css: v.css,
|
|
104
|
+
js: v.js,
|
|
105
|
+
resetJs: v.resetJs,
|
|
106
|
+
externalCssUrl: v.externalCssUrl,
|
|
107
|
+
externalJsUrl: v.externalJsUrl,
|
|
108
|
+
isControl: v.isControl ?? false
|
|
109
|
+
})),
|
|
110
|
+
goals: def.goals?.map((g) => ({
|
|
111
|
+
id: g.id,
|
|
112
|
+
type: g.type,
|
|
113
|
+
selector: g.selector,
|
|
114
|
+
urlPattern: g.urlPattern,
|
|
115
|
+
countOnce: g.countOnce
|
|
116
|
+
}))
|
|
117
|
+
};
|
|
118
|
+
return Object.freeze(experiment);
|
|
119
|
+
}
|
|
120
|
+
function defineExperiments(defs) {
|
|
121
|
+
return defs.map(defineExperiment);
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=define.cjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/define.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * defineExperiment \u2014 typed experiment definitions with validation.\n *\n * Usage:\n * const hero = defineExperiment({\n * slug: \"hero-banner-v2\",\n * name: \"Hero Banner V2\",\n * variations: [\n * { id: \"control\", weight: 0.5, mutations: [], isControl: true },\n * { id: \"variant-a\", weight: 0.5, mutations: [{ selector: \".hero h1\", action: \"text\", text: \"New headline\" }] },\n * ],\n * targeting: { url: [{ type: \"simple\", pattern: \"/\" }] },\n * goals: [{ id: \"cta-click\", type: \"click\", selector: \".hero-cta\" }],\n * });\n */\n\nimport type { Experiment, Goal, Mutation, Targeting, Variation } from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Input types \u2014 looser than runtime types for developer ergonomics\n// ---------------------------------------------------------------------------\n\nexport interface ExperimentDefinition {\n /** Unique slug for GitOps upsert. Required for code-first experiments. */\n slug: string;\n /** Human-readable name. */\n name: string;\n /** Traffic allocation percentage (0\u20131). Default: 1. */\n trafficAllocation?: number;\n /** Targeting rules. */\n targeting?: Targeting;\n /** Hash attribute override (default: visitor cookie). */\n hashAttribute?: string;\n /** Variation definitions. At least two required. */\n variations: VariationDefinition[];\n /** Goal definitions for conversion tracking. */\n goals?: GoalDefinition[];\n}\n\nexport interface VariationDefinition {\n /** Variation identifier (used as slug within the experiment). */\n id: string;\n /** Display name. Falls back to id if omitted. */\n name?: string;\n /** Traffic weight (0\u20131). All weights should sum to 1. */\n weight: number;\n /** DOM mutations to apply. Default: []. */\n mutations?: Mutation[];\n /** Optional typed value returned by feature-flag helpers. */\n value?: unknown;\n /** Custom CSS injected when this variation is active. */\n css?: string;\n /** Custom JavaScript executed when this variation is active. */\n js?: string;\n /** Reset JavaScript executed on SPA navigation away. */\n resetJs?: string;\n /** External CSS artifact URL loaded when this variation is active. */\n externalCssUrl?: string;\n /** External JS artifact URL loaded when this variation is active. */\n externalJsUrl?: string;\n /** Mark as control variation. Exactly one variation should be control. */\n isControl?: boolean;\n}\n\nexport interface GoalDefinition {\n /** Goal identifier (used as slug). */\n id: string;\n /** Goal type. */\n type: \"click\" | \"pageview\" | \"custom\" | \"revenue\";\n /** CSS selector for click/element goals. */\n selector?: string;\n /** URL pattern for pageview goals. */\n urlPattern?: string;\n /** Only count once per session. Default: false. */\n countOnce?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Validation errors\n// ---------------------------------------------------------------------------\n\nexport class ExperimentDefinitionError extends Error {\n public readonly issues: string[];\n\n constructor(slug: string, issues: string[]) {\n super(`Invalid experiment \"${slug}\": ${issues.join(\"; \")}`);\n this.name = \"ExperimentDefinitionError\";\n this.issues = issues;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\n\nfunction validate(def: ExperimentDefinition): string[] {\n const issues: string[] = [];\n\n if (!def.slug || typeof def.slug !== \"string\") {\n issues.push(\"slug is required and must be a non-empty string\");\n } else if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(def.slug) && def.slug.length > 1) {\n issues.push(\"slug must be lowercase alphanumeric with hyphens (e.g. 'hero-banner-v2')\");\n }\n\n if (!def.name || typeof def.name !== \"string\") {\n issues.push(\"name is required\");\n }\n\n if (!Array.isArray(def.variations) || def.variations.length < 2) {\n issues.push(\"at least 2 variations are required\");\n } else {\n const controls = def.variations.filter((v) => v.isControl);\n if (controls.length === 0) {\n issues.push(\"exactly one variation must be marked as control (isControl: true)\");\n } else if (controls.length > 1) {\n issues.push(\"only one variation can be the control\");\n }\n\n const totalWeight = def.variations.reduce((sum, v) => sum + v.weight, 0);\n if (Math.abs(totalWeight - 1) > 0.01) {\n issues.push(`variation weights should sum to 1 (got ${totalWeight.toFixed(3)})`);\n }\n\n const ids = new Set<string>();\n for (const v of def.variations) {\n if (!v.id) issues.push(\"every variation must have an id\");\n if (ids.has(v.id)) issues.push(`duplicate variation id: \"${v.id}\"`);\n ids.add(v.id);\n if (v.weight < 0 || v.weight > 1) issues.push(`variation \"${v.id}\" weight must be between 0 and 1`);\n }\n }\n\n if (def.trafficAllocation !== undefined) {\n if (def.trafficAllocation < 0 || def.trafficAllocation > 1) {\n issues.push(\"trafficAllocation must be between 0 and 1\");\n }\n }\n\n if (def.goals) {\n const goalIds = new Set<string>();\n for (const g of def.goals) {\n if (!g.id) issues.push(\"every goal must have an id\");\n if (goalIds.has(g.id)) issues.push(`duplicate goal id: \"${g.id}\"`);\n goalIds.add(g.id);\n\n if (g.type === \"click\" && !g.selector) {\n issues.push(`click goal \"${g.id}\" requires a selector`);\n }\n }\n }\n\n return issues;\n}\n\n// ---------------------------------------------------------------------------\n// defineExperiment\n// ---------------------------------------------------------------------------\n\n/**\n * Define a typed, validated experiment configuration.\n *\n * Returns a frozen Experiment object ready for use with `init({ experiments: [...] })`.\n * Throws `ExperimentDefinitionError` if the definition is invalid.\n */\nexport function defineExperiment(def: ExperimentDefinition): Experiment & { slug: string } {\n const issues = validate(def);\n if (issues.length > 0) {\n throw new ExperimentDefinitionError(def.slug ?? \"(unnamed)\", issues);\n }\n\n const experiment: Experiment & { slug: string } = {\n id: def.slug, // slug is used as the id for code-first experiments\n slug: def.slug,\n name: def.name,\n trafficAllocation: def.trafficAllocation ?? 1,\n targeting: def.targeting,\n hashAttribute: def.hashAttribute,\n variations: def.variations.map((v) => ({\n id: v.id,\n name: v.name,\n weight: v.weight,\n mutations: v.mutations ?? [],\n value: v.value,\n css: v.css,\n js: v.js,\n resetJs: v.resetJs,\n externalCssUrl: v.externalCssUrl,\n externalJsUrl: v.externalJsUrl,\n isControl: v.isControl ?? false,\n })),\n goals: def.goals?.map((g) => ({\n id: g.id,\n type: g.type,\n selector: g.selector,\n urlPattern: g.urlPattern,\n countOnce: g.countOnce,\n })),\n };\n\n return Object.freeze(experiment) as Experiment & { slug: string };\n}\n\n/**\n * Define multiple experiments at once. Validates all, throws on first invalid.\n */\nexport function defineExperiments(defs: ExperimentDefinition[]): Array<Experiment & { slug: string }> {\n return defs.map(defineExperiment);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/define.mjs
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// src/define.ts
|
|
2
|
+
var ExperimentDefinitionError = class extends Error {
|
|
3
|
+
constructor(slug, issues) {
|
|
4
|
+
super(`Invalid experiment "${slug}": ${issues.join("; ")}`);
|
|
5
|
+
this.name = "ExperimentDefinitionError";
|
|
6
|
+
this.issues = issues;
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
function validate(def) {
|
|
10
|
+
const issues = [];
|
|
11
|
+
if (!def.slug || typeof def.slug !== "string") {
|
|
12
|
+
issues.push("slug is required and must be a non-empty string");
|
|
13
|
+
} else if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(def.slug) && def.slug.length > 1) {
|
|
14
|
+
issues.push("slug must be lowercase alphanumeric with hyphens (e.g. 'hero-banner-v2')");
|
|
15
|
+
}
|
|
16
|
+
if (!def.name || typeof def.name !== "string") {
|
|
17
|
+
issues.push("name is required");
|
|
18
|
+
}
|
|
19
|
+
if (!Array.isArray(def.variations) || def.variations.length < 2) {
|
|
20
|
+
issues.push("at least 2 variations are required");
|
|
21
|
+
} else {
|
|
22
|
+
const controls = def.variations.filter((v) => v.isControl);
|
|
23
|
+
if (controls.length === 0) {
|
|
24
|
+
issues.push("exactly one variation must be marked as control (isControl: true)");
|
|
25
|
+
} else if (controls.length > 1) {
|
|
26
|
+
issues.push("only one variation can be the control");
|
|
27
|
+
}
|
|
28
|
+
const totalWeight = def.variations.reduce((sum, v) => sum + v.weight, 0);
|
|
29
|
+
if (Math.abs(totalWeight - 1) > 0.01) {
|
|
30
|
+
issues.push(`variation weights should sum to 1 (got ${totalWeight.toFixed(3)})`);
|
|
31
|
+
}
|
|
32
|
+
const ids = /* @__PURE__ */ new Set();
|
|
33
|
+
for (const v of def.variations) {
|
|
34
|
+
if (!v.id) issues.push("every variation must have an id");
|
|
35
|
+
if (ids.has(v.id)) issues.push(`duplicate variation id: "${v.id}"`);
|
|
36
|
+
ids.add(v.id);
|
|
37
|
+
if (v.weight < 0 || v.weight > 1) issues.push(`variation "${v.id}" weight must be between 0 and 1`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (def.trafficAllocation !== void 0) {
|
|
41
|
+
if (def.trafficAllocation < 0 || def.trafficAllocation > 1) {
|
|
42
|
+
issues.push("trafficAllocation must be between 0 and 1");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (def.goals) {
|
|
46
|
+
const goalIds = /* @__PURE__ */ new Set();
|
|
47
|
+
for (const g of def.goals) {
|
|
48
|
+
if (!g.id) issues.push("every goal must have an id");
|
|
49
|
+
if (goalIds.has(g.id)) issues.push(`duplicate goal id: "${g.id}"`);
|
|
50
|
+
goalIds.add(g.id);
|
|
51
|
+
if (g.type === "click" && !g.selector) {
|
|
52
|
+
issues.push(`click goal "${g.id}" requires a selector`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return issues;
|
|
57
|
+
}
|
|
58
|
+
function defineExperiment(def) {
|
|
59
|
+
const issues = validate(def);
|
|
60
|
+
if (issues.length > 0) {
|
|
61
|
+
throw new ExperimentDefinitionError(def.slug ?? "(unnamed)", issues);
|
|
62
|
+
}
|
|
63
|
+
const experiment = {
|
|
64
|
+
id: def.slug,
|
|
65
|
+
// slug is used as the id for code-first experiments
|
|
66
|
+
slug: def.slug,
|
|
67
|
+
name: def.name,
|
|
68
|
+
trafficAllocation: def.trafficAllocation ?? 1,
|
|
69
|
+
targeting: def.targeting,
|
|
70
|
+
hashAttribute: def.hashAttribute,
|
|
71
|
+
variations: def.variations.map((v) => ({
|
|
72
|
+
id: v.id,
|
|
73
|
+
name: v.name,
|
|
74
|
+
weight: v.weight,
|
|
75
|
+
mutations: v.mutations ?? [],
|
|
76
|
+
value: v.value,
|
|
77
|
+
css: v.css,
|
|
78
|
+
js: v.js,
|
|
79
|
+
resetJs: v.resetJs,
|
|
80
|
+
externalCssUrl: v.externalCssUrl,
|
|
81
|
+
externalJsUrl: v.externalJsUrl,
|
|
82
|
+
isControl: v.isControl ?? false
|
|
83
|
+
})),
|
|
84
|
+
goals: def.goals?.map((g) => ({
|
|
85
|
+
id: g.id,
|
|
86
|
+
type: g.type,
|
|
87
|
+
selector: g.selector,
|
|
88
|
+
urlPattern: g.urlPattern,
|
|
89
|
+
countOnce: g.countOnce
|
|
90
|
+
}))
|
|
91
|
+
};
|
|
92
|
+
return Object.freeze(experiment);
|
|
93
|
+
}
|
|
94
|
+
function defineExperiments(defs) {
|
|
95
|
+
return defs.map(defineExperiment);
|
|
96
|
+
}
|
|
97
|
+
export {
|
|
98
|
+
ExperimentDefinitionError,
|
|
99
|
+
defineExperiment,
|
|
100
|
+
defineExperiments
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=define.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/define.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * defineExperiment \u2014 typed experiment definitions with validation.\n *\n * Usage:\n * const hero = defineExperiment({\n * slug: \"hero-banner-v2\",\n * name: \"Hero Banner V2\",\n * variations: [\n * { id: \"control\", weight: 0.5, mutations: [], isControl: true },\n * { id: \"variant-a\", weight: 0.5, mutations: [{ selector: \".hero h1\", action: \"text\", text: \"New headline\" }] },\n * ],\n * targeting: { url: [{ type: \"simple\", pattern: \"/\" }] },\n * goals: [{ id: \"cta-click\", type: \"click\", selector: \".hero-cta\" }],\n * });\n */\n\nimport type { Experiment, Goal, Mutation, Targeting, Variation } from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Input types \u2014 looser than runtime types for developer ergonomics\n// ---------------------------------------------------------------------------\n\nexport interface ExperimentDefinition {\n /** Unique slug for GitOps upsert. Required for code-first experiments. */\n slug: string;\n /** Human-readable name. */\n name: string;\n /** Traffic allocation percentage (0\u20131). Default: 1. */\n trafficAllocation?: number;\n /** Targeting rules. */\n targeting?: Targeting;\n /** Hash attribute override (default: visitor cookie). */\n hashAttribute?: string;\n /** Variation definitions. At least two required. */\n variations: VariationDefinition[];\n /** Goal definitions for conversion tracking. */\n goals?: GoalDefinition[];\n}\n\nexport interface VariationDefinition {\n /** Variation identifier (used as slug within the experiment). */\n id: string;\n /** Display name. Falls back to id if omitted. */\n name?: string;\n /** Traffic weight (0\u20131). All weights should sum to 1. */\n weight: number;\n /** DOM mutations to apply. Default: []. */\n mutations?: Mutation[];\n /** Optional typed value returned by feature-flag helpers. */\n value?: unknown;\n /** Custom CSS injected when this variation is active. */\n css?: string;\n /** Custom JavaScript executed when this variation is active. */\n js?: string;\n /** Reset JavaScript executed on SPA navigation away. */\n resetJs?: string;\n /** External CSS artifact URL loaded when this variation is active. */\n externalCssUrl?: string;\n /** External JS artifact URL loaded when this variation is active. */\n externalJsUrl?: string;\n /** Mark as control variation. Exactly one variation should be control. */\n isControl?: boolean;\n}\n\nexport interface GoalDefinition {\n /** Goal identifier (used as slug). */\n id: string;\n /** Goal type. */\n type: \"click\" | \"pageview\" | \"custom\" | \"revenue\";\n /** CSS selector for click/element goals. */\n selector?: string;\n /** URL pattern for pageview goals. */\n urlPattern?: string;\n /** Only count once per session. Default: false. */\n countOnce?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Validation errors\n// ---------------------------------------------------------------------------\n\nexport class ExperimentDefinitionError extends Error {\n public readonly issues: string[];\n\n constructor(slug: string, issues: string[]) {\n super(`Invalid experiment \"${slug}\": ${issues.join(\"; \")}`);\n this.name = \"ExperimentDefinitionError\";\n this.issues = issues;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\n\nfunction validate(def: ExperimentDefinition): string[] {\n const issues: string[] = [];\n\n if (!def.slug || typeof def.slug !== \"string\") {\n issues.push(\"slug is required and must be a non-empty string\");\n } else if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(def.slug) && def.slug.length > 1) {\n issues.push(\"slug must be lowercase alphanumeric with hyphens (e.g. 'hero-banner-v2')\");\n }\n\n if (!def.name || typeof def.name !== \"string\") {\n issues.push(\"name is required\");\n }\n\n if (!Array.isArray(def.variations) || def.variations.length < 2) {\n issues.push(\"at least 2 variations are required\");\n } else {\n const controls = def.variations.filter((v) => v.isControl);\n if (controls.length === 0) {\n issues.push(\"exactly one variation must be marked as control (isControl: true)\");\n } else if (controls.length > 1) {\n issues.push(\"only one variation can be the control\");\n }\n\n const totalWeight = def.variations.reduce((sum, v) => sum + v.weight, 0);\n if (Math.abs(totalWeight - 1) > 0.01) {\n issues.push(`variation weights should sum to 1 (got ${totalWeight.toFixed(3)})`);\n }\n\n const ids = new Set<string>();\n for (const v of def.variations) {\n if (!v.id) issues.push(\"every variation must have an id\");\n if (ids.has(v.id)) issues.push(`duplicate variation id: \"${v.id}\"`);\n ids.add(v.id);\n if (v.weight < 0 || v.weight > 1) issues.push(`variation \"${v.id}\" weight must be between 0 and 1`);\n }\n }\n\n if (def.trafficAllocation !== undefined) {\n if (def.trafficAllocation < 0 || def.trafficAllocation > 1) {\n issues.push(\"trafficAllocation must be between 0 and 1\");\n }\n }\n\n if (def.goals) {\n const goalIds = new Set<string>();\n for (const g of def.goals) {\n if (!g.id) issues.push(\"every goal must have an id\");\n if (goalIds.has(g.id)) issues.push(`duplicate goal id: \"${g.id}\"`);\n goalIds.add(g.id);\n\n if (g.type === \"click\" && !g.selector) {\n issues.push(`click goal \"${g.id}\" requires a selector`);\n }\n }\n }\n\n return issues;\n}\n\n// ---------------------------------------------------------------------------\n// defineExperiment\n// ---------------------------------------------------------------------------\n\n/**\n * Define a typed, validated experiment configuration.\n *\n * Returns a frozen Experiment object ready for use with `init({ experiments: [...] })`.\n * Throws `ExperimentDefinitionError` if the definition is invalid.\n */\nexport function defineExperiment(def: ExperimentDefinition): Experiment & { slug: string } {\n const issues = validate(def);\n if (issues.length > 0) {\n throw new ExperimentDefinitionError(def.slug ?? \"(unnamed)\", issues);\n }\n\n const experiment: Experiment & { slug: string } = {\n id: def.slug, // slug is used as the id for code-first experiments\n slug: def.slug,\n name: def.name,\n trafficAllocation: def.trafficAllocation ?? 1,\n targeting: def.targeting,\n hashAttribute: def.hashAttribute,\n variations: def.variations.map((v) => ({\n id: v.id,\n name: v.name,\n weight: v.weight,\n mutations: v.mutations ?? [],\n value: v.value,\n css: v.css,\n js: v.js,\n resetJs: v.resetJs,\n externalCssUrl: v.externalCssUrl,\n externalJsUrl: v.externalJsUrl,\n isControl: v.isControl ?? false,\n })),\n goals: def.goals?.map((g) => ({\n id: g.id,\n type: g.type,\n selector: g.selector,\n urlPattern: g.urlPattern,\n countOnce: g.countOnce,\n })),\n };\n\n return Object.freeze(experiment) as Experiment & { slug: string };\n}\n\n/**\n * Define multiple experiments at once. Validates all, throws on first invalid.\n */\nexport function defineExperiments(defs: ExperimentDefinition[]): Array<Experiment & { slug: string }> {\n return defs.map(defineExperiment);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|