@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.
- package/README.md +1209 -39
- package/docs/API.md +692 -0
- package/docs/ARCHITECTURE.md +430 -0
- package/docs/CONTRIBUTING.md +264 -0
- package/docs/ROADMAP.md +292 -0
- package/docs/SECURITY.md +323 -0
- package/docs/design/api-philosophy.md +347 -0
- package/docs/design/config-format.md +442 -0
- package/docs/design/design-principles.md +212 -0
- package/docs/design/targeting-dsl.md +433 -0
- package/docs/features/codegen.md +351 -0
- package/docs/features/crash-rollback.md +399 -0
- package/docs/features/debug-overlay.md +328 -0
- package/docs/features/hmac-signing.md +330 -0
- package/docs/features/killer-features.md +308 -0
- package/docs/features/multivariate.md +339 -0
- package/docs/features/qr-sharing.md +372 -0
- package/docs/features/targeting.md +481 -0
- package/docs/features/time-travel.md +306 -0
- package/docs/features/value-experiments.md +487 -0
- package/docs/phases/phase-2-expansion.md +307 -0
- package/docs/phases/phase-3-ecosystem.md +289 -0
- package/docs/phases/phase-4-advanced.md +306 -0
- package/docs/phases/phase-5-v1-stable.md +350 -0
- package/docs/research/bundle-size-analysis.md +279 -0
- package/docs/research/competitors.md +327 -0
- package/docs/research/framework-ssr-quirks.md +394 -0
- package/docs/research/naming-rationale.md +238 -0
- package/docs/research/origin-story.md +179 -0
- package/docs/research/security-threats.md +312 -0
- package/package.json +2 -1
|
@@ -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).
|