@variantlab/core 0.1.4 → 0.1.6

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.
@@ -0,0 +1,307 @@
1
+ # Phase 2 — Expansion (v0.2)
2
+
3
+ **Status**: Not started
4
+ **Goal**: Broaden framework coverage and add power-user features that didn't make the MVP cut.
5
+ **Target version**: `0.2.0`
6
+
7
+ ## Table of contents
8
+
9
+ - [Exit criteria](#exit-criteria)
10
+ - [Scope](#scope)
11
+ - [New packages](#new-packages)
12
+ - [Core improvements](#core-improvements)
13
+ - [CLI improvements](#cli-improvements)
14
+ - [Documentation](#documentation)
15
+ - [Milestones](#milestones)
16
+
17
+ ---
18
+
19
+ ## Exit criteria
20
+
21
+ Phase 2 is done when:
22
+
23
+ 1. `@variantlab/remix` is published and tested in a production-style example
24
+ 2. `@variantlab/vue` is published with a composition API that matches the hooks API
25
+ 3. `@variantlab/vanilla` (framework-free) is published
26
+ 4. Devtools browser extension scaffolding exists (beta)
27
+ 5. Telemetry interface is finalized and at least one reference adapter is shipped
28
+ 6. `variantlab distribution` simulation command works
29
+ 7. Core supports server-sent events (SSE) for live config push (optional)
30
+ 8. All framework examples still pass CI
31
+ 9. Migration from v0.1 → v0.2 is documented
32
+
33
+ ---
34
+
35
+ ## Scope
36
+
37
+ ### Remix adapter (`@variantlab/remix`)
38
+
39
+ - [ ] Server helpers: `getVariantLoader`, `variantLabMiddleware`
40
+ - [ ] `<VariantLabProvider>` with loader data hydration
41
+ - [ ] Cookie-based sticky assignment (reuses Next.js adapter logic)
42
+ - [ ] Works on Remix SPA mode and SSR mode
43
+ - [ ] Example app in `examples/remix-app`
44
+
45
+ ### Vue adapter (`@variantlab/vue`)
46
+
47
+ - [ ] `<VariantLabProvider>` component
48
+ - [ ] `useVariant(id)` composable
49
+ - [ ] `useVariantValue(id)` composable
50
+ - [ ] `useExperiment(id)` composable
51
+ - [ ] `useRouteExperiments()` composable
52
+ - [ ] `<Variant>` component with slots
53
+ - [ ] Reactivity via Vue's `ref`/`computed` bridged to engine `subscribe`
54
+ - [ ] Nuxt module in a separate package (phase 3)
55
+
56
+ ### Vanilla adapter (`@variantlab/vanilla`)
57
+
58
+ - [ ] No framework dependency
59
+ - [ ] Imperative API: `getVariant`, `getVariantValue`, `setVariant`
60
+ - [ ] DOM helpers: `bindElement(id, ...)` for vanilla-JS sites
61
+ - [ ] Storage adapter for localStorage / sessionStorage
62
+ - [ ] Route watcher for `popstate` + `hashchange`
63
+ - [ ] Example in `examples/vanilla-app` (a static HTML file)
64
+
65
+ ---
66
+
67
+ ## New packages
68
+
69
+ | Package | Size | Notes |
70
+ |---|---|---|
71
+ | `@variantlab/remix` | ~3 KB | Extends core with Remix-specific loaders |
72
+ | `@variantlab/vue` | ~2 KB | Composition API; hooks with same names as React |
73
+ | `@variantlab/vanilla` | ~1 KB | Framework-free |
74
+ | `@variantlab/telemetry-console` | ~0.5 KB | Reference telemetry adapter that logs to console |
75
+ | `@variantlab/devtools` | Deferred | Chrome/Firefox extension — beta only |
76
+
77
+ ---
78
+
79
+ ## Core improvements
80
+
81
+ ### Targeting enhancements
82
+
83
+ - [ ] Wildcard prefix matching for locale (`"en*"`)
84
+ - [ ] `appVersion` prerelease support (`>=2.0.0-beta.1`)
85
+ - [ ] `attributes` key globs (`"plan*"`)
86
+
87
+ These are backward-compatible additions; existing configs still work.
88
+
89
+ ### Config improvements
90
+
91
+ - [ ] Support `include` field to split configs across files (`experiments/*.json` merged into one)
92
+ - [ ] Support `$ref` for variant value reuse
93
+ - [ ] Per-experiment `startDate`/`endDate` fractional support (interpolate rollout over time)
94
+
95
+ ### Assignment
96
+
97
+ - [ ] New strategy: `sticky-session` — same variant for the entire session, re-assigned on new session
98
+ - [ ] New strategy: `sticky-device` — per-device stickiness using a generated device ID
99
+ - [ ] Configurable bucket resolution (100 buckets → 10000 buckets for fine-grained splits)
100
+
101
+ ### Telemetry
102
+
103
+ - [ ] Finalize `Telemetry` interface
104
+ - [ ] Ship reference implementations:
105
+ - `@variantlab/telemetry-console` — logs to console
106
+ - `@variantlab/telemetry-posthog` — for PostHog (user hosts their own PostHog)
107
+ - [ ] Document how to integrate with Mixpanel, Amplitude, Segment, custom webhooks
108
+
109
+ ### Live config push
110
+
111
+ - [ ] SSE-based `createEventSourceFetcher` for push updates
112
+ - [ ] WebSocket-based `createWebSocketFetcher` (optional)
113
+ - [ ] Config change events surface in debug overlay
114
+
115
+ This is optional — most users can poll every 60s.
116
+
117
+ ---
118
+
119
+ ## CLI improvements
120
+
121
+ ### `variantlab distribution`
122
+
123
+ Simulate variant distribution for a config:
124
+
125
+ ```bash
126
+ variantlab distribution experiments.json \
127
+ --experiment cta-copy \
128
+ --users 10000
129
+ ```
130
+
131
+ Output: table of variant IDs with actual/expected percentages. Useful for verifying splits.
132
+
133
+ ### `variantlab eval`
134
+
135
+ Add filtering and batch mode:
136
+
137
+ ```bash
138
+ variantlab eval experiments.json \
139
+ --experiments cta-copy,card-layout \
140
+ --context-file ./test-contexts.json
141
+ ```
142
+
143
+ ### `variantlab diff`
144
+
145
+ Compare two configs:
146
+
147
+ ```bash
148
+ variantlab diff old.json new.json
149
+ ```
150
+
151
+ Shows added/removed/changed experiments.
152
+
153
+ ### `variantlab migrate`
154
+
155
+ Upgrade configs across major versions (placeholder for future):
156
+
157
+ ```bash
158
+ variantlab migrate --from 1 --to 2 experiments.json
159
+ ```
160
+
161
+ ---
162
+
163
+ ## Documentation
164
+
165
+ ### Migration guides
166
+
167
+ - `docs/migrations/v0.1-to-v0.2.md` — what changed, what to update
168
+ - `docs/migrations/from-firebase.md` — migrating from Firebase Remote Config
169
+ - `docs/migrations/from-growthbook.md` — migrating from GrowthBook
170
+ - `docs/migrations/from-launchdarkly.md` — migrating from LaunchDarkly
171
+
172
+ ### Adapter cookbooks
173
+
174
+ - `docs/adapters/remix.md`
175
+ - `docs/adapters/vue.md`
176
+ - `docs/adapters/vanilla.md`
177
+
178
+ Each cookbook shows:
179
+
180
+ - Install
181
+ - Provider setup
182
+ - Using hooks/composables
183
+ - SSR pattern
184
+ - Debug overlay integration
185
+ - Common pitfalls
186
+
187
+ ### Tutorial
188
+
189
+ - `docs/tutorial/first-experiment.md` — a 10-minute walkthrough for new users
190
+ - `docs/tutorial/multivariate-layout.md` — building a Drishtikon-style layout test
191
+ - `docs/tutorial/feature-flags.md` — using variantlab as feature flags
192
+
193
+ ---
194
+
195
+ ## Milestones
196
+
197
+ ### M1: Remix adapter
198
+
199
+ - Package scaffolding
200
+ - Loader-based SSR
201
+ - Cookie handling
202
+ - Example app
203
+ - Tests
204
+
205
+ Gate: example deployed to Remix's deploy target, no hydration issues.
206
+
207
+ ### M2: Vue adapter
208
+
209
+ - Package scaffolding
210
+ - Composition API composables
211
+ - Component shims
212
+ - Example SFC app (Vite + Vue)
213
+ - Tests
214
+
215
+ Gate: composables reactive, debug overlay renders in Vue.
216
+
217
+ ### M3: Vanilla adapter
218
+
219
+ - Framework-free core wrapper
220
+ - DOM bindings
221
+ - Example static HTML
222
+ - Tests
223
+
224
+ Gate: static example works in all evergreen browsers.
225
+
226
+ ### M4: CLI distribution + diff + migrate
227
+
228
+ - `distribution` command with statistical output
229
+ - `diff` command with readable delta
230
+ - `migrate` command stub (no actual migrations yet)
231
+ - Tests
232
+
233
+ Gate: CLI commands exit with correct codes on all expected inputs.
234
+
235
+ ### M5: Telemetry interface + reference adapters
236
+
237
+ - Finalize `Telemetry` interface (no breaking changes in phase 3+)
238
+ - Ship console adapter
239
+ - Ship posthog adapter
240
+ - Integration tests
241
+
242
+ Gate: end-to-end telemetry flow from hook call → posthog event.
243
+
244
+ ### M6: Live config push (optional)
245
+
246
+ - SSE fetcher
247
+ - Event subscription in debug overlay
248
+ - Example with a small SSE server
249
+
250
+ Gate: a config change on the server appears in the app within 1 second. This is optional; if it adds > 500 bytes to core, defer to phase 3.
251
+
252
+ ### M7: v0.2.0 release
253
+
254
+ - Changeset + changelog
255
+ - Migration guides
256
+ - Publish to npm
257
+
258
+ Gate: all new packages pass size checks, tests, and publint.
259
+
260
+ ---
261
+
262
+ ## Non-goals
263
+
264
+ Explicitly **not** in phase 2:
265
+
266
+ - ❌ Svelte / SvelteKit (phase 3)
267
+ - ❌ Solid / SolidStart (phase 3)
268
+ - ❌ Astro (phase 3)
269
+ - ❌ Nuxt module (phase 3)
270
+ - ❌ HMAC signing (phase 4)
271
+ - ❌ Crash rollback persistence (phase 4)
272
+ - ❌ Time travel replay (phase 4)
273
+ - ❌ Devtools full implementation (phase 3)
274
+ - ❌ Hosted dashboard (never)
275
+
276
+ ---
277
+
278
+ ## Risks
279
+
280
+ ### Risk: Vue reactivity doesn't map cleanly to `subscribe`
281
+
282
+ Mitigation: study how Pinia and VueUse bridge external state. If hooks feel wrong in Vue, we adjust the composable shapes without touching core.
283
+
284
+ ### Risk: Remix loader + client variant mismatch
285
+
286
+ Mitigation: strict cookie-based stickiness. The loader sets the cookie, the client reads it. No client-side randomness.
287
+
288
+ ### Risk: Telemetry interface is wrong and we can't change it
289
+
290
+ Mitigation: finalize only after shipping 2 reference adapters that use it. If both work, the interface is probably right.
291
+
292
+ ### Risk: Scope creep
293
+
294
+ Mitigation: strict "phase 2 is only these items" discipline. New ideas go to phase 3 or later.
295
+
296
+ ---
297
+
298
+ ## Transition to phase 3
299
+
300
+ Phase 2 exits when:
301
+
302
+ - All exit criteria met
303
+ - v0.2.0 is tagged and published
304
+ - Migration guides written
305
+ - No regressions in phase 1 packages
306
+
307
+ Phase 3 starts with: the remaining framework ecosystem (Svelte, Solid, Astro, Nuxt) and full devtools. See [`phase-3-ecosystem.md`](./phase-3-ecosystem.md).
@@ -0,0 +1,289 @@
1
+ # Phase 3 — Ecosystem (v0.3)
2
+
3
+ **Status**: Not started
4
+ **Goal**: Reach every major frontend framework and ship the devtools extension.
5
+ **Target version**: `0.3.0`
6
+
7
+ ## Table of contents
8
+
9
+ - [Exit criteria](#exit-criteria)
10
+ - [Framework adapters](#framework-adapters)
11
+ - [Devtools extension](#devtools-extension)
12
+ - [Meta-framework integrations](#meta-framework-integrations)
13
+ - [Quality of life](#quality-of-life)
14
+ - [Milestones](#milestones)
15
+
16
+ ---
17
+
18
+ ## Exit criteria
19
+
20
+ Phase 3 is done when:
21
+
22
+ 1. `@variantlab/svelte` is published
23
+ 2. `@variantlab/sveltekit` is published
24
+ 3. `@variantlab/solid` is published
25
+ 4. `@variantlab/solid-start` is published
26
+ 5. `@variantlab/astro` is published
27
+ 6. `@variantlab/nuxt` is published
28
+ 7. `@variantlab/devtools` Chrome/Firefox extension is in beta
29
+ 8. At least one community contribution has been merged
30
+ 9. Adapter sizes meet budgets
31
+ 10. Each adapter has a working example app
32
+ 11. Documentation covers all adapters uniformly
33
+
34
+ ---
35
+
36
+ ## Framework adapters
37
+
38
+ ### Svelte (`@variantlab/svelte`)
39
+
40
+ - [ ] `<VariantLabProvider>` component (Svelte 5 runes-ready)
41
+ - [ ] `useVariant(id)` as a Svelte store + `$` auto-subscribe
42
+ - [ ] `useVariantValue(id)` store
43
+ - [ ] `<Variant>` with named slots for each variant
44
+ - [ ] Store reactivity via engine `subscribe`
45
+ - [ ] Works on Svelte 4 and Svelte 5
46
+ - [ ] Example in `examples/svelte-app`
47
+
48
+ ### SvelteKit (`@variantlab/sveltekit`)
49
+
50
+ - [ ] Server hooks (`handle`) for context extraction
51
+ - [ ] Load function helpers for SSR
52
+ - [ ] Cookie-based sticky assignment
53
+ - [ ] Edge adapter compatibility
54
+ - [ ] Example in `examples/sveltekit-app`
55
+
56
+ ### Solid (`@variantlab/solid`)
57
+
58
+ - [ ] `<VariantLabProvider>` component
59
+ - [ ] `useVariant(id)` as a Solid accessor
60
+ - [ ] `useVariantValue(id)` accessor
61
+ - [ ] `<Variant>` with children-as-function signal
62
+ - [ ] Reactivity via `createSignal` bridged to engine `subscribe`
63
+ - [ ] Example in `examples/solid-app`
64
+
65
+ ### SolidStart (`@variantlab/solid-start`)
66
+
67
+ - [ ] Server-side variant resolution
68
+ - [ ] `createServerAction$` integration
69
+ - [ ] Cookie-based stickiness
70
+ - [ ] Example in `examples/solid-start-app`
71
+
72
+ ### Astro (`@variantlab/astro`)
73
+
74
+ - [ ] Astro integration plugin
75
+ - [ ] Server-side rendering helpers
76
+ - [ ] Client-island hydration
77
+ - [ ] Works with any framework island (React, Vue, Svelte, Solid)
78
+ - [ ] View transitions integration
79
+ - [ ] Example in `examples/astro-app`
80
+
81
+ ### Nuxt (`@variantlab/nuxt`)
82
+
83
+ - [ ] Nuxt module (`addPlugin`, `addImportsDir`)
84
+ - [ ] Auto-imported composables
85
+ - [ ] Server-side rendering via `nuxt/server`
86
+ - [ ] Config pushed via runtime config
87
+ - [ ] Example in `examples/nuxt-app`
88
+
89
+ ---
90
+
91
+ ## Devtools extension
92
+
93
+ ### Chrome / Firefox / Edge extension
94
+
95
+ - [ ] Manifest V3
96
+ - [ ] Attaches to pages with `@variantlab/react` (or other adapters) loaded
97
+ - [ ] Panel in DevTools showing:
98
+ - [ ] Active experiments on the current page
99
+ - [ ] Current variant of each
100
+ - [ ] Override controls
101
+ - [ ] History timeline
102
+ - [ ] Config viewer
103
+ - [ ] Context inspector
104
+ - [ ] No network calls — all local
105
+ - [ ] No data collection
106
+ - [ ] Open source in the same monorepo
107
+ - [ ] Published to Chrome Web Store + Firefox Add-ons
108
+
109
+ ### Implementation sketch
110
+
111
+ The devtools extension uses the existing engine's `subscribe` + `getHistory` + `getExperiments` API. It talks to the page via `window.postMessage` and reads engine state from `window.__VARIANTLAB__` (exposed in dev builds only).
112
+
113
+ Security:
114
+
115
+ - The extension only activates on pages that explicitly expose the devtools hook
116
+ - No data leaves the browser
117
+ - Read-only by default; writes require confirmation
118
+
119
+ ---
120
+
121
+ ## Meta-framework integrations
122
+
123
+ ### TanStack Router
124
+
125
+ - [ ] Reference integration that syncs route context with TanStack Router's navigation state
126
+ - [ ] Not a separate package — documented in `docs/adapters/tanstack-router.md`
127
+
128
+ ### React Router v7
129
+
130
+ - [ ] Reference integration with `useLocation`
131
+ - [ ] Documented in `docs/adapters/react-router.md`
132
+
133
+ ### Expo Router v3+
134
+
135
+ - [ ] Already covered by `@variantlab/react-native`, but document edge cases
136
+ - [ ] `docs/adapters/expo-router.md`
137
+
138
+ ### Ionic + Capacitor
139
+
140
+ - [ ] Reference integration — mostly just React
141
+ - [ ] Native plugin wrapper for deep links
142
+ - [ ] `docs/adapters/ionic.md`
143
+
144
+ ### Tauri
145
+
146
+ - [ ] Reference integration for Tauri apps
147
+ - [ ] Custom fetcher that reads from Tauri's filesystem API
148
+ - [ ] `docs/adapters/tauri.md`
149
+
150
+ ### Electron
151
+
152
+ - [ ] Reference integration
153
+ - [ ] Main process + renderer process separation
154
+ - [ ] `docs/adapters/electron.md`
155
+
156
+ ---
157
+
158
+ ## Quality of life
159
+
160
+ ### Storybook addon (`@variantlab/storybook`)
161
+
162
+ - [ ] Addon that lets stories select variants via toolbar
163
+ - [ ] Each story can declare its experiment dependencies
164
+ - [ ] Integrates with the existing debug overlay
165
+ - [ ] Example storybook in `examples/storybook`
166
+
167
+ ### Playwright fixtures (`@variantlab/playwright`)
168
+
169
+ - [ ] Fixture for setting variants in Playwright tests
170
+ - [ ] Helper for asserting which variant is active
171
+ - [ ] Example in `examples/playwright-tests`
172
+
173
+ ### Vitest matchers (`@variantlab/vitest`)
174
+
175
+ - [ ] `expect(engine).toHaveVariant("foo", "bar")`
176
+ - [ ] `expect(engine).toHaveExposed("foo")`
177
+ - [ ] Mock engine helper
178
+ - [ ] Documented in `docs/tooling/testing.md`
179
+
180
+ ### MSW handlers (`@variantlab/msw`)
181
+
182
+ - [ ] Mock Service Worker handlers for remote config fetching
183
+ - [ ] Lets tests control what configs get served
184
+ - [ ] Example in `examples/msw-tests`
185
+
186
+ ### `create-variantlab` scaffolder
187
+
188
+ - [ ] `npm create variantlab@latest`
189
+ - [ ] Prompts for framework, prefers sensible defaults
190
+ - [ ] Generates a working project with example experiment
191
+
192
+ ---
193
+
194
+ ## Milestones
195
+
196
+ ### M1: Svelte + SvelteKit
197
+
198
+ - Svelte 5 runes support
199
+ - SvelteKit SSR
200
+ - Example apps
201
+ - Tests
202
+
203
+ ### M2: Solid + SolidStart
204
+
205
+ - Signal-based hooks
206
+ - SSR
207
+ - Example apps
208
+ - Tests
209
+
210
+ ### M3: Astro
211
+
212
+ - Integration plugin
213
+ - Island hydration
214
+ - Example app
215
+
216
+ ### M4: Nuxt
217
+
218
+ - Nuxt module
219
+ - Auto-imports
220
+ - Example app
221
+
222
+ ### M5: Devtools extension beta
223
+
224
+ - Manifest V3 scaffolding
225
+ - Panel UI
226
+ - Read-only support
227
+ - Chrome Web Store submission (beta channel)
228
+
229
+ ### M6: QoL tools
230
+
231
+ - Storybook addon
232
+ - Playwright fixtures
233
+ - Vitest matchers
234
+ - MSW handlers
235
+ - create-variantlab
236
+
237
+ ### M7: v0.3.0 release
238
+
239
+ - All adapters published
240
+ - Devtools in beta
241
+ - Changelog + migration notes
242
+
243
+ ---
244
+
245
+ ## Community targets
246
+
247
+ By the end of phase 3, we want:
248
+
249
+ - [ ] 10+ external contributors
250
+ - [ ] 5+ example apps in `examples/`
251
+ - [ ] 100+ GitHub stars
252
+ - [ ] 1000+ weekly downloads on npm
253
+ - [ ] 1+ blog post from a user
254
+ - [ ] 1+ conference talk (any size)
255
+
256
+ These are "hope so" targets, not gates.
257
+
258
+ ---
259
+
260
+ ## Risks
261
+
262
+ ### Risk: framework coverage becomes a maintenance burden
263
+
264
+ Mitigation: each adapter is < 200 LOC and shares 95% of its code via `@variantlab/core`. Core changes ripple to all adapters. Adapter-specific bugs stay in adapters.
265
+
266
+ ### Risk: devtools extension is harder than expected
267
+
268
+ Mitigation: ship a minimal panel first (read-only). Interactive features come later if the read-only version is useful.
269
+
270
+ ### Risk: Astro's island model doesn't fit our API
271
+
272
+ Mitigation: Astro users primarily use React/Vue/Svelte inside islands. The Astro adapter is thin — it provides the Provider at the island boundary, and the framework adapter does the rest.
273
+
274
+ ### Risk: Nuxt auto-imports collide with user code
275
+
276
+ Mitigation: namespace all composables under a `vl` prefix option. Users can opt out of auto-imports.
277
+
278
+ ---
279
+
280
+ ## Transition to phase 4
281
+
282
+ Phase 3 exits when:
283
+
284
+ - All framework adapters published
285
+ - Devtools extension in beta
286
+ - All milestones hit
287
+ - v0.3.0 published
288
+
289
+ Phase 4 adds advanced features: HMAC signing, crash rollback persistence, time travel replay, statistical features. See [`phase-4-advanced.md`](./phase-4-advanced.md).