@picked/client 0.0.71-next.1 → 0.0.71-next.3
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/dist/.tsbuildinfo +1 -1
- package/dist/{api-CH2FNa6S.cjs → api-CnjBi6JG.cjs} +1 -1
- package/dist/{api-CgqRvuKL.js → api-vBVGLtvU.js} +1 -1
- package/dist/cjs/index.js +2 -2
- package/dist/cjs/plugins/dynamic-experiences/index.js +1 -1
- package/dist/cjs/server/index.js +1 -1
- package/dist/docs/explanation/conditions-and-the-logic-engine.md +94 -0
- package/dist/docs/explanation/feature-flag-allocation.md +176 -0
- package/dist/docs/explanation/how-picked-works.md +102 -0
- package/dist/docs/explanation/how-pickedql-works.md +76 -0
- package/dist/docs/explanation/pages-and-flows.md +64 -0
- package/dist/docs/explanation/the-content-model.md +77 -0
- package/dist/docs/explanation/the-two-apis.md +68 -0
- package/dist/docs/explanation/why-server-side-rendering.md +76 -0
- package/dist/docs/explanation/workspaces-and-versions.md +71 -0
- package/dist/docs/how-to/add-a-step-field-or-action.md +43 -0
- package/dist/docs/{pickedql/examples.md → how-to/analytics/index.md} +19 -6
- package/dist/docs/how-to/archive-or-delete-a-workspace.md +46 -0
- package/dist/docs/how-to/authenticate-with-a-magic-link.md +40 -0
- package/dist/docs/how-to/authenticate-with-a-password.md +51 -0
- package/dist/docs/how-to/authenticate-with-an-api-key.md +51 -0
- package/dist/docs/how-to/authenticate-with-oauth.md +40 -0
- package/dist/docs/{flow-builder/building-flows.md → how-to/build-a-flow.md} +13 -9
- package/dist/docs/how-to/connect-the-mcp-server.md +38 -0
- package/dist/docs/how-to/create-a-project.md +29 -0
- package/dist/docs/how-to/create-an-experience.md +42 -0
- package/dist/docs/how-to/create-and-revoke-an-api-key.md +37 -0
- package/dist/docs/how-to/debug-with-the-chrome-extension.md +57 -0
- package/dist/docs/how-to/define-a-page-layout.md +39 -0
- package/dist/docs/how-to/fill-and-publish-a-page.md +50 -0
- package/dist/docs/{gtm-template.md → how-to/install-the-gtm-template.md} +9 -3
- package/dist/docs/how-to/install-the-sdk.md +68 -0
- package/dist/docs/how-to/install-the-vite-plugin.md +56 -0
- package/dist/docs/how-to/invite-and-remove-members.md +34 -0
- package/dist/docs/how-to/preview-a-workspace.md +28 -0
- package/dist/docs/how-to/publish-a-workspace.md +30 -0
- package/dist/docs/how-to/read-a-feature-flag.md +101 -0
- package/dist/docs/how-to/register-a-component.md +55 -0
- package/dist/docs/how-to/register-an-action-handler.md +67 -0
- package/dist/docs/how-to/render-a-step-form.md +132 -0
- package/dist/docs/how-to/render-an-unpublished-preview.md +41 -0
- package/dist/docs/how-to/reuse-a-section.md +37 -0
- package/dist/docs/how-to/reuse-a-step-field-or-action.md +28 -0
- package/dist/docs/how-to/roll-back-a-published-version.md +26 -0
- package/dist/docs/how-to/run-an-ab-test.md +49 -0
- package/dist/docs/how-to/run-code-on-a-form-submission.md +125 -0
- package/dist/docs/how-to/server-render-a-page.md +81 -0
- package/dist/docs/how-to/set-a-content-security-policy.md +32 -0
- package/dist/docs/how-to/set-up-mfa.md +69 -0
- package/dist/docs/how-to/upload-and-use-an-asset.mdx +73 -0
- package/dist/docs/how-to/validate-a-field.md +52 -0
- package/dist/docs/how-to/write-a-condition.md +45 -0
- package/dist/docs/how-to/write-a-predicate.md +60 -0
- package/dist/docs/index.md +41 -70
- package/dist/docs/reference/dashboard/actions.mdx +53 -0
- package/dist/docs/{api-keys.mdx → reference/dashboard/api-keys.mdx} +7 -15
- package/dist/docs/reference/dashboard/conditions.mdx +42 -0
- package/dist/docs/reference/dashboard/content/asset-library.mdx +63 -0
- package/dist/docs/{content → reference/dashboard/content}/content-entries.mdx +11 -3
- package/dist/docs/{content → reference/dashboard/content}/layouts.mdx +11 -35
- package/dist/docs/reference/dashboard/content/pages.mdx +35 -0
- package/dist/docs/reference/dashboard/content/sections.mdx +28 -0
- package/dist/docs/{experiences.mdx → reference/dashboard/experiences.mdx} +9 -16
- package/dist/docs/{features.mdx → reference/dashboard/features.mdx} +26 -41
- package/dist/docs/{fields.mdx → reference/dashboard/fields.mdx} +26 -20
- package/dist/docs/{flow-builder/overview.mdx → reference/dashboard/flow-builder/canvas.mdx} +8 -13
- package/dist/docs/{flow-builder → reference/dashboard/flow-builder}/condition-builder.md +5 -13
- package/dist/docs/{flow-builder → reference/dashboard/flow-builder}/keyboard-shortcuts.md +7 -1
- package/dist/docs/{flow-builder → reference/dashboard/flow-builder}/nodes.md +10 -3
- package/dist/docs/reference/dashboard/projects.mdx +44 -0
- package/dist/docs/reference/dashboard/reports.mdx +43 -0
- package/dist/docs/reference/dashboard/steps.mdx +37 -0
- package/dist/docs/{team-management.md → reference/dashboard/team-management.md} +5 -22
- package/dist/docs/{workspace-versioning.mdx → reference/dashboard/workspace-versioning.mdx} +6 -10
- package/dist/docs/reference/dashboard/workspaces.mdx +76 -0
- package/dist/docs/{delivery-api → reference/delivery-api}/experience-content.md +11 -4
- package/dist/docs/reference/delivery-api/index.md +45 -0
- package/dist/docs/{delivery-api → reference/delivery-api}/preview-caching-crawling.md +12 -5
- package/dist/docs/{delivery-api → reference/delivery-api}/project-config.md +11 -4
- package/dist/docs/reference/glossary.md +173 -0
- package/dist/docs/reference/management-api/authentication.md +74 -0
- package/dist/docs/{api → reference/management-api}/authorization-and-scopes.md +10 -3
- package/dist/docs/{api → reference/management-api}/error-handling.md +9 -3
- package/dist/docs/{api → reference/management-api}/pagination.md +6 -0
- package/dist/docs/{pickedql → reference/pickedql}/data-sources.md +16 -9
- package/dist/docs/{pickedql → reference/pickedql}/dimensions.md +7 -0
- package/dist/docs/{pickedql → reference/pickedql}/filtering.md +7 -0
- package/dist/docs/reference/pickedql/index.md +18 -0
- package/dist/docs/{pickedql → reference/pickedql}/schema.md +13 -6
- package/dist/docs/{pickedql → reference/pickedql}/virtual-functions.md +7 -0
- package/dist/docs/reference/sdk/actions.md +30 -0
- package/dist/docs/{sdk → reference/sdk}/conditions.md +8 -1
- package/dist/docs/reference/sdk/content-security-policy.md +21 -0
- package/dist/docs/reference/sdk/experiences.md +51 -0
- package/dist/docs/{sdk → reference/sdk}/features.md +30 -117
- package/dist/docs/{sdk → reference/sdk}/fields.md +10 -88
- package/dist/docs/reference/sdk/index.md +68 -0
- package/dist/docs/{sdk → reference/sdk}/plugins/create-experience.md +10 -22
- package/dist/docs/reference/sdk/plugins/dynamic-experiences.md +26 -0
- package/dist/docs/reference/sdk/plugins/index.md +29 -0
- package/dist/docs/{sdk → reference/sdk}/plugins/window-object.md +6 -0
- package/dist/docs/reference/sdk/predicates.md +43 -0
- package/dist/docs/reference/sdk/ssr/index.md +33 -0
- package/dist/docs/{sdk → reference/sdk}/ssr/rendering-and-hydration.md +4 -63
- package/dist/docs/{sdk → reference/sdk}/ssr/serialize.md +6 -33
- package/dist/docs/{sdk → reference/sdk}/ssr/slots-and-placements.md +7 -1
- package/dist/docs/reference/sdk/steps.md +87 -0
- package/dist/docs/reference/tools/chrome-extension.md +16 -0
- package/dist/docs/{image-optimisation-service.md → reference/tools/image-optimisation-service.md} +8 -0
- package/dist/docs/{mcp.md → reference/tools/mcp.md} +5 -45
- package/dist/docs/{vite-plugin.md → reference/tools/vite-plugin.md} +8 -38
- package/dist/docs/{api/index.md → tutorials/first-api-call.md} +27 -15
- package/dist/docs/tutorials/first-experience-in-the-dashboard.mdx +143 -0
- package/dist/docs/tutorials/first-experience.mdx +255 -0
- package/dist/docs/tutorials/first-server-rendered-page.mdx +186 -0
- package/dist/esm/index.js +468 -426
- package/dist/esm/plugins/dynamic-experiences/index.js +2 -2
- package/dist/esm/server/index.js +1 -1
- package/dist/{plugin-DCW-tnjO.cjs → plugin-CSmD1gPl.cjs} +1 -1
- package/dist/{plugin-D2UqFikQ.js → plugin-GOTtoDlC.js} +1 -1
- package/dist/types/entities/feature/feature.d.ts +25 -2
- package/dist/types/entities/feature/feature.d.ts.map +1 -1
- package/dist/types/picked.d.ts +2 -1
- package/dist/types/picked.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/docs/actions.mdx +0 -98
- package/dist/docs/api/authentication.md +0 -403
- package/dist/docs/chrome-extension.md +0 -68
- package/dist/docs/conditions.mdx +0 -72
- package/dist/docs/content/asset-library.mdx +0 -124
- package/dist/docs/content/pages.mdx +0 -82
- package/dist/docs/content/sections.mdx +0 -44
- package/dist/docs/delivery-api/index.md +0 -53
- package/dist/docs/guides/build-a-personalized-page.mdx +0 -161
- package/dist/docs/pickedql/index.md +0 -54
- package/dist/docs/projects.mdx +0 -44
- package/dist/docs/reports.mdx +0 -20
- package/dist/docs/sdk/actions.md +0 -82
- package/dist/docs/sdk/content-security-policy.md +0 -33
- package/dist/docs/sdk/experiences.md +0 -64
- package/dist/docs/sdk/feature-allocation.md +0 -161
- package/dist/docs/sdk/index.md +0 -98
- package/dist/docs/sdk/plugins/dynamic-experiences.md +0 -84
- package/dist/docs/sdk/plugins/index.md +0 -9
- package/dist/docs/sdk/predicates.md +0 -128
- package/dist/docs/sdk/ssr/index.md +0 -79
- package/dist/docs/sdk/steps.md +0 -479
- package/dist/docs/steps.mdx +0 -53
- 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](
|
|
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](/
|
|
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
|
-
:::
|
|
258
|
-
The SDK
|
|
259
|
-
|
|
260
|
-
|
|
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
|
-
##
|
|
310
|
-
|
|
311
|
-
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
##
|
|
271
|
-
|
|
272
|
-
-
|
|
273
|
-
|
|
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](
|
|
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](
|
|
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)
|