@picked/client 0.0.71-next.1 → 0.0.71-next.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 (148) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/{api-CgqRvuKL.js → api-B0ydpQBE.js} +1 -1
  3. package/dist/{api-CH2FNa6S.cjs → api-CObostrU.cjs} +1 -1
  4. package/dist/cjs/index.js +2 -2
  5. package/dist/cjs/plugins/dynamic-experiences/index.js +1 -1
  6. package/dist/cjs/server/index.js +1 -1
  7. package/dist/docs/explanation/conditions-and-the-logic-engine.md +94 -0
  8. package/dist/docs/explanation/feature-flag-allocation.md +176 -0
  9. package/dist/docs/explanation/how-picked-works.md +102 -0
  10. package/dist/docs/explanation/how-pickedql-works.md +76 -0
  11. package/dist/docs/explanation/pages-and-flows.md +64 -0
  12. package/dist/docs/explanation/the-content-model.md +77 -0
  13. package/dist/docs/explanation/the-two-apis.md +68 -0
  14. package/dist/docs/explanation/why-server-side-rendering.md +76 -0
  15. package/dist/docs/explanation/workspaces-and-versions.md +71 -0
  16. package/dist/docs/how-to/add-a-step-field-or-action.md +43 -0
  17. package/dist/docs/{pickedql/examples.md → how-to/analytics/index.md} +19 -6
  18. package/dist/docs/how-to/archive-or-delete-a-workspace.md +46 -0
  19. package/dist/docs/how-to/authenticate-with-a-magic-link.md +40 -0
  20. package/dist/docs/how-to/authenticate-with-a-password.md +51 -0
  21. package/dist/docs/how-to/authenticate-with-an-api-key.md +51 -0
  22. package/dist/docs/how-to/authenticate-with-oauth.md +40 -0
  23. package/dist/docs/{flow-builder/building-flows.md → how-to/build-a-flow.md} +13 -9
  24. package/dist/docs/how-to/connect-the-mcp-server.md +38 -0
  25. package/dist/docs/how-to/create-a-project.md +29 -0
  26. package/dist/docs/how-to/create-an-experience.md +42 -0
  27. package/dist/docs/how-to/create-and-revoke-an-api-key.md +37 -0
  28. package/dist/docs/how-to/debug-with-the-chrome-extension.md +57 -0
  29. package/dist/docs/how-to/define-a-page-layout.md +39 -0
  30. package/dist/docs/how-to/fill-and-publish-a-page.md +50 -0
  31. package/dist/docs/{gtm-template.md → how-to/install-the-gtm-template.md} +9 -3
  32. package/dist/docs/how-to/install-the-sdk.md +68 -0
  33. package/dist/docs/how-to/install-the-vite-plugin.md +56 -0
  34. package/dist/docs/how-to/invite-and-remove-members.md +34 -0
  35. package/dist/docs/how-to/preview-a-workspace.md +28 -0
  36. package/dist/docs/how-to/publish-a-workspace.md +30 -0
  37. package/dist/docs/how-to/read-a-feature-flag.md +101 -0
  38. package/dist/docs/how-to/register-a-component.md +55 -0
  39. package/dist/docs/how-to/register-an-action-handler.md +67 -0
  40. package/dist/docs/how-to/render-a-step-form.md +132 -0
  41. package/dist/docs/how-to/render-an-unpublished-preview.md +41 -0
  42. package/dist/docs/how-to/reuse-a-section.md +37 -0
  43. package/dist/docs/how-to/reuse-a-step-field-or-action.md +28 -0
  44. package/dist/docs/how-to/roll-back-a-published-version.md +26 -0
  45. package/dist/docs/how-to/run-an-ab-test.md +49 -0
  46. package/dist/docs/how-to/run-code-on-a-form-submission.md +125 -0
  47. package/dist/docs/how-to/server-render-a-page.md +81 -0
  48. package/dist/docs/how-to/set-a-content-security-policy.md +32 -0
  49. package/dist/docs/how-to/set-up-mfa.md +69 -0
  50. package/dist/docs/how-to/upload-and-use-an-asset.mdx +73 -0
  51. package/dist/docs/how-to/validate-a-field.md +52 -0
  52. package/dist/docs/how-to/write-a-condition.md +45 -0
  53. package/dist/docs/how-to/write-a-predicate.md +60 -0
  54. package/dist/docs/index.md +41 -70
  55. package/dist/docs/reference/dashboard/actions.mdx +53 -0
  56. package/dist/docs/{api-keys.mdx → reference/dashboard/api-keys.mdx} +7 -15
  57. package/dist/docs/reference/dashboard/conditions.mdx +42 -0
  58. package/dist/docs/reference/dashboard/content/asset-library.mdx +63 -0
  59. package/dist/docs/{content → reference/dashboard/content}/content-entries.mdx +11 -3
  60. package/dist/docs/{content → reference/dashboard/content}/layouts.mdx +11 -35
  61. package/dist/docs/reference/dashboard/content/pages.mdx +35 -0
  62. package/dist/docs/reference/dashboard/content/sections.mdx +28 -0
  63. package/dist/docs/{experiences.mdx → reference/dashboard/experiences.mdx} +9 -16
  64. package/dist/docs/{features.mdx → reference/dashboard/features.mdx} +26 -41
  65. package/dist/docs/{fields.mdx → reference/dashboard/fields.mdx} +26 -20
  66. package/dist/docs/{flow-builder/overview.mdx → reference/dashboard/flow-builder/canvas.mdx} +8 -13
  67. package/dist/docs/{flow-builder → reference/dashboard/flow-builder}/condition-builder.md +5 -13
  68. package/dist/docs/{flow-builder → reference/dashboard/flow-builder}/keyboard-shortcuts.md +7 -1
  69. package/dist/docs/{flow-builder → reference/dashboard/flow-builder}/nodes.md +10 -3
  70. package/dist/docs/reference/dashboard/projects.mdx +44 -0
  71. package/dist/docs/reference/dashboard/reports.mdx +43 -0
  72. package/dist/docs/reference/dashboard/steps.mdx +37 -0
  73. package/dist/docs/{team-management.md → reference/dashboard/team-management.md} +5 -22
  74. package/dist/docs/{workspace-versioning.mdx → reference/dashboard/workspace-versioning.mdx} +6 -10
  75. package/dist/docs/reference/dashboard/workspaces.mdx +76 -0
  76. package/dist/docs/{delivery-api → reference/delivery-api}/experience-content.md +11 -4
  77. package/dist/docs/reference/delivery-api/index.md +45 -0
  78. package/dist/docs/{delivery-api → reference/delivery-api}/preview-caching-crawling.md +12 -5
  79. package/dist/docs/{delivery-api → reference/delivery-api}/project-config.md +11 -4
  80. package/dist/docs/reference/glossary.md +173 -0
  81. package/dist/docs/reference/management-api/authentication.md +74 -0
  82. package/dist/docs/{api → reference/management-api}/authorization-and-scopes.md +10 -3
  83. package/dist/docs/{api → reference/management-api}/error-handling.md +9 -3
  84. package/dist/docs/{api → reference/management-api}/pagination.md +6 -0
  85. package/dist/docs/{pickedql → reference/pickedql}/data-sources.md +16 -9
  86. package/dist/docs/{pickedql → reference/pickedql}/dimensions.md +7 -0
  87. package/dist/docs/{pickedql → reference/pickedql}/filtering.md +7 -0
  88. package/dist/docs/reference/pickedql/index.md +18 -0
  89. package/dist/docs/{pickedql → reference/pickedql}/schema.md +13 -6
  90. package/dist/docs/{pickedql → reference/pickedql}/virtual-functions.md +7 -0
  91. package/dist/docs/reference/sdk/actions.md +30 -0
  92. package/dist/docs/{sdk → reference/sdk}/conditions.md +8 -1
  93. package/dist/docs/reference/sdk/content-security-policy.md +21 -0
  94. package/dist/docs/reference/sdk/experiences.md +51 -0
  95. package/dist/docs/{sdk → reference/sdk}/features.md +30 -117
  96. package/dist/docs/{sdk → reference/sdk}/fields.md +10 -88
  97. package/dist/docs/reference/sdk/index.md +68 -0
  98. package/dist/docs/{sdk → reference/sdk}/plugins/create-experience.md +10 -22
  99. package/dist/docs/reference/sdk/plugins/dynamic-experiences.md +26 -0
  100. package/dist/docs/reference/sdk/plugins/index.md +29 -0
  101. package/dist/docs/{sdk → reference/sdk}/plugins/window-object.md +6 -0
  102. package/dist/docs/reference/sdk/predicates.md +43 -0
  103. package/dist/docs/reference/sdk/ssr/index.md +33 -0
  104. package/dist/docs/{sdk → reference/sdk}/ssr/rendering-and-hydration.md +4 -63
  105. package/dist/docs/{sdk → reference/sdk}/ssr/serialize.md +6 -33
  106. package/dist/docs/{sdk → reference/sdk}/ssr/slots-and-placements.md +7 -1
  107. package/dist/docs/reference/sdk/steps.md +87 -0
  108. package/dist/docs/reference/tools/chrome-extension.md +16 -0
  109. package/dist/docs/{image-optimisation-service.md → reference/tools/image-optimisation-service.md} +8 -0
  110. package/dist/docs/{mcp.md → reference/tools/mcp.md} +5 -45
  111. package/dist/docs/{vite-plugin.md → reference/tools/vite-plugin.md} +8 -38
  112. package/dist/docs/{api/index.md → tutorials/first-api-call.md} +27 -15
  113. package/dist/docs/tutorials/first-experience-in-the-dashboard.mdx +143 -0
  114. package/dist/docs/tutorials/first-experience.mdx +255 -0
  115. package/dist/docs/tutorials/first-server-rendered-page.mdx +186 -0
  116. package/dist/esm/index.js +260 -257
  117. package/dist/esm/plugins/dynamic-experiences/index.js +2 -2
  118. package/dist/esm/server/index.js +1 -1
  119. package/dist/{plugin-DCW-tnjO.cjs → plugin-CNna0UjB.cjs} +1 -1
  120. package/dist/{plugin-D2UqFikQ.js → plugin-DUOJpHaq.js} +1 -1
  121. package/dist/types/entities/feature/feature.d.ts +16 -0
  122. package/dist/types/entities/feature/feature.d.ts.map +1 -1
  123. package/dist/types/picked.d.ts.map +1 -1
  124. package/package.json +1 -1
  125. package/dist/docs/actions.mdx +0 -98
  126. package/dist/docs/api/authentication.md +0 -403
  127. package/dist/docs/chrome-extension.md +0 -68
  128. package/dist/docs/conditions.mdx +0 -72
  129. package/dist/docs/content/asset-library.mdx +0 -124
  130. package/dist/docs/content/pages.mdx +0 -82
  131. package/dist/docs/content/sections.mdx +0 -44
  132. package/dist/docs/delivery-api/index.md +0 -53
  133. package/dist/docs/guides/build-a-personalized-page.mdx +0 -161
  134. package/dist/docs/pickedql/index.md +0 -54
  135. package/dist/docs/projects.mdx +0 -44
  136. package/dist/docs/reports.mdx +0 -20
  137. package/dist/docs/sdk/actions.md +0 -82
  138. package/dist/docs/sdk/content-security-policy.md +0 -33
  139. package/dist/docs/sdk/experiences.md +0 -64
  140. package/dist/docs/sdk/feature-allocation.md +0 -161
  141. package/dist/docs/sdk/index.md +0 -98
  142. package/dist/docs/sdk/plugins/dynamic-experiences.md +0 -84
  143. package/dist/docs/sdk/plugins/index.md +0 -9
  144. package/dist/docs/sdk/predicates.md +0 -128
  145. package/dist/docs/sdk/ssr/index.md +0 -79
  146. package/dist/docs/sdk/steps.md +0 -479
  147. package/dist/docs/steps.mdx +0 -53
  148. package/dist/docs/workspaces.mdx +0 -133
@@ -1,4 +1,5 @@
1
1
  ---
2
+ type: reference
2
3
  layout: docs
3
4
  ---
4
5
 
@@ -228,3 +229,9 @@ SELECT feature_users('homepage_test', 'control', 'v1') FROM features
228
229
  | Function | Description |
229
230
  |---|---|
230
231
  | `event_count()` | Total event count (from pre-aggregation) |
232
+
233
+ ## Related
234
+
235
+ - [Filters](/reference/pickedql/filtering) — the filter string a function takes.
236
+ - [Analytics queries](/how-to/analytics)
237
+ - [About PickedQL](/explanation/how-pickedql-works)
@@ -0,0 +1,30 @@
1
+ ---
2
+ type: reference
3
+ layout: docs
4
+ ---
5
+
6
+ # Actions
7
+
8
+ Register an action handler on the Picked client. The SDK then invokes the handler
9
+ when an experience flow reaches an action node.
10
+
11
+ ## Outcome and branch
12
+
13
+ | Handler outcome | Branch the flow takes |
14
+ | --- | --- |
15
+ | Throws, or its promise rejects | `onFail` |
16
+ | Resolves to any value, and that includes `false` | `onSuccess` |
17
+
18
+ An action node can also carry one plain connection, with no success or failure branch. The
19
+ flow then continues through it on either outcome.
20
+
21
+ An action does not interrupt your visitor. The current step stays on the screen while an
22
+ async action runs.
23
+
24
+ ## Related
25
+
26
+ - [How to register an action handler](/how-to/register-an-action-handler) — the handler,
27
+ and when to throw.
28
+ - [About how Picked works](/explanation/how-picked-works#how-an-action-decides-which-way-to-go)
29
+ — why a returned `false` is a success.
30
+ - [Actions](/reference/dashboard/actions) — the dashboard options.
@@ -1,4 +1,5 @@
1
1
  ---
2
+ type: reference
2
3
  layout: docs
3
4
  ---
4
5
 
@@ -50,7 +51,7 @@ isSigningUpForNewsletter.addFalse(userDoesNotWantNewsletter)
50
51
  ## Conditional fields
51
52
 
52
53
  A JSON Logic condition can show or hide a field. The condition can read an earlier
53
- response or a [predicate](./predicates):
54
+ response or a [predicate](/reference/sdk/predicates):
54
55
 
55
56
  ```ts
56
57
  import { createField } from '@picked/client/plugins/create-experience'
@@ -138,3 +139,9 @@ export const contactMethodsField = createField({
138
139
  ],
139
140
  })
140
141
  ```
142
+
143
+ ## Related
144
+
145
+ - [About conditions and the logic engine](/explanation/conditions-and-the-logic-engine)
146
+ - [How to write a condition](/how-to/write-a-condition)
147
+ - [Predicates](/reference/sdk/predicates)
@@ -0,0 +1,21 @@
1
+ ---
2
+ type: reference
3
+ layout: docs
4
+ ---
5
+
6
+ # Content Security Policy
7
+
8
+ The Picked SDK loads resources from the domains below. Add each domain to the CSP
9
+ directive that the table gives.
10
+
11
+ ## Required domains
12
+
13
+ | Domain | Directive | Purpose |
14
+ | --- | --- | --- |
15
+ | `api.picked.app` | `connect-src` | API requests, project configuration, experiences, and the record of each session |
16
+ | `evnts.pckd.dev` | `connect-src` | Analytics events, which Snowplow collects |
17
+ | `img.picked-content.com` | `img-src` | Optimised images, which the [image optimisation service](/reference/tools/image-optimisation-service) serves |
18
+
19
+ ## Related
20
+
21
+ - [How to set a Content Security Policy](/how-to/set-a-content-security-policy)
@@ -0,0 +1,51 @@
1
+ ---
2
+ type: reference
3
+ layout: docs
4
+ ---
5
+
6
+ # Experiences
7
+
8
+ Define an experience in code with `createExperience()`. You can also load an
9
+ experience from the dashboard at runtime with the
10
+ [Dynamic Experiences Plugin](/reference/sdk/plugins/dynamic-experiences).
11
+
12
+ The flow below points at a DOM selector and renders into it. A Page experience
13
+ renders through a named slot instead. Point it at `.picked-slot-<name>` and render
14
+ `<ExperienceSlot name="<name>" />` on your server. Use the slot path for a Page that
15
+ the server renders and that search engines must read. Use the selector path for a
16
+ Flow that the browser renders. See [Server-Side Rendering](/reference/sdk/ssr) and
17
+ [Slots and Placements](/reference/sdk/ssr/slots-and-placements).
18
+
19
+ ## Create an experience
20
+
21
+ ```ts
22
+ import { createExperience } from '@picked/client/plugins/create-experience'
23
+ import { adaptSvelteComponent } from '@picked/svelte'
24
+ import appCss from '../app.css?inline'
25
+
26
+ export const productPageExperience = createExperience({
27
+ id: 'product-page-experience',
28
+ name: 'Product Page Experience',
29
+ condition: {
30
+ '==': [
31
+ { var: 'location.pathname' },
32
+ '/product/',
33
+ ],
34
+ },
35
+ targets: [{ selector: 'body', placement: 'after' }],
36
+ component: adaptSvelteComponent(
37
+ import('../ui/ProductPageExperience.svelte'),
38
+ { css: [appCss] },
39
+ ),
40
+ })
41
+ ```
42
+
43
+ To register an experience that you define in code, use the
44
+ [Create Experience Plugin](/reference/sdk/plugins/create-experience). To read an experience from
45
+ the dashboard at runtime and control it there, use the
46
+ [Dynamic Experiences Plugin](/reference/sdk/plugins/dynamic-experiences).
47
+
48
+ ## Related
49
+
50
+ - [About how Picked works](/explanation/how-picked-works) — the startup order.
51
+ - [How to register a component](/how-to/register-a-component)
@@ -1,4 +1,5 @@
1
1
  ---
2
+ type: reference
2
3
  layout: docs
3
4
  ---
4
5
 
@@ -11,7 +12,7 @@ rules.
11
12
  ## Where feature flags come from
12
13
 
13
14
  You control most feature flags from the dashboard. The
14
- [dynamic experiences plugin](/sdk/plugins/dynamic-experiences) reads the options of
15
+ [dynamic experiences plugin](/reference/sdk/plugins/dynamic-experiences) reads the options of
15
16
  your project at runtime and registers every feature flag for you. You declare no feature flag in
16
17
  code.
17
18
 
@@ -24,7 +25,7 @@ For a feature flag that you control from the dashboard, the SDK makes the defini
24
25
  | `name` | The dashboard name of the feature flag |
25
26
  | `enabled` | The dashboard **Enabled** toggle |
26
27
  | `weight` | The dashboard **Weight**, 0 to 100 |
27
- | `storage` | `local`, or `session` in [preview mode](/sdk/feature-allocation#preview-mode) |
28
+ | `storage` | `local`, or `session` in [preview mode](/explanation/feature-flag-allocation#preview-mode) |
28
29
  | `decide` | Made by the SDK. It compares the stable value of the visitor for this feature flag against `weight`. |
29
30
 
30
31
  The `decide` function that the SDK makes derives from the visitor ID, the ID of the
@@ -80,26 +81,6 @@ features: {
80
81
  }
81
82
  ```
82
83
 
83
- ## How feature flags work
84
-
85
- After you register a feature flag, the SDK follows this order:
86
-
87
- 1. **Registration** — the client registers each feature flag at startup. A registration
88
- allocates nobody.
89
- 2. **Cache check** — the SDK looks in browser storage for a decision from an earlier
90
- visit. The cache key holds the feature flag `id` and `key`. When an entry is there, the
91
- feature flag starts in a resolved state. This is why Picked allocates a visitor who
92
- returns before anything reads the feature flag.
93
- 3. **Lazy evaluation** — with no cached decision, `decide` runs the first time that
94
- something reads the bucket.
95
- 4. **Storage** — the SDK writes the resolved bucket to browser storage.
96
- 5. **Event emission** — the SDK sends a `feature_decide` event, but only for a
97
- decision that it makes during this page view. A decision that step 2 restores
98
- sends nothing.
99
-
100
- See [Allocation and exposure](/sdk/feature-allocation) for what this means when you
101
- track an exposure.
102
-
103
84
  ## Overrides in the URL
104
85
 
105
86
  You can override a feature flag decision from the URL search parameters, for a test:
@@ -120,84 +101,6 @@ This is useful for QA, because you can test one bucket value and change no code.
120
101
  SDK keeps the override value in session storage. The browser clears it when you close
121
102
  the tab.
122
103
 
123
- ## Read a feature flag
124
-
125
- After you define your feature flags, your application code can read the resolved
126
- bucket value of each one. The SDK gives you a hook for each framework, and an event
127
- bus that works with any framework.
128
-
129
- :::code-group
130
-
131
- ```ts [Vanilla JS]
132
- import { createClient } from '@picked/client'
133
-
134
- const client = createClient({
135
- /* Put your client options here. */
136
- })
137
-
138
- client.eventBus.watch((event) => {
139
- if (event.type === 'feature_decide' && event.feature.id === 'my-feature') {
140
- console.log(event.feature) // { id: 'my-feature', key: 'v1', bucket: 'control' }
141
- }
142
- })
143
- ```
144
-
145
- ```svelte [Svelte]
146
- <script lang="ts">
147
- import { getFeatureResult } from '@picked/svelte/hooks'
148
-
149
- // A store that holds { id, bucket }, or undefined until the feature flag resolves.
150
- const result = getFeatureResult('my-feature')
151
- </script>
152
-
153
- {#if $result?.bucket === 'control'}
154
- <p>Control</p>
155
- {:else if $result?.bucket === 'variant-a'}
156
- <p>Variant A</p>
157
- {/if}
158
- ```
159
-
160
- ```tsx [React]
161
- import { useFeatureResult } from '@picked/react/hooks'
162
-
163
- function MyComponent() {
164
- // { id, bucket }, or undefined until the feature flag resolves.
165
- const result = useFeatureResult('my-feature')
166
-
167
- return (
168
- <div>
169
- {result?.bucket === 'control' && <p>Control</p>}
170
- {result?.bucket === 'variant-a' && <p>Variant A</p>}
171
- </div>
172
- )
173
- }
174
- ```
175
-
176
- :::
177
-
178
- ### Choose an accessor
179
-
180
- Each accessor returns a different value. They also differ in whether a read resolves
181
- the feature flag. A read that resolves the feature flag commits the visitor to a bucket and reports
182
- the decision. See [Allocation and exposure](/sdk/feature-allocation).
183
-
184
- | API | Returns | Does a read resolve the feature flag? |
185
- | --- | --- | --- |
186
- | `useFeature` / `getFeature` | `() => Promise<bucket>` | Only when you call the function that it returns |
187
- | `useFeatureResult` / `getFeatureResult` | `{ id, bucket }`, and `undefined` until it resolves | Yes, as soon as the component renders |
188
- | `useFeatureEnabled` / `getFeatureEnabled` | `boolean \| undefined`, the dashboard **Enabled** toggle | No |
189
- | `client.features.listStore` | Every registered feature flag, each with an `isResolved` feature flag | No |
190
- | `client.features.getById(id)` | A store that holds one feature flag | No |
191
-
192
- :::warning
193
- `useFeatureResult` and `getFeatureResult` resolve the feature flag as soon as the component
194
- renders. They commit the visitor to a bucket at that moment.
195
-
196
- Some decisions must wait for data that you do not have yet, such as a cart, a quote,
197
- or a profile of a signed-in user. For those, use `useFeature` or `getFeature`. Call
198
- the function that they return after the data arrives.
199
- :::
200
-
201
104
  ## Use a feature flag in an experience condition
202
105
 
203
106
  You can use a feature flag in an experience condition through JSON logic:
@@ -248,17 +151,32 @@ client.eventBus.watch((event) => {
248
151
  })
249
152
  ```
250
153
 
154
+ A watch on `eventBus` reports only what happens after you subscribe.
155
+
156
+ ## Read the events that already happened
157
+
158
+ `client.events` is a store of every event since the client started, in order. Each entry
159
+ holds an `id`, the time `at`, and the `payload` that went on the bus.
160
+
161
+ ```ts
162
+ const alreadyAllocated = client.events
163
+ .getState()
164
+ .filter((entry) => entry.payload.type === 'feature_decide')
165
+ ```
166
+
167
+ Use it when your code starts after Picked does, and a `feature_decide` that you needed
168
+ already happened before you subscribed.
169
+
251
170
  ## Versions and cache invalidation
252
171
 
253
172
  The SDK caches a feature flag decision in browser storage. It uses `localStorage`, or
254
173
  `sessionStorage` in a preview. The cache key derives from the feature flag ID and the version,
255
174
  so a visitor keeps the same decision across page loads.
256
175
 
257
- :::warning
258
- The SDK does not yet restore a string bucket value, such as `'variant-a'`, correctly
259
- from storage. It treats the visitor as unallocated on the next page load. A boolean
260
- bucket value is correct, and that includes every feature flag that you control from the
261
- dashboard. See PICK-1574.
176
+ :::note
177
+ The SDK keeps a bucket value in storage as text. It reads `'true'` and `'false'` back as
178
+ booleans. A variant with the name `true` or `false` therefore comes back as a boolean.
179
+ Give each variant a different name.
262
180
  :::
263
181
 
264
182
  ### How versions work
@@ -306,15 +224,10 @@ features: {
306
224
  For a feature flag that you control from the dashboard, Picked sets the version for you. The
307
225
  dashboard adds the version number to the end of the cache key that the SDK uses.
308
226
 
309
- ## Best practices
310
-
311
- - **Use `session` storage for a short test**: set `storage: 'session'` for an
312
- experiment that must not stay across browser sessions.
313
- - **Keep each `decide` function fast**: everything that waits for the feature flag waits for
314
- `decide` to finish. This includes an experience condition and a component that
315
- reads the result. Do not make a slow network call inside it.
316
- - **Allocate when the decision matters**: read a feature flag only after the data that it
317
- needs arrives. Do not read every feature flag at startup. See
318
- [Allocation and exposure](/sdk/feature-allocation).
319
- - **Return one type**: do not return a boolean from a feature flag one time and a string the
320
- next. Choose one type and keep it.
227
+ ## Related
228
+
229
+ - [How to read a feature flag](/how-to/read-a-feature-flag) — the accessors in use.
230
+ - [About feature flag allocation](/explanation/feature-flag-allocation) — when a visitor
231
+ enters a test.
232
+ - [Feature flags](/reference/dashboard/features) — the dashboard controls.
233
+
@@ -1,4 +1,5 @@
1
1
  ---
2
+ type: reference
2
3
  layout: docs
3
4
  ---
4
5
 
@@ -55,24 +56,6 @@ export const communicationMethods = createField({
55
56
  })
56
57
  ```
57
58
 
58
- ## How fields work
59
-
60
- After you register a field and an experience uses it:
61
-
62
- 1. **Registration**: you register a field on the experience while you create the
63
- step, with `.addField()`.
64
- 2. **Condition evaluation**: when the field has a `condition`, the SDK evaluates it
65
- against the current context. The result decides whether the field is visible.
66
- 3. **Rendering**: the SDK renders each visible field in the experience UI, with its
67
- label, its placeholder, and its options.
68
- 4. **User input**: the user enters or chooses a value. Validation then runs in the
69
- browser, when you set a rule.
70
- 5. **Storage**: the SDK stores a valid response in the experience state, under
71
- `responses[field.name]`.
72
- 6. **Reference**: a later condition can point at the value with
73
- `{ var: 'responses.field-name' }`. This works in a field condition, in a step
74
- condition, and in the logic of an experience.
75
-
76
59
  ## Field types
77
60
 
78
61
  A field has one of four basic types: `text`, `number`, `boolean`, or `object`.
@@ -139,58 +122,6 @@ const conditionalField = createField({
139
122
  })
140
123
  ```
141
124
 
142
- ## Field validation
143
-
144
- Check the input of a user with a JSON Logic rule. The `$input` variable holds the
145
- current value of the field:
146
-
147
- ```ts
148
- const quantityField = createField({
149
- id: 'quantity',
150
- name: 'quantity',
151
- type: 'number',
152
- label: 'Quantity',
153
- multiple: false,
154
- required: true,
155
- validation: {
156
- catch: {
157
- or: [
158
- {
159
- and: [
160
- {
161
- // The input value is above 0.
162
- '>': [
163
- {
164
- var: '$input',
165
- },
166
- 0,
167
- ],
168
- },
169
- {
170
- // The input value is 999 or below.
171
- '<=': [
172
- {
173
- var: '$input',
174
- },
175
- 999,
176
- ],
177
- },
178
- ],
179
- },
180
- {
181
- throw: 'Please enter a value between 1 and 999',
182
- },
183
- ],
184
- },
185
- },
186
- })
187
- ```
188
-
189
- :::tip
190
- The `catch` and `throw` pattern in JSON Logic checks that the condition gives `true`.
191
- When it does not, the SDK shows the `throw` message to the user.
192
- :::
193
-
194
125
  ## Field policies
195
126
 
196
127
  Use a privacy policy to control how Picked tracks and stores the data of a field:
@@ -220,6 +151,11 @@ const sensitiveField = createField({
220
151
  Use these policies for sensitive data such as a tax number, a credit card number, or
221
152
  a password. That data must stay in the browser.
222
153
 
154
+ Neither policy stops the flow from the use of the value inside the page. A field with both
155
+ policies set still holds its value there, for step validation and for the conditions after
156
+ the step. The policies control what leaves the page: your analytics, and browser
157
+ storage.
158
+
223
159
  ## Field metadata
224
160
 
225
161
  Add more text to help the user:
@@ -267,21 +203,7 @@ const sensitiveField = createField({
267
203
 
268
204
  :::
269
205
 
270
- ## Best practices
271
-
272
- - **Give each field a name that says what it holds**: use `email-address` and not
273
- `field1`. It is then easier to debug the flow and to read your analytics.
274
- - **Match the type to the data**: use `number` for a numeric value, and not `text`.
275
- The SDK can then check and compare the value correctly.
276
- - **Mark a field as required only when it is**: fewer required fields means fewer
277
- users who leave the form.
278
- - **Keep each validation message clear**: an error message must tell the user exactly
279
- how to correct the input.
280
- - **Show a field only when it is relevant**: use a condition on the field, and decide
281
- it from the earlier responses.
282
- - **Apply a privacy policy to sensitive data**: use `doNotTrackValue` and
283
- `doNotStoreValue` for personal information, a password, or financial data.
284
- - **Give helpful metadata**: add a `placeholder` and a `description` to help the
285
- user, above all for an input with a format.
286
- - **Keep an option list easy to read**: for more than 5 options, add a search or
287
- break the field into more than one step.
206
+ ## Related
207
+
208
+ - [How to validate a field](/how-to/validate-a-field)
209
+ - [Fields](/reference/dashboard/fields) — the dashboard privacy controls.
@@ -0,0 +1,68 @@
1
+ ---
2
+ type: reference
3
+ layout: docs
4
+ ---
5
+
6
+ # Picked SDK
7
+
8
+ The Picked SDK lets you add a dynamic, personalized experience to your web
9
+ application with very little code.
10
+
11
+ :::tip
12
+ The SDK is framework-agnostic. It works with React, Svelte, Vue, and plain
13
+ JavaScript.
14
+ :::
15
+
16
+ ## Precondition and condition
17
+
18
+ | Category | Question | Description |
19
+ | ------------ | ---------------------- | --------------------------------------------------------------------------------------------- |
20
+ | Precondition | Must Picked start? | Use it to check the environment, to filter by page, or for a feature flag that turns the SDK off completely. |
21
+ | Condition | Is this user eligible? | Use it to divide your users into groups, to enter a user into an A/B test, or to reach one group of users. |
22
+
23
+ ```ts
24
+ const picked = createClient({
25
+ precondition: {
26
+ and: [
27
+ {
28
+ or: [
29
+ {
30
+ invoke: {
31
+ var: 'features.aa-test',
32
+ },
33
+ },
34
+ true,
35
+ ],
36
+ },
37
+ { invoke: { var: 'predicates.shopify-is-product-page' } },
38
+ ],
39
+ },
40
+ condition: {
41
+ invoke: {
42
+ var: 'features.convincely-enabled',
43
+ },
44
+ },
45
+ // The rest of the Picked options go here.
46
+ })
47
+ ```
48
+
49
+ :::warning
50
+ A precondition or a condition that points at a feature flag reads the bucket of that
51
+ feature flag. That read allocates the visitor at that moment.
52
+
53
+ A precondition runs at startup. Picked therefore allocates every visitor to a feature
54
+ flag that a precondition points at, before anything else happens. This includes a
55
+ visitor who never reaches the experience that the feature flag controls.
56
+
57
+ See [Allocation and exposure](/explanation/feature-flag-allocation) before you point a
58
+ precondition at a flag.
59
+ :::
60
+
61
+ See [conditions](/reference/sdk/conditions) for more detail.
62
+
63
+ ## Related
64
+
65
+ - [How to install the SDK](/how-to/install-the-sdk)
66
+ - [How to register a component](/how-to/register-a-component)
67
+ - [About conditions and the logic engine](/explanation/conditions-and-the-logic-engine) —
68
+ what the difference is between a precondition and a condition.
@@ -1,4 +1,5 @@
1
1
  ---
2
+ type: reference
2
3
  layout: docs
3
4
  ---
4
5
 
@@ -6,26 +7,6 @@ layout: docs
6
7
 
7
8
  This plugin registers an array of experiences on the Picked client.
8
9
 
9
- ## Set the plugin up
10
-
11
- :::code-group
12
-
13
- ```ts [app.ts]
14
- import { createClient } from '@picked/client'
15
- import { createExperiencePlugin } from '@picked/client/plugins/create-experience'
16
- import { funnelExperience } from '../experiences/funnel-experience'
17
- import { widgetExperience } from '../experiences/widget-experience'
18
-
19
- const client = createClient({
20
- projectId: 'your-project-id',
21
- plugins: createExperiencePlugin({
22
- experiences: [funnelExperience, widgetExperience],
23
- }),
24
- })
25
- ```
26
-
27
- :::
28
-
29
10
  ## Define a step
30
11
 
31
12
  Define a step with `createStep()`. Add it directly to an experience with
@@ -101,7 +82,7 @@ const contactStep = createStep({
101
82
 
102
83
  ## Use a condition in an experience
103
84
 
104
- A [condition](../conditions) defines when an experience becomes active for the user.
85
+ A [condition](/reference/sdk/conditions) defines when an experience becomes active for the user.
105
86
 
106
87
  ```ts
107
88
  import { createExperience, createStep } from '@picked/client/plugins/create-experience'
@@ -128,8 +109,15 @@ const productPageFunnel = createExperience({
128
109
  })
129
110
  ```
130
111
 
131
- A [condition](../conditions) also controls the move from one step to the next.
112
+ A [condition](/reference/sdk/conditions) also controls the move from one step to the next.
132
113
 
133
114
  ```ts
134
115
  import { createExperience, createStep } from '@picked/client/plugins/create-experience'
135
116
  ```
117
+
118
+ ## Related
119
+
120
+ - [Plugins](/reference/sdk/plugins)
121
+ - [SDK experiences](/reference/sdk/experiences)
122
+ - [Dynamic Experiences](/reference/sdk/plugins/dynamic-experiences) — the dashboard-driven
123
+ alternative.
@@ -0,0 +1,26 @@
1
+ ---
2
+ type: reference
3
+ layout: docs
4
+ ---
5
+
6
+ # Dynamic Experiences Plugin
7
+
8
+ This plugin reads the configuration of your project from the Picked API at runtime. You can
9
+ therefore control your experiences, feature flags, steps, fields, and conditions from
10
+ the [dashboard](/reference/dashboard/experiences) alone.
11
+
12
+ ## Preview mode
13
+
14
+ After you [enter preview mode from the dashboard](/reference/dashboard/workspaces#preview), the SDK
15
+ receives a preview token and starts preview mode:
16
+
17
+ - The plugin reads fresh options from the API with that token. It bypasses the
18
+ build-time configuration and the cached options.
19
+ - A feature flag decision uses session storage. The decision resets when you close
20
+ the tab.
21
+
22
+ ## Related
23
+
24
+ - [About how Picked works](/explanation/how-picked-works) — how the plugin resolves your
25
+ options at startup.
26
+ - [About feature flag allocation](/explanation/feature-flag-allocation)
@@ -0,0 +1,29 @@
1
+ ---
2
+ type: reference
3
+ layout: docs
4
+ ---
5
+
6
+ # Plugins
7
+
8
+ A plugin adds behaviour to the Picked client. Pass one to the `plugins` array of
9
+ `createClient()`.
10
+
11
+ | Plugin | Import | What it does |
12
+ | --- | --- | --- |
13
+ | [Dynamic Experiences](/reference/sdk/plugins/dynamic-experiences) | `@picked/client/plugins/dynamic-experiences` | Reads the published configuration of your project at runtime and registers every experience, step, field, condition, and feature flag. |
14
+ | [Create Experience](/reference/sdk/plugins/create-experience) | `@picked/client/plugins/create-experience` | Registers an experience, a step, and a field that you define in code. |
15
+ | [Window Object](/reference/sdk/plugins/window-object) | `@picked/client/plugins/window-object` | Puts the client on `window.Picked`, which helps when you debug. |
16
+ | Single Page App | `@picked/client/plugins/single-page-app` | Starts the client again on a history event. |
17
+ | React framework | `@picked/react/plugins/framework` | Lets the client render a React component. |
18
+ | Svelte framework | `@picked/svelte/plugins/framework` | Lets the client render a Svelte component. |
19
+ | Devtools | `@picked/svelte/plugins/devtools` | Connects the client to the Chrome extension. |
20
+
21
+ Most applications need a framework plugin and **one** of Dynamic Experiences or Create
22
+ Experience. Dynamic Experiences puts your options in the dashboard; Create Experience
23
+ puts them in code.
24
+
25
+ ## Related
26
+
27
+ - [How to install the SDK](/how-to/install-the-sdk) — where the plugins go.
28
+ - [SDK installation](/reference/sdk) — every client option.
29
+ - [How to debug with the Chrome extension](/how-to/debug-with-the-chrome-extension)
@@ -1,4 +1,5 @@
1
1
  ---
2
+ type: reference
2
3
  layout: docs
3
4
  ---
4
5
 
@@ -17,3 +18,8 @@ const client = createClient({
17
18
  plugins: [windowObjectPlugin()],
18
19
  })
19
20
  ```
21
+
22
+ ## Related
23
+
24
+ - [Plugins](/reference/sdk/plugins)
25
+ - [How to install the SDK](/how-to/install-the-sdk)