@onboarding-yak/react-native 0.0.0-stage → 0.6.1
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 +240 -2
- package/lib/appearance.d.ts +6 -0
- package/lib/appearance.d.ts.map +1 -0
- package/lib/appearance.js +45 -0
- package/lib/appearance.js.map +1 -0
- package/lib/countries.d.ts +4 -0
- package/lib/countries.d.ts.map +1 -0
- package/lib/countries.js +28 -0
- package/lib/countries.js.map +1 -0
- package/lib/experiment.d.ts +44 -0
- package/lib/experiment.d.ts.map +1 -0
- package/lib/experiment.js +47 -0
- package/lib/experiment.js.map +1 -0
- package/lib/index.d.ts +252 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +930 -0
- package/lib/index.js.map +1 -0
- package/lib/logic.d.ts +24 -0
- package/lib/logic.d.ts.map +1 -0
- package/lib/logic.js +31 -0
- package/lib/logic.js.map +1 -0
- package/lib/pickers.d.ts +80 -0
- package/lib/pickers.d.ts.map +1 -0
- package/lib/pickers.js +180 -0
- package/lib/pickers.js.map +1 -0
- package/lib/units.d.ts +32 -0
- package/lib/units.d.ts.map +1 -0
- package/lib/units.js +37 -0
- package/lib/units.js.map +1 -0
- package/lib/variables.d.ts +52 -0
- package/lib/variables.d.ts.map +1 -0
- package/lib/variables.js +82 -0
- package/lib/variables.js.map +1 -0
- package/package.json +70 -4
- package/src/appearance.ts +41 -0
- package/src/countries.ts +28 -0
- package/src/experiment.ts +47 -0
- package/src/index.tsx +1087 -0
- package/src/logic.ts +36 -0
- package/src/pickers.tsx +253 -0
- package/src/units.ts +41 -0
- package/src/variables.ts +80 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 OnboardingYak
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,241 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @onboarding-yak/react-native (prototype)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
React Native renderer for OnboardingYak onboarding flows. It fetches a published flow, validates its schema, caches the last valid response with AsyncStorage, and falls back to an app-bundled flow when the network is unavailable.
|
|
4
|
+
|
|
5
|
+
## SDK 0.6.1
|
|
6
|
+
|
|
7
|
+
This update adds image motion and analytics environment labels, alongside sign-up in the middle of an onboarding:
|
|
8
|
+
|
|
9
|
+
- Images can float, rock gently, pulse, or slowly zoom in the phone renderer.
|
|
10
|
+
- Development builds label analytics events as `development`. Set `analyticsEnvironment="testing"` for TestFlight builds and use `production` for App Store builds.
|
|
11
|
+
- A `signUp` action that hands off to your own account screen.
|
|
12
|
+
- `onAction` can return a Promise. The flow waits for it and shows a spinner on the button. If it resolves to `false` or rejects, the person stays on the screen.
|
|
13
|
+
- `onAction` gets the answers so far as its third argument.
|
|
14
|
+
- Progress is saved on the device. Someone who leaves the app to sign up, for example in a browser, comes back to the same screen with their answers.
|
|
15
|
+
|
|
16
|
+
Older SDKs pass `signUp` to `onAction` like any other action and move on without waiting.
|
|
17
|
+
|
|
18
|
+
## SDK 0.5.0
|
|
19
|
+
|
|
20
|
+
This update adds:
|
|
21
|
+
|
|
22
|
+
- New answer formats: a number wheel, a date picker, and a country picker.
|
|
23
|
+
- Loading screens with check marks, and benefit checklists.
|
|
24
|
+
- Image carousels.
|
|
25
|
+
- Custom header and body layouts.
|
|
26
|
+
- Graphs that draw in from zero.
|
|
27
|
+
- Light and dark mode.
|
|
28
|
+
- A keyboard-aware layout: the button stays above the keyboard, content scrolls, and tapping empty space in the content or the button dismisses it.
|
|
29
|
+
|
|
30
|
+
Everything is built from plain React Native views, so there are no new dependencies. Existing apps should reinstall the local package and restart Metro to pick up the JavaScript changes.
|
|
31
|
+
|
|
32
|
+
Flows built with these features still load on 0.4.0 and earlier, with simpler fallbacks:
|
|
33
|
+
|
|
34
|
+
- Pickers show as a plain text box.
|
|
35
|
+
- Loading screens show as normal screens with a button.
|
|
36
|
+
- Carousels show their first image.
|
|
37
|
+
- Custom layouts use the default order.
|
|
38
|
+
- Dark mode and animations are skipped.
|
|
39
|
+
|
|
40
|
+
## SDK 0.4.0
|
|
41
|
+
|
|
42
|
+
Adds full-screen background illustrations with solid or gradient overlays, and optional emoji icons on choice answers. It also adds runtime localization: translated copy follows the device locale by default, or you can pass `locale` to override it.
|
|
43
|
+
|
|
44
|
+
## Install in an Expo app
|
|
45
|
+
|
|
46
|
+
Install the SDK and its required peer dependencies:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx expo install @onboarding-yak/react-native @react-native-async-storage/async-storage react-native-safe-area-context
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The `expo-*` peer dependencies are optional. Install one only when a flow uses that feature (the studio's install guide lists the ones your app needs).
|
|
53
|
+
|
|
54
|
+
Import `Onboarding` from `@onboarding-yak/react-native` and provide a `fallbackFlow` in the same schema as the dashboard. Pass the app's publishable key as `apiKey`; the studio shows it in the app's install guide. Like a Stripe publishable key, it's meant to ship inside the app: it only lets the holder read that app's published flow and send its analytics events. Without a valid key the API refuses both requests and the SDK shows the cached or fallback flow. The dashboard API must be reachable from the device or simulator; `localhost` on a physical device refers to the device itself, so use the development machine's LAN IP during local testing.
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
<Onboarding
|
|
58
|
+
flowId="onboarding"
|
|
59
|
+
apiKey="oy_pub_…"
|
|
60
|
+
baseUrl="http://192.168.1.10:3000"
|
|
61
|
+
fallbackFlow={bundledFlow}
|
|
62
|
+
locale="fr-CA"
|
|
63
|
+
appearance="system"
|
|
64
|
+
onComplete={() => navigation.replace('Home')}
|
|
65
|
+
onAction={(action, screen) => {
|
|
66
|
+
if (action === 'requestNotifications') requestPushPermission();
|
|
67
|
+
if (action === 'showPaywall') openPaywall();
|
|
68
|
+
}}
|
|
69
|
+
/>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Development builds are tagged as `development` automatically. Release builds default to `production`. To keep TestFlight and other pre-release builds separate, set `analyticsEnvironment="testing"` on `Onboarding`; switch it back to `production` for App Store builds. The studio overview excludes development and testing events by default, and its Test data toggle includes them.
|
|
73
|
+
|
|
74
|
+
Supported screen kinds are `Welcome`, `Question`, `Feature`, `Permission`, and `Paywall`. These currently share a starter renderer. Actions are identifiers handled by the app; the flow contains no executable code.
|
|
75
|
+
|
|
76
|
+
## Appearance
|
|
77
|
+
|
|
78
|
+
Each app picks Light, Dark, or Match device in OnboardingYak, and the choice is published with the flow. Pass `appearance` (`'light'`, `'dark'`, or `'system'`) to override it. With `'system'`, the onboarding follows the phone's setting and switches live.
|
|
79
|
+
|
|
80
|
+
Screens can save their own dark colors. Any screen without them gets a dark version of its light palette that keeps its hue, so existing flows work in dark mode without changes.
|
|
81
|
+
|
|
82
|
+
## Layout
|
|
83
|
+
|
|
84
|
+
`theme.arrangement` sets the order of parts in the header and body. `theme.headerAlign`, `theme.bodyAlign`, and `theme.footerAlign` (`'top'`, `'center'`, or `'bottom'`) place each section in a top, middle, or bottom zone, with free space split evenly between zones. Order is kept: a later section never sits above an earlier one. Defaults are header top, footer bottom, and body bottom on full-photo screens and top otherwise. A bottom footer stays pinned above the keyboard; a footer moved up scrolls with the content.
|
|
85
|
+
|
|
86
|
+
## Answer formats
|
|
87
|
+
|
|
88
|
+
Question screens with a written answer can set `input.format`. Every format arrives as a string in `onAnswer` and `onComplete`.
|
|
89
|
+
|
|
90
|
+
| Format | What people see | Answer value |
|
|
91
|
+
| --- | --- | --- |
|
|
92
|
+
| `text` (default) | A multi-line text box | The text, e.g. `Notifications and meetings` |
|
|
93
|
+
| `number` | A large number field with the number keypad | Digits as typed, e.g. `170` |
|
|
94
|
+
| `wheel` | A scrolling number wheel using `min`, `max`, `step`, `initial`, and `unit` | The selected number, e.g. `70.5` |
|
|
95
|
+
| `date` | Month, day, and year wheels; `min` and `max` are years | An ISO date, e.g. `1995-01-01` |
|
|
96
|
+
| `country` | A searchable list of countries with flags | The English country name, e.g. `Spain` |
|
|
97
|
+
|
|
98
|
+
The unit is not included in the answer. Read it from `screen.input.unit` in `onAnswer` if you need it.
|
|
99
|
+
|
|
100
|
+
`wheel` and `number` inputs can offer up to two more units in `input.units`, shown as tabs above the wheel or field. Each option has a `unit` label and a `factor` from the base unit (other = base × factor), plus an optional `min`, `max`, and `step`. The range is derived from the base range when left out. Set `display: 'feet-inches'` to show inches as 5′ 9″. Answers are always converted back to the base unit: someone who picks 154 lb on a kg wheel sends `69.9`.
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
input: { format: 'wheel', unit: 'kg', min: 35, max: 200, step: 0.5, initial: 70, units: [{ unit: 'lb', factor: 2.20462, step: 1 }] }
|
|
104
|
+
``` Wheels and date pickers start with a value already selected, so people can continue without scrolling.
|
|
105
|
+
|
|
106
|
+
## Multiple choice, branching, and personalized copy
|
|
107
|
+
|
|
108
|
+
**Multiple choice.** A choice question with `multiple: { min?, max? }` lets people pick several options. The button stays disabled until `min` are picked. The answer joins the picks with commas (`"Keto, Vegan"`), and `onAnswer(screen, answer, values)` also receives them as a list. Older SDKs show the question as single choice.
|
|
109
|
+
|
|
110
|
+
**Branching.** `showIf: { screen, anyOf, not? }` shows a screen only when an earlier choice question's answer is one of `anyOf`, or none of them with `not: true`. Screens that don't match are skipped, Back returns to the screens that were actually shown, and a screen whose question hasn't been answered is skipped. Older SDKs show every screen.
|
|
111
|
+
|
|
112
|
+
**Variables.** A question with `variable: "name"` saves its answer as a variable. Headlines, supporting text, buttons, checklist items, and loading steps can recall it as `{name}`, or `{name|friend}` to show "friend" when there's no answer. The variable's type comes from the question, and recalled values are formatted:
|
|
113
|
+
|
|
114
|
+
| Question | Type | Recalled as |
|
|
115
|
+
| --- | --- | --- |
|
|
116
|
+
| Written answer | `text` | `Alex` |
|
|
117
|
+
| Number field or wheel | `number` | `70 kg`, or `154 lb` if the person switched units |
|
|
118
|
+
| Number with `variableFormat.currency` | `money` | `$3,000` |
|
|
119
|
+
| Date picker | `date` | `January 4, 1995`; set `variableFormat.dateStyle` to `medium`, `short`, or `numeric` |
|
|
120
|
+
| Single choice | `choice` | `Lose weight` |
|
|
121
|
+
| Multiple choice | `list` | `Dairy, Nuts and Gluten` |
|
|
122
|
+
| Country picker | `country` | `Spain` |
|
|
123
|
+
|
|
124
|
+
Built-in variables are always available: `{app_name}`, `{first_name}` (the first word of `{name}`), `{today}` (e.g. March 12), and `{weekday}`.
|
|
125
|
+
|
|
126
|
+
When the flow ends, `onComplete(answers, { variables })` receives every variable keyed by name, as `{ type, value, unit?, currency?, display }`: the raw value for your logic or storage, and the text the onboarding showed. `onAnswer` also receives the variable as its fourth argument. Store them, use them, or ignore them; apps that only read `answers` keep working. Older SDKs show tokens as written, so flows that use variables need this SDK version.
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
onComplete={(answers, { variables }) => {
|
|
130
|
+
// variables.weight → { type: 'number', value: 69.9, unit: 'kg', display: '154 lb' }
|
|
131
|
+
saveProfile(variables);
|
|
132
|
+
}}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Actions and permissions
|
|
136
|
+
|
|
137
|
+
A screen's `action` runs when its button is tapped. `next` and `complete` are handled by the SDK. Every other action reaches `onAction(action, screen)` so your app can make the native request, and then the flow moves on:
|
|
138
|
+
|
|
139
|
+
| Action | What your app does | Package |
|
|
140
|
+
| --- | --- | --- |
|
|
141
|
+
| `requestNotifications` | Ask for notification permission | your push setup |
|
|
142
|
+
| `requestTracking` | Ask for App Tracking Transparency (add `NSUserTrackingUsageDescription`) | `expo-tracking-transparency` |
|
|
143
|
+
| `requestLocation` | Ask for location while in use | `expo-location` |
|
|
144
|
+
| `requestCamera` | Ask for camera access | `expo-camera` |
|
|
145
|
+
| `requestPhotos` | Ask for photo library access | `expo-media-library` |
|
|
146
|
+
| `requestMicrophone` | Ask for microphone access | `expo-audio` |
|
|
147
|
+
| `requestContacts` | Ask for contacts access | `expo-contacts` |
|
|
148
|
+
| `requestHealth` | Ask for Apple Health / Health Connect access (needs a development build) | `react-native-health`, `react-native-health-connect` |
|
|
149
|
+
| `requestBiometrics` | Run a Face ID or fingerprint check and turn on biometric unlock | `expo-local-authentication` |
|
|
150
|
+
| `requestReview` | Show the store rating prompt (iOS decides whether it appears) | `expo-store-review` |
|
|
151
|
+
| `showPaywall` | Present your paywall | your paywall |
|
|
152
|
+
| `signUp` | Present your sign-up and resolve once the account exists | your auth |
|
|
153
|
+
| `custom:<name>` | Anything else: your app decides, e.g. `custom:requestBluetooth` or `custom:addWidget` | your code |
|
|
154
|
+
|
|
155
|
+
Custom actions reach `onAction` exactly as written in the studio, so match on the full string:
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
onAction={(action) => {
|
|
159
|
+
if (action === 'custom:requestBluetooth') return requestBluetooth(); // a Promise holds the flow until it resolves
|
|
160
|
+
}}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Turn on the matching capabilities in OnboardingYak and the setup guide includes the packages and the `onAction` code. Older SDKs pass these actions through the same way.
|
|
164
|
+
|
|
165
|
+
**Waiting for the app.** If `onAction` returns nothing or `true`, the flow moves on straight away. If it returns a Promise, the flow waits and the button shows a spinner. Resolve to `true` to move on. Resolve to `false`, or reject, to keep the person on the screen, for example when they cancel. `screen_completed` is sent only once the flow moves on, so the dashboard shows how many people stop at that screen.
|
|
166
|
+
|
|
167
|
+
## Sign-up during onboarding
|
|
168
|
+
|
|
169
|
+
Sign-up stays in your app. The flow never sees credentials or tokens. A screen with `action: 'signUp'` hands off to your own account screen (Sign in with Apple, Google, email, and so on), and the flow resumes when you resolve:
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
onAction={async (action, screen, { variables }) => {
|
|
173
|
+
if (action !== 'signUp') return;
|
|
174
|
+
if (await isSignedIn()) return true; // already has an account: move on
|
|
175
|
+
const user = await presentSignUp(); // your sheet; null if the person cancels
|
|
176
|
+
if (!user) return false; // stay on this screen
|
|
177
|
+
await saveProfile(user.id, variables); // the answers so far, typed
|
|
178
|
+
return true;
|
|
179
|
+
}}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The third argument holds `answers` and `variables` up to this screen, so you can save them to the new account right away. Everything after it still arrives in `onComplete`.
|
|
183
|
+
|
|
184
|
+
**Leaving the app.** OAuth redirects and magic links send people out of the app, and the system may close it while they're gone. The SDK saves progress (screen, Back history, answers, and session) on the device after every step. On the next launch it picks up where the person left off and keeps the same analytics session. Saved progress is ignored if the published flow version changed or the screen moved, and it's cleared when the onboarding finishes. Pass `resume={false}` to always start from the first screen.
|
|
185
|
+
|
|
186
|
+
When the person comes back signed in, they land on the sign-up screen again. Have `onAction` check for an existing session and resolve `true` straight away, as in the example.
|
|
187
|
+
|
|
188
|
+
**Placement.** Ask after the questions and the personalized result, before the paywall. People have invested in the onboarding by then, and purchases can be tied to the account. Offer a way to continue without an account in your sign-up sheet unless the app needs one: App Store guideline 5.1.1 rejects required sign-up for apps without account-based features. Note that the screen's Skip ends the whole onboarding, so let people skip sign-up in your own sheet (resolve `true`) instead.
|
|
189
|
+
|
|
190
|
+
## A/B tests
|
|
191
|
+
|
|
192
|
+
**Flow tests.** A published flow can carry `experiment: { id, name, controlWeight, variants }`, where each variant has its own full `screens`. The flow's own `screens` are version A. Each install is assigned one version by a stable hash of an install id saved in AsyncStorage, so a person keeps the same version on later launches. Every event during that onboarding carries `variant: "flow-a"`, `"flow-b"`, and so on, and OnboardingYak compares started and finished per version. Screen tests are paused while a flow test runs. If a version's screens fail validation, the SDK runs version A. Older SDKs always run version A.
|
|
193
|
+
|
|
194
|
+
**Screen tests.** A screen with `experiment: { controlWeight, variants }` shows one of up to four versions: the original (`a`) or a variant that changes `title`, `subtitle`, `button`, or `illustration`. Weights split traffic. Each install is assigned by a stable hash, so a person keeps the same version across launches. `screen_viewed` and `screen_completed` events on that screen include `variant`, and OnboardingYak compares how many people continued and finished per version. Variant copy is shown as written, without translations. Older SDKs always show the original and send events without a variant, which count toward version A.
|
|
195
|
+
|
|
196
|
+
## Social proof
|
|
197
|
+
|
|
198
|
+
`proof` adds a star rating (`score` 1–5, `count`, `label`), up to three testimonials (`quote`, `author`, `detail`, `stars`), and a `stat` line, shown with a short staggered entrance. Hide it with `theme.showProof: false`. Use real reviews and numbers only. Older SDKs skip the block.
|
|
199
|
+
|
|
200
|
+
## Loading screens and checklists
|
|
201
|
+
|
|
202
|
+
A screen with a `loader` shows a ring that fills to 100% while each step gets a check mark. The button stays disabled until loading finishes. By default the flow then moves on by itself; set `autoAdvance: false` to wait for a tap. A `checklist` shows benefit rows with check marks, and is often used on paywalls. Set `theme.showLoader`, `theme.showChecklist`, or `theme.showCharts` to `false` to hide them without losing their settings. A hidden loader doesn't hold up the button.
|
|
203
|
+
|
|
204
|
+
Entrance animations, graph drawing, and carousel autoplay are skipped when the device's reduced-motion setting is on. The loading ring still fills, because it shows progress.
|
|
205
|
+
|
|
206
|
+
## Videos and phone mockups
|
|
207
|
+
|
|
208
|
+
Any image slot (the screen image, carousel slides, and full-screen backgrounds) can hold a video instead: a link ending in `.mp4`, `.m4v`, `.mov`, or `.webm`, or a video uploaded in the studio. The SDK has no video dependency of its own, so pass `renderVideo` to draw them. It should fill `style`, loop, and stay muted. With expo-video:
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
import { useVideoPlayer, VideoView } from 'expo-video';
|
|
212
|
+
import { Onboarding, type OnboardingVideo } from '@onboarding-yak/react-native';
|
|
213
|
+
|
|
214
|
+
function FlowVideo({ uri, resizeMode, style }: OnboardingVideo) {
|
|
215
|
+
const player = useVideoPlayer(uri, (player) => { player.loop = true; player.muted = true; player.play(); });
|
|
216
|
+
return <VideoView player={player} style={style} contentFit={resizeMode === 'stretch' ? 'fill' : resizeMode} nativeControls={false} />;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
<Onboarding {...props} renderVideo={(video) => <FlowVideo {...video} />} />
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Without `renderVideo`, a video slot shows the screen's illustration color. Videos uploaded in the studio are embedded as `data:` URLs; if your player can't play those, write them to a file first (for example with expo-file-system) or link hosted videos instead.
|
|
223
|
+
|
|
224
|
+
`theme.imageFrame` set to `iphone` or `android` shows an inline image, carousel, or video as a screenshot inside a phone. The phone's height follows `imageHeight`; its width follows the phone's proportions. Background images are never framed. Older SDKs show the image without the phone.
|
|
225
|
+
|
|
226
|
+
## Current limitations
|
|
227
|
+
|
|
228
|
+
The package is an early prototype and has not yet been published to npm. Flows use the app-specific `flowId` created in OnboardingYak. When the SDK starts, it sends anonymous `flow_started` and `screen_viewed` events. Advancing sends `screen_completed`; finishing or skipping sends `flow_completed`. Events include app ID, screen ID, flow version, and an ephemeral session ID. Answer text and selected answers are never sent as analytics events.
|
|
229
|
+
|
|
230
|
+
Question screens require an answer: a single choice, or any of the answer formats above. `onAnswer(screen, answer)` receives each submitted response; `onComplete(answers)` receives all submitted values keyed by screen ID so the host app can use or persist them. Uploaded screen illustrations are included in the published flow and rendered by the native SDK.
|
|
231
|
+
|
|
232
|
+
## Developing and releasing the SDK
|
|
233
|
+
|
|
234
|
+
The package ships compiled JavaScript and type declarations from `lib/`, built from `src/` with `npm run build`. This folder has its own `node_modules` with React and React Native for type-checking, so don't link it into an app with `file:../path` or a symlink: Metro would load a second copy of React. Test a change in an app from a tarball instead, the same way npm delivers it:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
npm run build && npm pack --pack-destination ../path/to/app/vendor
|
|
238
|
+
cd ../path/to/app && npm install ./vendor/onboardingyak-react-native-<version>.tgz
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
To release, bump `version` in `package.json`, then run `npm publish` while logged in to an npm account that's a member of the `onboarding-yak` org. `prepublishOnly` rebuilds `lib/` first, and `publishConfig` publishes the package as public.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const DARK_COLOR_KEYS: readonly ["background", "text", "muted", "primary", "cardBackground", "overlayColor"];
|
|
2
|
+
export type DarkColorKey = (typeof DARK_COLOR_KEYS)[number];
|
|
3
|
+
export type DarkColors = Partial<Record<DarkColorKey, string>>;
|
|
4
|
+
/** Dark-mode colors for a screen: saved overrides first, otherwise a dark version of the light palette that keeps its hue. */
|
|
5
|
+
export declare function darkColors(light: Record<DarkColorKey, string>, overrides?: DarkColors): Record<DarkColorKey, string>;
|
|
6
|
+
//# sourceMappingURL=appearance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appearance.d.ts","sourceRoot":"","sources":["../src/appearance.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,eAAe,uFAAwF,CAAC;AACrH,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAC5D,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;AAqB/D,8HAA8H;AAC9H,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,EAAE,SAAS,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAepH"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Mirrors lib/flow-schema.ts so the SDK derives the same dark palette as the studio preview.
|
|
2
|
+
export const DARK_COLOR_KEYS = ['background', 'text', 'muted', 'primary', 'cardBackground', 'overlayColor'];
|
|
3
|
+
function channels(hex) { return [1, 3, 5].map((index) => Number.parseInt(hex.slice(index, index + 2), 16) / 255); }
|
|
4
|
+
function luminanceOf(hex) {
|
|
5
|
+
const [r, g, b] = channels(hex).map((part) => part <= 0.03928 ? part / 12.92 : ((part + 0.055) / 1.055) ** 2.4);
|
|
6
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
7
|
+
}
|
|
8
|
+
function hslOf(hex) {
|
|
9
|
+
const [r, g, b] = channels(hex);
|
|
10
|
+
const max = Math.max(r, g, b);
|
|
11
|
+
const min = Math.min(r, g, b);
|
|
12
|
+
const l = (max + min) / 2;
|
|
13
|
+
const d = max - min;
|
|
14
|
+
if (!d)
|
|
15
|
+
return { h: 0, s: 0, l };
|
|
16
|
+
const s = d / (1 - Math.abs(2 * l - 1));
|
|
17
|
+
const h = max === r ? ((g - b) / d) % 6 : max === g ? (b - r) / d + 2 : (r - g) / d + 4;
|
|
18
|
+
return { h: (h * 60 + 360) % 360, s, l };
|
|
19
|
+
}
|
|
20
|
+
function hexOf(h, s, l) {
|
|
21
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
22
|
+
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
23
|
+
const m = l - c / 2;
|
|
24
|
+
const [r, g, b] = h < 60 ? [c, x, 0] : h < 120 ? [x, c, 0] : h < 180 ? [0, c, x] : h < 240 ? [0, x, c] : h < 300 ? [x, 0, c] : [c, 0, x];
|
|
25
|
+
return `#${[r, g, b].map((part) => Math.round((part + m) * 255).toString(16).padStart(2, '0')).join('')}`;
|
|
26
|
+
}
|
|
27
|
+
/** Dark-mode colors for a screen: saved overrides first, otherwise a dark version of the light palette that keeps its hue. */
|
|
28
|
+
export function darkColors(light, overrides) {
|
|
29
|
+
const valid = (value) => typeof value === 'string' && /^#[\da-f]{6}$/i.test(value);
|
|
30
|
+
const pick = (key, fallback) => valid(overrides?.[key]) ? overrides[key] : fallback;
|
|
31
|
+
if (luminanceOf(light.background) < 0.18)
|
|
32
|
+
return Object.fromEntries(DARK_COLOR_KEYS.map((key) => [key, pick(key, light[key])]));
|
|
33
|
+
const tint = hslOf(light.background).s > 0.06 ? hslOf(light.background).h : hslOf(light.primary).h;
|
|
34
|
+
const primary = hslOf(light.primary);
|
|
35
|
+
const derived = {
|
|
36
|
+
background: hexOf(tint, 0.14, 0.08),
|
|
37
|
+
cardBackground: hexOf(tint, 0.12, 0.14),
|
|
38
|
+
text: '#f1f3f2',
|
|
39
|
+
muted: hexOf(tint, 0.08, 0.7),
|
|
40
|
+
primary: luminanceOf(light.primary) < 0.03 ? '#f1f3f2' : primary.l < 0.62 ? hexOf(primary.h, Math.min(0.75, primary.s + 0.08), 0.64) : light.primary,
|
|
41
|
+
overlayColor: light.overlayColor,
|
|
42
|
+
};
|
|
43
|
+
return Object.fromEntries(DARK_COLOR_KEYS.map((key) => [key, pick(key, derived[key])]));
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=appearance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appearance.js","sourceRoot":"","sources":["../src/appearance.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"countries.d.ts","sourceRoot":"","sources":["../src/countries.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,SAAS,EAAE,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,EAqBvE,CAAC;AAEF,uFAAuF;AACvF,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,UAElC"}
|
package/lib/countries.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// ISO 3166-1 alpha-2 codes with English names, used by the country picker in the SDK and the studio.
|
|
2
|
+
export const COUNTRIES = [
|
|
3
|
+
['AF', 'Afghanistan'], ['AL', 'Albania'], ['DZ', 'Algeria'], ['AD', 'Andorra'], ['AO', 'Angola'], ['AG', 'Antigua and Barbuda'], ['AR', 'Argentina'], ['AM', 'Armenia'], ['AU', 'Australia'], ['AT', 'Austria'],
|
|
4
|
+
['AZ', 'Azerbaijan'], ['BS', 'Bahamas'], ['BH', 'Bahrain'], ['BD', 'Bangladesh'], ['BB', 'Barbados'], ['BY', 'Belarus'], ['BE', 'Belgium'], ['BZ', 'Belize'], ['BJ', 'Benin'], ['BT', 'Bhutan'],
|
|
5
|
+
['BO', 'Bolivia'], ['BA', 'Bosnia and Herzegovina'], ['BW', 'Botswana'], ['BR', 'Brazil'], ['BN', 'Brunei'], ['BG', 'Bulgaria'], ['BF', 'Burkina Faso'], ['BI', 'Burundi'], ['CV', 'Cabo Verde'], ['KH', 'Cambodia'],
|
|
6
|
+
['CM', 'Cameroon'], ['CA', 'Canada'], ['CF', 'Central African Republic'], ['TD', 'Chad'], ['CL', 'Chile'], ['CN', 'China'], ['CO', 'Colombia'], ['KM', 'Comoros'], ['CG', 'Congo'], ['CD', 'Congo (DRC)'],
|
|
7
|
+
['CR', 'Costa Rica'], ['CI', 'Côte d’Ivoire'], ['HR', 'Croatia'], ['CU', 'Cuba'], ['CY', 'Cyprus'], ['CZ', 'Czechia'], ['DK', 'Denmark'], ['DJ', 'Djibouti'], ['DM', 'Dominica'], ['DO', 'Dominican Republic'],
|
|
8
|
+
['EC', 'Ecuador'], ['EG', 'Egypt'], ['SV', 'El Salvador'], ['GQ', 'Equatorial Guinea'], ['ER', 'Eritrea'], ['EE', 'Estonia'], ['SZ', 'Eswatini'], ['ET', 'Ethiopia'], ['FJ', 'Fiji'], ['FI', 'Finland'],
|
|
9
|
+
['FR', 'France'], ['GA', 'Gabon'], ['GM', 'Gambia'], ['GE', 'Georgia'], ['DE', 'Germany'], ['GH', 'Ghana'], ['GR', 'Greece'], ['GD', 'Grenada'], ['GT', 'Guatemala'], ['GN', 'Guinea'],
|
|
10
|
+
['GW', 'Guinea-Bissau'], ['GY', 'Guyana'], ['HT', 'Haiti'], ['HN', 'Honduras'], ['HK', 'Hong Kong'], ['HU', 'Hungary'], ['IS', 'Iceland'], ['IN', 'India'], ['ID', 'Indonesia'], ['IR', 'Iran'],
|
|
11
|
+
['IQ', 'Iraq'], ['IE', 'Ireland'], ['IL', 'Israel'], ['IT', 'Italy'], ['JM', 'Jamaica'], ['JP', 'Japan'], ['JO', 'Jordan'], ['KZ', 'Kazakhstan'], ['KE', 'Kenya'], ['KI', 'Kiribati'],
|
|
12
|
+
['XK', 'Kosovo'], ['KW', 'Kuwait'], ['KG', 'Kyrgyzstan'], ['LA', 'Laos'], ['LV', 'Latvia'], ['LB', 'Lebanon'], ['LS', 'Lesotho'], ['LR', 'Liberia'], ['LY', 'Libya'], ['LI', 'Liechtenstein'],
|
|
13
|
+
['LT', 'Lithuania'], ['LU', 'Luxembourg'], ['MO', 'Macao'], ['MG', 'Madagascar'], ['MW', 'Malawi'], ['MY', 'Malaysia'], ['MV', 'Maldives'], ['ML', 'Mali'], ['MT', 'Malta'], ['MH', 'Marshall Islands'],
|
|
14
|
+
['MR', 'Mauritania'], ['MU', 'Mauritius'], ['MX', 'Mexico'], ['FM', 'Micronesia'], ['MD', 'Moldova'], ['MC', 'Monaco'], ['MN', 'Mongolia'], ['ME', 'Montenegro'], ['MA', 'Morocco'], ['MZ', 'Mozambique'],
|
|
15
|
+
['MM', 'Myanmar'], ['NA', 'Namibia'], ['NR', 'Nauru'], ['NP', 'Nepal'], ['NL', 'Netherlands'], ['NZ', 'New Zealand'], ['NI', 'Nicaragua'], ['NE', 'Niger'], ['NG', 'Nigeria'], ['KP', 'North Korea'],
|
|
16
|
+
['MK', 'North Macedonia'], ['NO', 'Norway'], ['OM', 'Oman'], ['PK', 'Pakistan'], ['PW', 'Palau'], ['PS', 'Palestine'], ['PA', 'Panama'], ['PG', 'Papua New Guinea'], ['PY', 'Paraguay'], ['PE', 'Peru'],
|
|
17
|
+
['PH', 'Philippines'], ['PL', 'Poland'], ['PT', 'Portugal'], ['PR', 'Puerto Rico'], ['QA', 'Qatar'], ['RO', 'Romania'], ['RU', 'Russia'], ['RW', 'Rwanda'], ['KN', 'Saint Kitts and Nevis'], ['LC', 'Saint Lucia'],
|
|
18
|
+
['VC', 'Saint Vincent and the Grenadines'], ['WS', 'Samoa'], ['SM', 'San Marino'], ['ST', 'São Tomé and Príncipe'], ['SA', 'Saudi Arabia'], ['SN', 'Senegal'], ['RS', 'Serbia'], ['SC', 'Seychelles'], ['SL', 'Sierra Leone'], ['SG', 'Singapore'],
|
|
19
|
+
['SK', 'Slovakia'], ['SI', 'Slovenia'], ['SB', 'Solomon Islands'], ['SO', 'Somalia'], ['ZA', 'South Africa'], ['KR', 'South Korea'], ['SS', 'South Sudan'], ['ES', 'Spain'], ['LK', 'Sri Lanka'], ['SD', 'Sudan'],
|
|
20
|
+
['SR', 'Suriname'], ['SE', 'Sweden'], ['CH', 'Switzerland'], ['SY', 'Syria'], ['TW', 'Taiwan'], ['TJ', 'Tajikistan'], ['TZ', 'Tanzania'], ['TH', 'Thailand'], ['TL', 'Timor-Leste'], ['TG', 'Togo'],
|
|
21
|
+
['TO', 'Tonga'], ['TT', 'Trinidad and Tobago'], ['TN', 'Tunisia'], ['TR', 'Türkiye'], ['TM', 'Turkmenistan'], ['TV', 'Tuvalu'], ['UG', 'Uganda'], ['UA', 'Ukraine'], ['AE', 'United Arab Emirates'], ['GB', 'United Kingdom'],
|
|
22
|
+
['US', 'United States'], ['UY', 'Uruguay'], ['UZ', 'Uzbekistan'], ['VU', 'Vanuatu'], ['VA', 'Vatican City'], ['VE', 'Venezuela'], ['VN', 'Vietnam'], ['YE', 'Yemen'], ['ZM', 'Zambia'], ['ZW', 'Zimbabwe'],
|
|
23
|
+
];
|
|
24
|
+
/** Flag emoji built from regional indicator letters, so no image assets are needed. */
|
|
25
|
+
export function flagOf(code) {
|
|
26
|
+
return String.fromCodePoint(...[...code.toUpperCase()].map((letter) => 0x1f1a5 + letter.charCodeAt(0)));
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=countries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"countries.js","sourceRoot":"","sources":["../src/countries.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** Another version of a screen for an A/B test. Only the fields it sets differ from the original (version A). */
|
|
2
|
+
export type ScreenVariant = {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
weight: number;
|
|
6
|
+
title?: string;
|
|
7
|
+
subtitle?: string;
|
|
8
|
+
button?: string;
|
|
9
|
+
illustration?: string;
|
|
10
|
+
};
|
|
11
|
+
/** An A/B test on one screen: the original keeps `controlWeight` of traffic, each variant its own weight. */
|
|
12
|
+
export type ScreenExperiment = {
|
|
13
|
+
controlWeight: number;
|
|
14
|
+
variants: ScreenVariant[];
|
|
15
|
+
};
|
|
16
|
+
export declare const CONTROL_VARIANT = "a";
|
|
17
|
+
/** The version a session sees for a screen: 'a' for the original, otherwise a variant id. */
|
|
18
|
+
export declare function pickVariant(screen: {
|
|
19
|
+
id: string;
|
|
20
|
+
experiment?: ScreenExperiment;
|
|
21
|
+
}, sessionId: string): string;
|
|
22
|
+
/** The screen as a given version shows it. */
|
|
23
|
+
export declare function applyVariant<T extends {
|
|
24
|
+
title: string;
|
|
25
|
+
subtitle: string;
|
|
26
|
+
button: string;
|
|
27
|
+
illustration?: string;
|
|
28
|
+
experiment?: ScreenExperiment;
|
|
29
|
+
}>(screen: T, variantId: string): T;
|
|
30
|
+
/** A whole-onboarding A/B test; the flow's own screens are version A. */
|
|
31
|
+
export type FlowTest<Screen = unknown> = {
|
|
32
|
+
id: string;
|
|
33
|
+
name: string;
|
|
34
|
+
controlWeight: number;
|
|
35
|
+
variants: {
|
|
36
|
+
id: string;
|
|
37
|
+
name: string;
|
|
38
|
+
weight: number;
|
|
39
|
+
screens: Screen[];
|
|
40
|
+
}[];
|
|
41
|
+
};
|
|
42
|
+
/** The flow-test version an install sees: 'a' for the original, otherwise a variant id. Same rule as lib/flow-schema.ts. */
|
|
43
|
+
export declare function pickFlowVersion(test: FlowTest | undefined, installId: string): string;
|
|
44
|
+
//# sourceMappingURL=experiment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"experiment.d.ts","sourceRoot":"","sources":["../src/experiment.ts"],"names":[],"mappings":"AACA,iHAAiH;AACjH,MAAM,MAAM,aAAa,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AACpJ,6GAA6G;AAC7G,MAAM,MAAM,gBAAgB,GAAG;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,aAAa,EAAE,CAAA;CAAE,CAAC;AACpF,eAAO,MAAM,eAAe,MAAM,CAAC;AASnC,6FAA6F;AAC7F,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,gBAAgB,CAAA;CAAE,EAAE,SAAS,EAAE,MAAM,UASnG;AAED,8CAA8C;AAC9C,wBAAgB,YAAY,CAAC,CAAC,SAAS;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,gBAAgB,CAAA;CAAE,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,CAAC,CAUjL;AAED,yEAAyE;AACzE,MAAM,MAAM,QAAQ,CAAC,MAAM,GAAG,OAAO,IAAI;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAA;CAAE,CAAC;AAE1K,4HAA4H;AAC5H,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,UAG5E"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export const CONTROL_VARIANT = 'a';
|
|
2
|
+
/** FNV-1a: a small, stable hash so the same session always lands in the same version. */
|
|
3
|
+
function hashOf(text) {
|
|
4
|
+
let hash = 0x811c9dc5;
|
|
5
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
6
|
+
hash ^= text.charCodeAt(index);
|
|
7
|
+
hash = Math.imul(hash, 0x01000193);
|
|
8
|
+
}
|
|
9
|
+
return hash >>> 0;
|
|
10
|
+
}
|
|
11
|
+
/** The version a session sees for a screen: 'a' for the original, otherwise a variant id. */
|
|
12
|
+
export function pickVariant(screen, sessionId) {
|
|
13
|
+
const experiment = screen.experiment;
|
|
14
|
+
if (!experiment?.variants?.length)
|
|
15
|
+
return CONTROL_VARIANT;
|
|
16
|
+
const weights = [{ id: CONTROL_VARIANT, weight: Math.max(0, experiment.controlWeight) }, ...experiment.variants.map((variant) => ({ id: variant.id, weight: Math.max(0, variant.weight) }))];
|
|
17
|
+
const total = weights.reduce((sum, item) => sum + item.weight, 0);
|
|
18
|
+
if (total <= 0)
|
|
19
|
+
return CONTROL_VARIANT;
|
|
20
|
+
let slot = hashOf(`${sessionId}:${screen.id}`) % total;
|
|
21
|
+
for (const item of weights) {
|
|
22
|
+
if (slot < item.weight)
|
|
23
|
+
return item.id;
|
|
24
|
+
slot -= item.weight;
|
|
25
|
+
}
|
|
26
|
+
return CONTROL_VARIANT;
|
|
27
|
+
}
|
|
28
|
+
/** The screen as a given version shows it. */
|
|
29
|
+
export function applyVariant(screen, variantId) {
|
|
30
|
+
const variant = screen.experiment?.variants.find((item) => item.id === variantId);
|
|
31
|
+
if (!variant)
|
|
32
|
+
return screen;
|
|
33
|
+
return {
|
|
34
|
+
...screen,
|
|
35
|
+
title: variant.title ?? screen.title,
|
|
36
|
+
subtitle: variant.subtitle ?? screen.subtitle,
|
|
37
|
+
button: variant.button ?? screen.button,
|
|
38
|
+
illustration: variant.illustration ?? screen.illustration,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** The flow-test version an install sees: 'a' for the original, otherwise a variant id. Same rule as lib/flow-schema.ts. */
|
|
42
|
+
export function pickFlowVersion(test, installId) {
|
|
43
|
+
if (!test?.variants?.length)
|
|
44
|
+
return CONTROL_VARIANT;
|
|
45
|
+
return pickVariant({ id: `flow-test:${test.id}`, experiment: { controlWeight: test.controlWeight, variants: test.variants.map(({ id, name, weight }) => ({ id, name, weight })) } }, installId);
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=experiment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"experiment.js","sourceRoot":"","sources":["../src/experiment.ts"],"names":[],"mappings":"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"}
|