@variantlab/core 0.1.0 → 0.1.2

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 (2) hide show
  1. package/README.md +180 -3
  2. package/package.json +4 -4
package/README.md CHANGED
@@ -1,7 +1,184 @@
1
1
  # @variantlab/core
2
2
 
3
- The framework-agnostic variantlab engine. Zero dependencies, runs anywhere.
3
+ > The framework-agnostic A/B testing and feature-flag engine. Zero runtime dependencies, runs anywhere.
4
4
 
5
- > **Status:** Phase 1 — Pre-alpha. Not ready for production use.
5
+ ![npm version](https://img.shields.io/npm/v/@variantlab/core/alpha?label=npm&color=blue)
6
+ ![bundle size](https://img.shields.io/badge/gzip-%3C3KB-brightgreen)
7
+ ![dependencies](https://img.shields.io/badge/runtime%20deps-0-brightgreen)
6
8
 
7
- See the [root README](../../README.md) for project overview, motivation, and roadmap.
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install @variantlab/core@alpha
13
+ ```
14
+
15
+ ## Quick start
16
+
17
+ ### 1. Define experiments
18
+
19
+ Create an `experiments.json` file:
20
+
21
+ ```json
22
+ {
23
+ "version": 1,
24
+ "experiments": [
25
+ {
26
+ "id": "cta-copy",
27
+ "type": "value",
28
+ "default": "buy-now",
29
+ "variants": [
30
+ { "id": "buy-now", "value": "Buy now" },
31
+ { "id": "get-started", "value": "Get started" }
32
+ ]
33
+ },
34
+ {
35
+ "id": "hero-layout",
36
+ "type": "render",
37
+ "default": "centered",
38
+ "variants": [
39
+ { "id": "centered" },
40
+ { "id": "split" }
41
+ ]
42
+ }
43
+ ]
44
+ }
45
+ ```
46
+
47
+ ### 2. Create the engine
48
+
49
+ ```ts
50
+ import { createEngine } from "@variantlab/core";
51
+ import experiments from "./experiments.json";
52
+
53
+ const engine = createEngine(experiments);
54
+ ```
55
+
56
+ ### 3. Get variants
57
+
58
+ ```ts
59
+ // Get assigned variant ID
60
+ const variant = engine.getVariant("hero-layout"); // "centered" | "split"
61
+
62
+ // Get a value experiment's value
63
+ const cta = engine.getVariantValue("cta-copy"); // "Buy now" | "Get started"
64
+
65
+ // Override for testing
66
+ engine.setVariant("hero-layout", "split");
67
+
68
+ // Clear override
69
+ engine.clearVariant("hero-layout");
70
+
71
+ // Reset all overrides
72
+ engine.resetAll();
73
+ ```
74
+
75
+ ### 4. Subscribe to changes
76
+
77
+ ```ts
78
+ engine.subscribe((event) => {
79
+ console.log(event.experimentId, event.variantId);
80
+ });
81
+ ```
82
+
83
+ ### 5. Targeting context
84
+
85
+ ```ts
86
+ const engine = createEngine(experiments, {
87
+ context: {
88
+ userId: "user-123",
89
+ platform: "web",
90
+ locale: "en",
91
+ screenSize: "large",
92
+ appVersion: "2.1.0",
93
+ },
94
+ });
95
+
96
+ // Update context at runtime
97
+ engine.updateContext({ locale: "bn" });
98
+ ```
99
+
100
+ ## Config validation
101
+
102
+ ```ts
103
+ import { validateConfig } from "@variantlab/core";
104
+
105
+ const result = validateConfig(experiments);
106
+ if (!result.ok) {
107
+ console.error(result.issues);
108
+ }
109
+ ```
110
+
111
+ ## Explain targeting (debug)
112
+
113
+ ```ts
114
+ import { explain } from "@variantlab/core";
115
+
116
+ const trace = explain(experiments, "hero-layout", {
117
+ platform: "ios",
118
+ screenSize: "small",
119
+ });
120
+ // Returns step-by-step targeting trace with pass/fail per field
121
+ ```
122
+
123
+ ## Assignment strategies
124
+
125
+ The engine supports multiple assignment strategies per experiment:
126
+
127
+ | Strategy | Description |
128
+ |----------|-------------|
129
+ | `default` | Always assigns the default variant |
130
+ | `random` | Random assignment on each evaluation |
131
+ | `sticky-hash` | Deterministic hash-based assignment (requires `userId`) |
132
+ | `weighted` | Weighted random distribution |
133
+
134
+ ```json
135
+ {
136
+ "id": "pricing",
137
+ "type": "value",
138
+ "default": "low",
139
+ "assignment": { "strategy": "sticky-hash" },
140
+ "variants": [
141
+ { "id": "low", "value": 9.99, "weight": 50 },
142
+ { "id": "high", "value": 14.99, "weight": 50 }
143
+ ]
144
+ }
145
+ ```
146
+
147
+ ## Targeting operators
148
+
149
+ Built-in targeting predicates:
150
+
151
+ - `platform` — `ios`, `android`, `web`
152
+ - `appVersion` — semver ranges (`>=1.2.0`, `^2.0.0`)
153
+ - `locale` — locale codes (`en`, `bn`, `fr`)
154
+ - `screenSize` — `small`, `medium`, `large`
155
+ - `routes` — glob patterns (`/settings/*`, `/dashboard`)
156
+ - `userId` — exact match or list
157
+ - `attributes` — custom key-value matching
158
+ - `predicate` — compound `and`/`or`/`not` logic
159
+
160
+ ## Key features
161
+
162
+ - Zero runtime dependencies
163
+ - < 3 KB gzipped
164
+ - O(1) hot-path `getVariant()` calls
165
+ - Kill switch and time-gated experiments
166
+ - Mutex groups (mutually exclusive experiments)
167
+ - Crash counter for auto-rollback
168
+ - History ring buffer
169
+ - `Object.freeze` on loaded config
170
+ - Prototype pollution guards
171
+ - CSP-strict compatible (no `eval`, no `Function()`)
172
+ - Works in Node, Deno, Bun, Cloudflare Workers, browsers, and React Native
173
+
174
+ ## Framework adapters
175
+
176
+ Use `@variantlab/core` directly for vanilla JS/TS, or pair it with a framework adapter:
177
+
178
+ - [`@variantlab/react`](https://www.npmjs.com/package/@variantlab/react) — React 18/19 hooks and components
179
+ - [`@variantlab/react-native`](https://www.npmjs.com/package/@variantlab/react-native) — React Native + Expo with debug overlay
180
+ - [`@variantlab/next`](https://www.npmjs.com/package/@variantlab/next) — Next.js 14/15 SSR + Edge
181
+
182
+ ## License
183
+
184
+ [MIT](./LICENSE)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@variantlab/core",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "The framework-agnostic variantlab engine. Zero dependencies, runs anywhere.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -38,13 +38,13 @@
38
38
  ],
39
39
  "repository": {
40
40
  "type": "git",
41
- "url": "git+https://github.com/variantlab/variantlab.git",
41
+ "url": "git+https://github.com/Minhaj-Rabby/variantlab.git",
42
42
  "directory": "packages/core"
43
43
  },
44
44
  "bugs": {
45
- "url": "https://github.com/variantlab/variantlab/issues"
45
+ "url": "https://github.com/Minhaj-Rabby/variantlab/issues"
46
46
  },
47
- "homepage": "https://github.com/variantlab/variantlab#readme",
47
+ "homepage": "https://github.com/Minhaj-Rabby/variantlab#readme",
48
48
  "engines": {
49
49
  "node": ">=18.17"
50
50
  },