@picked/client 0.0.70-next.6 → 0.0.71-next.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/dist/.tsbuildinfo +1 -0
- package/dist/api-CH2FNa6S.cjs +1 -0
- package/dist/api-CgqRvuKL.js +38 -0
- package/dist/cjs/form/index.js +1 -1
- package/dist/cjs/index.js +2 -2
- package/dist/cjs/plugins/dom-observer/index.js +1 -1
- package/dist/cjs/plugins/dynamic-experiences/index.js +1 -1
- package/dist/cjs/server/index.js +1 -1
- package/dist/docs/actions.mdx +38 -18
- package/dist/docs/api/authentication.md +75 -48
- package/dist/docs/api/authorization-and-scopes.md +63 -47
- package/dist/docs/api/error-handling.md +70 -54
- package/dist/docs/api/index.md +41 -22
- package/dist/docs/api/pagination.md +30 -20
- package/dist/docs/api-keys.mdx +11 -6
- package/dist/docs/chrome-extension.md +22 -5
- package/dist/docs/conditions.mdx +32 -19
- package/dist/docs/content/asset-library.mdx +65 -34
- package/dist/docs/content/content-entries.mdx +8 -2
- package/dist/docs/content/layouts.mdx +45 -29
- package/dist/docs/content/pages.mdx +29 -14
- package/dist/docs/content/sections.mdx +19 -8
- package/dist/docs/delivery-api/experience-content.md +10 -5
- package/dist/docs/delivery-api/index.md +19 -12
- package/dist/docs/delivery-api/preview-caching-crawling.md +27 -14
- package/dist/docs/delivery-api/project-config.md +6 -4
- package/dist/docs/experiences.mdx +26 -10
- package/dist/docs/features.mdx +66 -25
- package/dist/docs/fields.mdx +42 -27
- package/dist/docs/flow-builder/building-flows.md +38 -20
- package/dist/docs/flow-builder/condition-builder.md +32 -21
- package/dist/docs/flow-builder/keyboard-shortcuts.md +18 -18
- package/dist/docs/flow-builder/nodes.md +26 -13
- package/dist/docs/flow-builder/overview.mdx +28 -15
- package/dist/docs/gtm-template.md +35 -26
- package/dist/docs/guides/build-a-personalized-page.mdx +36 -18
- package/dist/docs/image-optimisation-service.md +33 -17
- package/dist/docs/index.md +28 -17
- package/dist/docs/mcp.md +46 -38
- package/dist/docs/pickedql/data-sources.md +65 -43
- package/dist/docs/pickedql/dimensions.md +27 -12
- package/dist/docs/pickedql/examples.md +38 -26
- package/dist/docs/pickedql/filtering.md +10 -6
- package/dist/docs/pickedql/index.md +32 -14
- package/dist/docs/pickedql/schema.md +85 -76
- package/dist/docs/pickedql/virtual-functions.md +62 -36
- package/dist/docs/projects.mdx +14 -7
- package/dist/docs/reports.mdx +5 -3
- package/dist/docs/sdk/actions.md +33 -17
- package/dist/docs/sdk/conditions.md +8 -5
- package/dist/docs/sdk/content-security-policy.md +5 -4
- package/dist/docs/sdk/experiences.md +29 -11
- package/dist/docs/sdk/feature-allocation.md +79 -47
- package/dist/docs/sdk/features.md +130 -76
- package/dist/docs/sdk/fields.md +66 -42
- package/dist/docs/sdk/index.md +22 -13
- package/dist/docs/sdk/plugins/create-experience.md +18 -15
- package/dist/docs/sdk/plugins/dynamic-experiences.md +42 -16
- package/dist/docs/sdk/predicates.md +53 -37
- package/dist/docs/sdk/ssr/index.md +25 -8
- package/dist/docs/sdk/ssr/rendering-and-hydration.md +37 -16
- package/dist/docs/sdk/ssr/serialize.md +38 -20
- package/dist/docs/sdk/ssr/slots-and-placements.md +28 -10
- package/dist/docs/sdk/steps.md +100 -55
- package/dist/docs/steps.mdx +20 -11
- package/dist/docs/team-management.md +17 -11
- package/dist/docs/vite-plugin.md +26 -19
- package/dist/docs/workspace-versioning.mdx +14 -8
- package/dist/docs/workspaces.mdx +55 -24
- package/dist/{dom-BwmTHeT7.js → dom-BqcToNL0.js} +1 -3
- package/dist/{dom-CDrHc_j9.cjs → dom-DlLH3FxV.cjs} +1 -1
- package/dist/esm/form/index.js +2 -4
- package/dist/esm/index.js +50 -45
- package/dist/esm/plugins/dom-observer/index.js +1 -1
- package/dist/esm/plugins/dynamic-experiences/index.js +2 -2
- package/dist/esm/preview/index.js +1 -1
- package/dist/esm/server/index.js +12 -10
- package/dist/{experience-instance-DOJShRk5.js → experience-instance-DGk11d8a.js} +3 -3
- package/dist/experience-instance-Dej3P1sE.cjs +1 -0
- package/dist/{plugin-BxMR7fOV.js → plugin-D2UqFikQ.js} +2 -2
- package/dist/plugin-DCW-tnjO.cjs +1 -0
- package/dist/{preview-channel-DW9J8t4B.js → preview-channel-CSr96XAo.js} +1 -1
- package/dist/{protocol-DSqOlW2r.js → protocol-C5KkkiZc.js} +1 -1
- package/dist/types/client.d.ts +7 -6
- package/dist/types/entities/action/action.d.ts +5 -4
- package/dist/types/entities/action/action.d.ts.map +1 -1
- package/dist/types/entities/component/component.d.ts +6 -5
- package/dist/types/entities/component/component.d.ts.map +1 -1
- package/dist/types/entities/condition/condition.d.ts +5 -5
- package/dist/types/entities/content/content.d.ts +4 -3
- package/dist/types/entities/experience/experience.d.ts +11 -10
- package/dist/types/entities/experience/experience.d.ts.map +1 -1
- package/dist/types/entities/experience-instance/experience-instance.d.ts +10 -9
- package/dist/types/entities/feature/feature.d.ts +3 -2
- package/dist/types/entities/feature/feature.d.ts.map +1 -1
- package/dist/types/entities/field/field.d.ts +7 -6
- package/dist/types/entities/framework/framework.d.ts +7 -6
- package/dist/types/entities/framework/framework.d.ts.map +1 -1
- package/dist/types/entities/history/history.d.ts +6 -6
- package/dist/types/entities/plugin/plugin.d.ts +3 -2
- package/dist/types/entities/plugin/plugin.d.ts.map +1 -1
- package/dist/types/entities/predicate/predicate.d.ts +4 -3
- package/dist/types/entities/predicate/predicate.d.ts.map +1 -1
- package/dist/types/entities/response/response-validation.d.ts +3 -2
- package/dist/types/entities/response/response.d.ts +12 -11
- package/dist/types/entities/session/session.d.ts +2 -1
- package/dist/types/entities/step/step.d.ts +6 -5
- package/dist/types/exports.d.ts +2 -1
- package/dist/types/exports.d.ts.map +1 -1
- package/dist/types/form/build-responses.d.ts +2 -1
- package/dist/types/form/create-field-evaluator.d.ts +4 -3
- package/dist/types/form/create-field-validation.d.ts +5 -4
- package/dist/types/form/create-form.d.ts +7 -6
- package/dist/types/form/create-submit-pipeline.d.ts +7 -6
- package/dist/types/form/get-step-fields.d.ts +3 -2
- package/dist/types/lifecycle/condition-selection.d.ts +14 -4
- package/dist/types/lifecycle/lifecycle.d.ts +4 -3
- package/dist/types/lifecycle/rules-logic.d.ts +4 -3
- package/dist/types/lifecycle/state-snapshot.d.ts +4 -3
- package/dist/types/picked.d.ts +64 -62
- package/dist/types/picked.d.ts.map +1 -1
- package/dist/types/ping.d.ts +1 -1
- package/dist/types/ping.d.ts.map +1 -1
- package/dist/types/plugins/create-experience/create-experience.d.ts +9 -8
- package/dist/types/plugins/create-experience/create-experience.d.ts.map +1 -1
- package/dist/types/plugins/create-experience/plugin.d.ts +3 -2
- package/dist/types/plugins/create-experience/plugin.d.ts.map +1 -1
- package/dist/types/plugins/create-experience/serialization.d.ts.map +1 -1
- package/dist/types/plugins/dom-observer/plugin.d.ts +1 -1
- package/dist/types/plugins/dynamic-experiences/plugin.d.ts +3 -3
- package/dist/types/plugins/dynamic-experiences/types.d.ts +8 -5
- package/dist/types/plugins/experience-browser-storage/plugin.d.ts +1 -1
- package/dist/types/plugins/experience-browser-storage/plugin.d.ts.map +1 -1
- package/dist/types/plugins/history-browser-storage/plugin.d.ts +1 -1
- package/dist/types/plugins/history-browser-storage/plugin.d.ts.map +1 -1
- package/dist/types/plugins/responses-browser-storage/plugin.d.ts +1 -1
- package/dist/types/plugins/responses-browser-storage/plugin.d.ts.map +1 -1
- package/dist/types/plugins/responses-indexeddb/plugin.d.ts +1 -1
- package/dist/types/plugins/single-page-app/plugin.d.ts +1 -1
- package/dist/types/plugins/single-page-app/plugin.d.ts.map +1 -1
- package/dist/types/plugins/web-components/plugin.d.ts +2 -1
- package/dist/types/plugins/window-object/plugin.d.ts +2 -2
- package/dist/types/preview-channel.d.ts +5 -1
- package/dist/types/preview-receiver/index.d.ts +18 -1
- package/dist/types/preview-receiver/protocol.d.ts +10 -1
- package/dist/types/server/index.d.ts +3 -3
- package/dist/types/server/serialize.d.ts +2 -2
- package/dist/types/utils/dom.d.ts +2 -1
- package/dist/types/utils/events.d.ts +6 -6
- package/dist/types/utils/events.d.ts.map +1 -1
- package/dist/types/utils/global-obj.d.ts +1 -1
- package/dist/types/utils/global-obj.d.ts.map +1 -1
- package/dist/types/utils/id.d.ts.map +1 -1
- package/dist/types/utils/logger.d.ts +2 -1
- package/dist/types/utils/logger.d.ts.map +1 -1
- package/dist/types/utils/node-tree/get-next-node-from-action.d.ts +2 -1
- package/dist/types/utils/node-tree/get-next-node-from-condition.d.ts +2 -1
- package/dist/types/utils/node-tree/get-next-node.d.ts +1 -1
- package/dist/types/utils/node-tree/get-next-node.d.ts.map +1 -1
- package/dist/types/utils/node-tree/get-origin-node.d.ts +1 -1
- package/dist/types/utils/node-tree/get-origin-node.d.ts.map +1 -1
- package/dist/types/utils/node-tree/min-max-steps-to-leaf.d.ts +1 -1
- package/dist/types/utils/node-tree/min-max-steps-to-leaf.d.ts.map +1 -1
- package/dist/types/utils/node-tree/min-max-steps-to-origin.d.ts +1 -1
- package/dist/types/utils/node-tree/min-max-steps-to-origin.d.ts.map +1 -1
- package/dist/types/utils/node-tree/next-reachable-step-nodes.d.ts +1 -1
- package/dist/types/utils/node-tree/next-reachable-step-nodes.d.ts.map +1 -1
- package/dist/types/utils/nodes.d.ts +1 -1
- package/dist/types/utils/picked-config.d.ts +2 -1
- package/dist/types/utils/rules-logic.d.ts +6 -2
- package/dist/types/utils/storage.d.ts +1 -1
- package/dist/types/utils/storage.d.ts.map +1 -1
- package/package.json +22 -23
- package/dist/api-CoUkIxIr.cjs +0 -1
- package/dist/api-ahdSBQ3Y.js +0 -40
- package/dist/experience-instance-DurC3pE-.cjs +0 -1
- package/dist/plugin-CASdGtTX.cjs +0 -1
package/dist/docs/sdk/index.md
CHANGED
|
@@ -4,13 +4,15 @@ layout: docs
|
|
|
4
4
|
|
|
5
5
|
# Picked SDK
|
|
6
6
|
|
|
7
|
-
The Picked SDK
|
|
7
|
+
The Picked SDK lets you add a dynamic, personalized experience to your web
|
|
8
|
+
application with very little code.
|
|
8
9
|
|
|
9
10
|
:::tip
|
|
10
|
-
The SDK is framework-agnostic
|
|
11
|
+
The SDK is framework-agnostic. It works with React, Svelte, Vue, and plain
|
|
12
|
+
JavaScript.
|
|
11
13
|
:::
|
|
12
14
|
|
|
13
|
-
## Quick
|
|
15
|
+
## Quick start
|
|
14
16
|
|
|
15
17
|
:::code-group
|
|
16
18
|
|
|
@@ -41,18 +43,20 @@ const snapshot = await serialize(client, {
|
|
|
41
43
|
|
|
42
44
|
:::
|
|
43
45
|
|
|
44
|
-
The `@picked/client/server` entry holds the
|
|
46
|
+
The `@picked/client/server` entry holds the helpers that run only on a server, such
|
|
47
|
+
as `serialize()`. Those helpers make an SSR snapshot. It is a separate entry point,
|
|
48
|
+
so a browser bundle never holds it. See [Server-Side Rendering](/sdk/ssr).
|
|
45
49
|
|
|
46
50
|
:::tip
|
|
47
|
-
|
|
51
|
+
In a browser, you can reach the Picked client from the console with `window.Picked`.
|
|
48
52
|
:::
|
|
49
53
|
|
|
50
|
-
## Precondition
|
|
54
|
+
## Precondition and condition
|
|
51
55
|
|
|
52
56
|
| Category | Question | Description |
|
|
53
57
|
| ------------ | ---------------------- | --------------------------------------------------------------------------------------------- |
|
|
54
|
-
| Precondition |
|
|
55
|
-
| Condition | Is this user eligible? | Use
|
|
58
|
+
| 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. |
|
|
59
|
+
| 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. |
|
|
56
60
|
|
|
57
61
|
```ts
|
|
58
62
|
const picked = createClient({
|
|
@@ -76,14 +80,19 @@ const picked = createClient({
|
|
|
76
80
|
var: 'features.convincely-enabled',
|
|
77
81
|
},
|
|
78
82
|
},
|
|
79
|
-
// rest of
|
|
83
|
+
// The rest of the Picked options go here.
|
|
80
84
|
})
|
|
81
85
|
```
|
|
82
86
|
|
|
83
87
|
:::warning
|
|
84
|
-
A precondition or condition that
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
A precondition or a condition that points at a flag reads the bucket of that flag,
|
|
89
|
+
and that allocates the visitor at that moment. A precondition runs at startup.
|
|
90
|
+
Picked therefore allocates every visitor to a flag that a precondition points at,
|
|
91
|
+
before anything else happens. This includes a visitor who never reaches the
|
|
92
|
+
experience that the flag controls.
|
|
93
|
+
|
|
94
|
+
See [Allocation and exposure](/sdk/feature-allocation) before you point a
|
|
95
|
+
precondition at a flag.
|
|
87
96
|
:::
|
|
88
97
|
|
|
89
|
-
See [conditions](./conditions.md) for more detail
|
|
98
|
+
See [conditions](./conditions.md) for more detail.
|
|
@@ -4,9 +4,9 @@ layout: docs
|
|
|
4
4
|
|
|
5
5
|
# Create Experience Plugin
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
This plugin registers an array of experiences on the Picked client.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Set the plugin up
|
|
10
10
|
|
|
11
11
|
:::code-group
|
|
12
12
|
|
|
@@ -26,14 +26,15 @@ const client = createClient({
|
|
|
26
26
|
|
|
27
27
|
:::
|
|
28
28
|
|
|
29
|
-
##
|
|
29
|
+
## Define a step
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
Define a step with `createStep()`. Add it directly to an experience with
|
|
32
|
+
`experience.addStep()`.
|
|
32
33
|
|
|
33
34
|
```ts
|
|
34
35
|
import { createExperience, createStep } from '@picked/client/plugins/create-experience'
|
|
35
36
|
|
|
36
|
-
// Define
|
|
37
|
+
// Define the steps on their own.
|
|
37
38
|
const welcomeStep = createStep({
|
|
38
39
|
id: 'welcome',
|
|
39
40
|
name: 'Welcome',
|
|
@@ -44,7 +45,7 @@ const contactStep = createStep({
|
|
|
44
45
|
name: 'Contact Details',
|
|
45
46
|
})
|
|
46
47
|
|
|
47
|
-
// Define the experience
|
|
48
|
+
// Define the experience, then add the steps to build the flow.
|
|
48
49
|
const onboardingExperience = createExperience({
|
|
49
50
|
id: 'onboarding',
|
|
50
51
|
name: 'User Onboarding',
|
|
@@ -55,13 +56,14 @@ onboardingExperience.addStep(welcomeStep)
|
|
|
55
56
|
onboardingExperience.addStep(contactStep)
|
|
56
57
|
```
|
|
57
58
|
|
|
58
|
-
Each step
|
|
59
|
+
Each step needs a `name`, and your analytics events use that name to identify the
|
|
60
|
+
step. An `id` is optional. Picked makes one for you when you give none.
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
You can use the same step on more than one experience.
|
|
61
63
|
|
|
62
|
-
###
|
|
64
|
+
### Add a field to a step
|
|
63
65
|
|
|
64
|
-
|
|
66
|
+
Define a field with `createField()`.
|
|
65
67
|
|
|
66
68
|
```ts
|
|
67
69
|
import { createStep, createField } from '@picked/client/plugins/create-experience'
|
|
@@ -77,7 +79,8 @@ const emailField = createField({
|
|
|
77
79
|
})
|
|
78
80
|
```
|
|
79
81
|
|
|
80
|
-
|
|
82
|
+
Add a field to a step with `step.addField()`. You can also pass an array of
|
|
83
|
+
`fieldIds` when you create the step:
|
|
81
84
|
|
|
82
85
|
```ts
|
|
83
86
|
const contactStep = createStep({
|
|
@@ -87,7 +90,7 @@ const contactStep = createStep({
|
|
|
87
90
|
|
|
88
91
|
contactStep.addField(emailField)
|
|
89
92
|
|
|
90
|
-
//Or
|
|
93
|
+
// Or:
|
|
91
94
|
|
|
92
95
|
const contactStep = createStep({
|
|
93
96
|
id: 'contact',
|
|
@@ -96,9 +99,9 @@ const contactStep = createStep({
|
|
|
96
99
|
})
|
|
97
100
|
```
|
|
98
101
|
|
|
99
|
-
##
|
|
102
|
+
## Use a condition in an experience
|
|
100
103
|
|
|
101
|
-
[
|
|
104
|
+
A [condition](../conditions) defines when an experience becomes active for the user.
|
|
102
105
|
|
|
103
106
|
```ts
|
|
104
107
|
import { createExperience, createStep } from '@picked/client/plugins/create-experience'
|
|
@@ -125,7 +128,7 @@ const productPageFunnel = createExperience({
|
|
|
125
128
|
})
|
|
126
129
|
```
|
|
127
130
|
|
|
128
|
-
|
|
131
|
+
A [condition](../conditions) also controls the move from one step to the next.
|
|
129
132
|
|
|
130
133
|
```ts
|
|
131
134
|
import { createExperience, createStep } from '@picked/client/plugins/create-experience'
|
|
@@ -4,9 +4,11 @@ layout: docs
|
|
|
4
4
|
|
|
5
5
|
# Dynamic Experiences Plugin
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
This plugin reads the options of your project from the Picked API at runtime. You can
|
|
8
|
+
therefore control your experiences, feature flags, steps, fields, and conditions from
|
|
9
|
+
the [dashboard](/experiences) alone.
|
|
8
10
|
|
|
9
|
-
##
|
|
11
|
+
## Set the plugin up
|
|
10
12
|
|
|
11
13
|
:::code-group
|
|
12
14
|
|
|
@@ -25,34 +27,58 @@ client.start()
|
|
|
25
27
|
:::
|
|
26
28
|
|
|
27
29
|
:::tip
|
|
28
|
-
|
|
30
|
+
With [`createExperiencePlugin`](./create-experience.md) you define an experience, a
|
|
31
|
+
step, and a field in code. With this plugin you do not. You control every option from
|
|
32
|
+
the [Picked dashboard](/experiences).
|
|
29
33
|
:::
|
|
30
34
|
|
|
31
|
-
## How
|
|
35
|
+
## How it works
|
|
32
36
|
|
|
33
|
-
When the client starts, the plugin resolves
|
|
37
|
+
When the client starts, the plugin resolves the options of your project. It tries
|
|
38
|
+
three sources, in this order:
|
|
34
39
|
|
|
35
|
-
1. **Preview mode** —
|
|
40
|
+
1. **Preview mode** — after you [enter preview mode from the dashboard](/workspaces#preview),
|
|
41
|
+
the plugin reads fresh options from the API. A feature flag decision then uses
|
|
42
|
+
session storage, so it does not stay after you close the tab.
|
|
36
43
|
|
|
37
|
-
2. **Build-time
|
|
44
|
+
2. **Build-time options** — the plugin uses the options that it has already, and
|
|
45
|
+
makes no network request. This applies in two cases. You pass `projectConfig` as a
|
|
46
|
+
plugin option, or the [Vite Plugin](/vite-plugin) injects the options at build
|
|
47
|
+
time.
|
|
38
48
|
|
|
39
|
-
3. **Runtime
|
|
49
|
+
3. **Runtime read** — in every other case, the plugin reads the options from the API
|
|
50
|
+
when the client starts.
|
|
40
51
|
|
|
41
|
-
|
|
52
|
+
After the options resolve, the plugin registers every feature flag, condition, field,
|
|
53
|
+
step, and experience on the client. Each experience then starts, and its
|
|
54
|
+
[conditions](/sdk/conditions) and [target selectors](/sdk/experiences) decide
|
|
55
|
+
whether it does.
|
|
42
56
|
|
|
43
57
|
:::note
|
|
44
|
-
The [Vite Plugin](/vite-plugin)
|
|
58
|
+
The [Vite Plugin](/vite-plugin) embeds the options of your project at build time. In
|
|
59
|
+
production there is therefore no API request at runtime. This is the recommended
|
|
60
|
+
setup for an application that uses Vite.
|
|
45
61
|
:::
|
|
46
62
|
|
|
47
63
|
## Feature flag allocation
|
|
48
64
|
|
|
49
|
-
Each [feature flag](/sdk/features) in your project has an `enabled` state and a
|
|
65
|
+
Each [feature flag](/sdk/features) in your project has an `enabled` state and a
|
|
66
|
+
`weight` from 0 to 100. The weight controls what percentage of your traffic Picked
|
|
67
|
+
allocates to the flag.
|
|
50
68
|
|
|
51
|
-
|
|
69
|
+
The plugin makes a deterministic hash from the session ID of the user, and that gives
|
|
70
|
+
a stable allocation value. Picked allocates a user whose value is below the weight of
|
|
71
|
+
the flag. The same user always gets the same result.
|
|
52
72
|
|
|
53
|
-
|
|
73
|
+
The plugin caches a feature flag decision in `localStorage` by default. In
|
|
74
|
+
[preview mode](#preview-mode) it uses `sessionStorage`.
|
|
54
75
|
|
|
55
|
-
|
|
76
|
+
## Preview mode
|
|
56
77
|
|
|
57
|
-
|
|
58
|
-
|
|
78
|
+
After you [enter preview mode from the dashboard](/workspaces#preview), the SDK
|
|
79
|
+
receives a preview token and starts preview mode:
|
|
80
|
+
|
|
81
|
+
- The plugin reads fresh options from the API with that token. It bypasses the
|
|
82
|
+
build-time options and the cached options.
|
|
83
|
+
- A feature flag decision uses session storage. The decision resets when you close
|
|
84
|
+
the tab.
|
|
@@ -4,11 +4,15 @@ layout: docs
|
|
|
4
4
|
|
|
5
5
|
# Predicates
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
A predicate holds reusable condition logic that reads the current state of your
|
|
8
|
+
application. Each predicate is a pure function. It takes a state snapshot, and that
|
|
9
|
+
snapshot holds the responses, the fields, the browser context, and optional
|
|
10
|
+
arguments.
|
|
8
11
|
|
|
9
|
-
##
|
|
12
|
+
## Define a predicate
|
|
10
13
|
|
|
11
|
-
Define predicates when
|
|
14
|
+
Define your predicates when you create the Picked client. Pass a `predicates` object.
|
|
15
|
+
Each predicate needs a unique identifier and a function that takes a state snapshot:
|
|
12
16
|
|
|
13
17
|
```ts
|
|
14
18
|
import { createClient } from '@picked/client'
|
|
@@ -21,24 +25,28 @@ const client = createClient({
|
|
|
21
25
|
},
|
|
22
26
|
'get-account-tier': async () => {
|
|
23
27
|
const data = await fetch('/api/get-account-tier').then((r) => r.json())
|
|
24
|
-
return data.result //
|
|
28
|
+
return data.result // Returns "trial".
|
|
25
29
|
},
|
|
26
30
|
},
|
|
27
31
|
})
|
|
28
32
|
```
|
|
29
33
|
|
|
30
|
-
## How
|
|
34
|
+
## How predicates work
|
|
31
35
|
|
|
32
|
-
|
|
36
|
+
After you register a predicate and a condition uses it, the SDK follows this flow:
|
|
33
37
|
|
|
34
|
-
1. **Registration**:
|
|
35
|
-
2. **State
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
1. **Registration**: you register each predicate on the Picked client at startup.
|
|
39
|
+
2. **State snapshot**: the SDK gives the predicate a frozen state snapshot. The
|
|
40
|
+
snapshot holds the current responses, the fields, and the browser context.
|
|
41
|
+
3. **Condition evaluation**: an experience condition invokes the predicate with a
|
|
42
|
+
fresh snapshot on each call.
|
|
43
|
+
4. **Return types**: a predicate can return a boolean, a string, an array of
|
|
44
|
+
strings, or `undefined`. It can also return a promise that resolves to one of
|
|
45
|
+
those.
|
|
38
46
|
|
|
39
|
-
##
|
|
47
|
+
## Use a predicate in a condition
|
|
40
48
|
|
|
41
|
-
|
|
49
|
+
Most predicates are used inside condition logic, through the `invoke` operator:
|
|
42
50
|
|
|
43
51
|
```ts
|
|
44
52
|
const myExperience = createExperience({
|
|
@@ -49,10 +57,10 @@ const myExperience = createExperience({
|
|
|
49
57
|
'==': [
|
|
50
58
|
{
|
|
51
59
|
invoke: {
|
|
52
|
-
var: 'predicates.is-example-page', //
|
|
60
|
+
var: 'predicates.is-example-page', // Points at a predicate that calls your endpoint.
|
|
53
61
|
},
|
|
54
62
|
},
|
|
55
|
-
true, // Check
|
|
63
|
+
true, // Check that the return value is true.
|
|
56
64
|
],
|
|
57
65
|
},
|
|
58
66
|
{
|
|
@@ -62,7 +70,7 @@ const myExperience = createExperience({
|
|
|
62
70
|
var: 'predicates.account-tier',
|
|
63
71
|
},
|
|
64
72
|
},
|
|
65
|
-
'/trial', //
|
|
73
|
+
'/trial', // The condition is true when the string matches.
|
|
66
74
|
],
|
|
67
75
|
},
|
|
68
76
|
],
|
|
@@ -71,42 +79,50 @@ const myExperience = createExperience({
|
|
|
71
79
|
```
|
|
72
80
|
|
|
73
81
|
:::tip
|
|
74
|
-
|
|
82
|
+
You can also use a predicate in `validation` logic.
|
|
75
83
|
:::
|
|
76
84
|
|
|
77
|
-
##
|
|
85
|
+
## Use the state snapshot
|
|
78
86
|
|
|
79
|
-
Each predicate
|
|
87
|
+
Each predicate takes a `StateSnapshot` object with these properties:
|
|
80
88
|
|
|
81
89
|
```ts
|
|
82
90
|
interface StateSnapshot {
|
|
83
|
-
client: Picked // The Picked client
|
|
91
|
+
client: Picked // The Picked client.
|
|
84
92
|
responses?: Array<{
|
|
85
|
-
//
|
|
93
|
+
// Every response from the user.
|
|
86
94
|
fieldId: string
|
|
87
95
|
value: FieldValue | undefined
|
|
88
96
|
}>
|
|
89
97
|
args?: unknown[]
|
|
90
|
-
fields?: Field[] //
|
|
91
|
-
location?: Location //
|
|
92
|
-
document?: Document //
|
|
93
|
-
sessionStorage?: Storage //
|
|
94
|
-
localStorage?: Storage //
|
|
98
|
+
fields?: Field[] // Every registered field.
|
|
99
|
+
location?: Location // The browser location: URL, pathname, search, and more.
|
|
100
|
+
document?: Document // The browser document object.
|
|
101
|
+
sessionStorage?: Storage // The browser sessionStorage API.
|
|
102
|
+
localStorage?: Storage // The browser localStorage API.
|
|
95
103
|
}
|
|
96
104
|
```
|
|
97
105
|
|
|
98
106
|
:::warning
|
|
99
|
-
In server-side rendering
|
|
100
|
-
and `localStorage`
|
|
107
|
+
In server-side rendering, and in any environment that is not a browser, `location`,
|
|
108
|
+
`document`, `sessionStorage`, and `localStorage` are `undefined`.
|
|
101
109
|
:::
|
|
102
110
|
|
|
103
|
-
## Best
|
|
104
|
-
|
|
105
|
-
- **Keep
|
|
106
|
-
|
|
107
|
-
- **
|
|
108
|
-
|
|
109
|
-
- **
|
|
110
|
-
|
|
111
|
-
- **
|
|
112
|
-
|
|
111
|
+
## Best practices
|
|
112
|
+
|
|
113
|
+
- **Keep each predicate pure**: a predicate must be deterministic and free of side
|
|
114
|
+
effects. The same snapshot must always give the same result.
|
|
115
|
+
- **Handle missing data**: check that a response or a field is there before you read
|
|
116
|
+
its value. Return `undefined` when the data is not available. Do not throw.
|
|
117
|
+
- **Do not do heavy work**: the SDK can invoke a predicate many times. Keep the logic
|
|
118
|
+
small and fast.
|
|
119
|
+
- **Use an async predicate only when you must**: the SDK supports one, but it can
|
|
120
|
+
make the experience slow to appear. Use synchronous logic where you can.
|
|
121
|
+
- **Return one type**: do not return a boolean from a predicate one time and a string
|
|
122
|
+
the next.
|
|
123
|
+
- **Use a meaningful name**: a predicate ID must say what the predicate evaluates,
|
|
124
|
+
such as `is-premium-user` or `has-items-in-cart`.
|
|
125
|
+
- **Keep the result of expensive work**: when a predicate must do expensive work, put
|
|
126
|
+
the result in `localStorage` or `sessionStorage` inside the predicate.
|
|
127
|
+
- **Do not change the snapshot**: the state snapshot is frozen. A change to it throws
|
|
128
|
+
an error in strict mode.
|
|
@@ -4,11 +4,17 @@ layout: docs
|
|
|
4
4
|
|
|
5
5
|
# Server-Side Rendering
|
|
6
6
|
|
|
7
|
-
Render Picked
|
|
7
|
+
Render a Picked Page on your server so that search engines can read it. Then hydrate
|
|
8
|
+
the page on the client. This page shows the whole shape. The pages after it cover
|
|
9
|
+
each part.
|
|
8
10
|
|
|
9
11
|
## Why server-side rendering
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
The search rank of a Page matters, so the HTML that your server returns must hold the
|
|
14
|
+
content of that Page. A crawler and a social preview both read that HTML directly.
|
|
15
|
+
Neither waits for the JavaScript in the browser. A Page that the server renders
|
|
16
|
+
therefore has its content in the first response, and the client takes over after
|
|
17
|
+
that.
|
|
12
18
|
|
|
13
19
|
## The flow
|
|
14
20
|
|
|
@@ -20,13 +26,19 @@ projectConfig (Delivery API)
|
|
|
20
26
|
```
|
|
21
27
|
|
|
22
28
|
1. Fetch your published `projectConfig` from the [Delivery API](/delivery-api).
|
|
23
|
-
2. Call `serialize()` on the server. It
|
|
29
|
+
2. Call `serialize()` on the server. It finds which experiences match the URL, reads
|
|
30
|
+
their content, and returns a `PickedSnapshot` as plain JSON. See
|
|
31
|
+
[Server Snapshot](/sdk/ssr/serialize).
|
|
24
32
|
3. Embed the snapshot in your HTML payload.
|
|
25
|
-
4. On the client, seed a Picked client from the snapshot
|
|
33
|
+
4. On the client, seed a Picked client from the snapshot. The first paint then
|
|
34
|
+
matches the markup from the server. Hydrate after that. See
|
|
35
|
+
[Rendering and Hydration](/sdk/ssr/rendering-and-hydration).
|
|
26
36
|
|
|
27
|
-
|
|
37
|
+
The snapshot carries the exact options that the server chose with, and the condition
|
|
38
|
+
results that it kept. The first render on the client therefore reuses the decisions
|
|
39
|
+
of the server. It does not evaluate them again.
|
|
28
40
|
|
|
29
|
-
## React
|
|
41
|
+
## React and Svelte side by side
|
|
30
42
|
|
|
31
43
|
:::code-group
|
|
32
44
|
|
|
@@ -52,11 +64,16 @@ import { PickedProvider, ExperienceSlot } from '@picked/react'
|
|
|
52
64
|
|
|
53
65
|
## The server entry point
|
|
54
66
|
|
|
55
|
-
The server helpers live in a dedicated entry so browser
|
|
67
|
+
The server helpers live in a dedicated entry point, so a browser bundle never holds
|
|
68
|
+
them:
|
|
56
69
|
|
|
57
70
|
```ts
|
|
58
71
|
import { serialize, registerProjectConfig } from '@picked/client/server'
|
|
59
72
|
import type { PickedSnapshot } from '@picked/client/server'
|
|
60
73
|
```
|
|
61
74
|
|
|
62
|
-
|
|
75
|
+
Go to [Server Snapshot](/sdk/ssr/serialize) for the `serialize()` reference. Go to
|
|
76
|
+
[Rendering and Hydration](/sdk/ssr/rendering-and-hydration) to connect React and
|
|
77
|
+
Svelte. Go to [Slots and Placements](/sdk/ssr/slots-and-placements) for how a slot
|
|
78
|
+
chooses an experience. If the SDK is new to you, read
|
|
79
|
+
[Installation](/sdk) first.
|
|
@@ -2,20 +2,25 @@
|
|
|
2
2
|
layout: docs
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
# Rendering
|
|
5
|
+
# Rendering and Hydration
|
|
6
6
|
|
|
7
|
-
Seed a Picked client from the server snapshot, render your slots, and hydrate.
|
|
7
|
+
Seed a Picked client from the server snapshot, render your slots, and hydrate. This
|
|
8
|
+
page covers React and Svelte.
|
|
8
9
|
|
|
9
|
-
## React: PickedProvider
|
|
10
|
+
## React: `PickedProvider`
|
|
10
11
|
|
|
11
|
-
`PickedProvider`
|
|
12
|
+
`PickedProvider` gives a client to each component below it. Its props are a union of
|
|
13
|
+
two modes:
|
|
12
14
|
|
|
13
15
|
| Props | Mode | Description |
|
|
14
16
|
| --- | --- | --- |
|
|
15
|
-
| `{ options, snapshot? }` | Create | The provider calls `createClient(options)` and hydrates
|
|
16
|
-
| `{ client, snapshot? }` | Adopt | The provider uses
|
|
17
|
+
| `{ options, snapshot? }` | Create | The provider calls `createClient(options)` and hydrates the client from the snapshot. |
|
|
18
|
+
| `{ client, snapshot? }` | Adopt | The provider uses a client that is there already, such as a global one for the whole application. It can also seed that client from the snapshot. |
|
|
17
19
|
|
|
18
|
-
The provider seeds
|
|
20
|
+
The provider seeds the client at once, so the server and the first paint on the
|
|
21
|
+
client give the same markup. In the browser the provider calls `client.start()` from
|
|
22
|
+
an effect. `start()` is idempotent, so it changes nothing on a client that already
|
|
23
|
+
started.
|
|
19
24
|
|
|
20
25
|
```tsx
|
|
21
26
|
import { PickedProvider, ExperienceSlot } from '@picked/react'
|
|
@@ -25,9 +30,11 @@ import { PickedProvider, ExperienceSlot } from '@picked/react'
|
|
|
25
30
|
</PickedProvider>
|
|
26
31
|
```
|
|
27
32
|
|
|
28
|
-
## Svelte: setPickedContext
|
|
33
|
+
## Svelte: `setPickedContext`
|
|
29
34
|
|
|
30
|
-
`setPickedContext`
|
|
35
|
+
`setPickedContext` does the same work in the Svelte style. Call it one time at the
|
|
36
|
+
top of the `<script>` block of a component. It takes the same two modes,
|
|
37
|
+
`{ options, snapshot? }` or `{ client, snapshot? }`, and it returns the client.
|
|
31
38
|
|
|
32
39
|
```svelte
|
|
33
40
|
<script lang="ts">
|
|
@@ -39,13 +46,20 @@ import { PickedProvider, ExperienceSlot } from '@picked/react'
|
|
|
39
46
|
<ExperienceSlot name="page" />
|
|
40
47
|
```
|
|
41
48
|
|
|
42
|
-
##
|
|
49
|
+
## Place the slot
|
|
43
50
|
|
|
44
|
-
|
|
51
|
+
Put `<ExperienceSlot name="page" />` where the Page must render. The slot renders
|
|
52
|
+
every experience that points at it and whose condition is active. See
|
|
53
|
+
[Slots and Placements](/sdk/ssr/slots-and-placements) for how a slot chooses and
|
|
54
|
+
orders your experiences.
|
|
45
55
|
|
|
46
|
-
##
|
|
56
|
+
## Render the page component
|
|
47
57
|
|
|
48
|
-
The SDK does not
|
|
58
|
+
The SDK does not connect content to a component. You own the component that a Page
|
|
59
|
+
renders. Register it under the name that the Page uses. The Page Layout in the dashboard
|
|
60
|
+
points at that same name. The component reads the resolved entry data
|
|
61
|
+
through a content hook: `useStepContent()` in React, and `getStepContent()` in
|
|
62
|
+
Svelte.
|
|
49
63
|
|
|
50
64
|
:::code-group
|
|
51
65
|
|
|
@@ -85,11 +99,15 @@ export function CMSPage() {
|
|
|
85
99
|
|
|
86
100
|
:::
|
|
87
101
|
|
|
88
|
-
`useStepContent()` returns the
|
|
102
|
+
`useStepContent()` returns the `data` object of the entry directly. It returns `null`
|
|
103
|
+
while no content is registered. `getStepContent()` returns a store, and you read it
|
|
104
|
+
with `$content`.
|
|
89
105
|
|
|
90
106
|
## Full example
|
|
91
107
|
|
|
92
|
-
This is the
|
|
108
|
+
This is the page-slot setup that we tested. The page client reads the project options
|
|
109
|
+
and renders the Page experience that points at `.picked-slot-page` and matches the
|
|
110
|
+
current URL.
|
|
93
111
|
|
|
94
112
|
:::code-group
|
|
95
113
|
|
|
@@ -138,4 +156,7 @@ export function PageSlot({ snapshot }: { snapshot?: PickedSnapshot }) {
|
|
|
138
156
|
|
|
139
157
|
:::
|
|
140
158
|
|
|
141
|
-
For the
|
|
159
|
+
For the full version with the server snapshot, see
|
|
160
|
+
[Build a Personalized Page](/guides/build-a-personalized-page). For the snapshot
|
|
161
|
+
reference, see [Server Snapshot](/sdk/ssr/serialize). For how an experience
|
|
162
|
+
registers, see [SDK Experiences](/sdk/experiences).
|