@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.
Files changed (177) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/api-CH2FNa6S.cjs +1 -0
  3. package/dist/api-CgqRvuKL.js +38 -0
  4. package/dist/cjs/form/index.js +1 -1
  5. package/dist/cjs/index.js +2 -2
  6. package/dist/cjs/plugins/dom-observer/index.js +1 -1
  7. package/dist/cjs/plugins/dynamic-experiences/index.js +1 -1
  8. package/dist/cjs/server/index.js +1 -1
  9. package/dist/docs/actions.mdx +38 -18
  10. package/dist/docs/api/authentication.md +75 -48
  11. package/dist/docs/api/authorization-and-scopes.md +63 -47
  12. package/dist/docs/api/error-handling.md +70 -54
  13. package/dist/docs/api/index.md +41 -22
  14. package/dist/docs/api/pagination.md +30 -20
  15. package/dist/docs/api-keys.mdx +11 -6
  16. package/dist/docs/chrome-extension.md +22 -5
  17. package/dist/docs/conditions.mdx +32 -19
  18. package/dist/docs/content/asset-library.mdx +65 -34
  19. package/dist/docs/content/content-entries.mdx +8 -2
  20. package/dist/docs/content/layouts.mdx +45 -29
  21. package/dist/docs/content/pages.mdx +29 -14
  22. package/dist/docs/content/sections.mdx +19 -8
  23. package/dist/docs/delivery-api/experience-content.md +10 -5
  24. package/dist/docs/delivery-api/index.md +19 -12
  25. package/dist/docs/delivery-api/preview-caching-crawling.md +27 -14
  26. package/dist/docs/delivery-api/project-config.md +6 -4
  27. package/dist/docs/experiences.mdx +26 -10
  28. package/dist/docs/features.mdx +66 -25
  29. package/dist/docs/fields.mdx +42 -27
  30. package/dist/docs/flow-builder/building-flows.md +38 -20
  31. package/dist/docs/flow-builder/condition-builder.md +32 -21
  32. package/dist/docs/flow-builder/keyboard-shortcuts.md +18 -18
  33. package/dist/docs/flow-builder/nodes.md +26 -13
  34. package/dist/docs/flow-builder/overview.mdx +28 -15
  35. package/dist/docs/gtm-template.md +35 -26
  36. package/dist/docs/guides/build-a-personalized-page.mdx +36 -18
  37. package/dist/docs/image-optimisation-service.md +33 -17
  38. package/dist/docs/index.md +28 -17
  39. package/dist/docs/mcp.md +46 -38
  40. package/dist/docs/pickedql/data-sources.md +65 -43
  41. package/dist/docs/pickedql/dimensions.md +27 -12
  42. package/dist/docs/pickedql/examples.md +38 -26
  43. package/dist/docs/pickedql/filtering.md +10 -6
  44. package/dist/docs/pickedql/index.md +32 -14
  45. package/dist/docs/pickedql/schema.md +85 -76
  46. package/dist/docs/pickedql/virtual-functions.md +62 -36
  47. package/dist/docs/projects.mdx +14 -7
  48. package/dist/docs/reports.mdx +5 -3
  49. package/dist/docs/sdk/actions.md +33 -17
  50. package/dist/docs/sdk/conditions.md +8 -5
  51. package/dist/docs/sdk/content-security-policy.md +5 -4
  52. package/dist/docs/sdk/experiences.md +29 -11
  53. package/dist/docs/sdk/feature-allocation.md +79 -47
  54. package/dist/docs/sdk/features.md +130 -76
  55. package/dist/docs/sdk/fields.md +66 -42
  56. package/dist/docs/sdk/index.md +22 -13
  57. package/dist/docs/sdk/plugins/create-experience.md +18 -15
  58. package/dist/docs/sdk/plugins/dynamic-experiences.md +42 -16
  59. package/dist/docs/sdk/predicates.md +53 -37
  60. package/dist/docs/sdk/ssr/index.md +25 -8
  61. package/dist/docs/sdk/ssr/rendering-and-hydration.md +37 -16
  62. package/dist/docs/sdk/ssr/serialize.md +38 -20
  63. package/dist/docs/sdk/ssr/slots-and-placements.md +28 -10
  64. package/dist/docs/sdk/steps.md +100 -55
  65. package/dist/docs/steps.mdx +20 -11
  66. package/dist/docs/team-management.md +17 -11
  67. package/dist/docs/vite-plugin.md +26 -19
  68. package/dist/docs/workspace-versioning.mdx +14 -8
  69. package/dist/docs/workspaces.mdx +55 -24
  70. package/dist/{dom-BwmTHeT7.js → dom-BqcToNL0.js} +1 -3
  71. package/dist/{dom-CDrHc_j9.cjs → dom-DlLH3FxV.cjs} +1 -1
  72. package/dist/esm/form/index.js +2 -4
  73. package/dist/esm/index.js +50 -45
  74. package/dist/esm/plugins/dom-observer/index.js +1 -1
  75. package/dist/esm/plugins/dynamic-experiences/index.js +2 -2
  76. package/dist/esm/preview/index.js +1 -1
  77. package/dist/esm/server/index.js +12 -10
  78. package/dist/{experience-instance-DOJShRk5.js → experience-instance-DGk11d8a.js} +3 -3
  79. package/dist/experience-instance-Dej3P1sE.cjs +1 -0
  80. package/dist/{plugin-BxMR7fOV.js → plugin-D2UqFikQ.js} +2 -2
  81. package/dist/plugin-DCW-tnjO.cjs +1 -0
  82. package/dist/{preview-channel-DW9J8t4B.js → preview-channel-CSr96XAo.js} +1 -1
  83. package/dist/{protocol-DSqOlW2r.js → protocol-C5KkkiZc.js} +1 -1
  84. package/dist/types/client.d.ts +7 -6
  85. package/dist/types/entities/action/action.d.ts +5 -4
  86. package/dist/types/entities/action/action.d.ts.map +1 -1
  87. package/dist/types/entities/component/component.d.ts +6 -5
  88. package/dist/types/entities/component/component.d.ts.map +1 -1
  89. package/dist/types/entities/condition/condition.d.ts +5 -5
  90. package/dist/types/entities/content/content.d.ts +4 -3
  91. package/dist/types/entities/experience/experience.d.ts +11 -10
  92. package/dist/types/entities/experience/experience.d.ts.map +1 -1
  93. package/dist/types/entities/experience-instance/experience-instance.d.ts +10 -9
  94. package/dist/types/entities/feature/feature.d.ts +3 -2
  95. package/dist/types/entities/feature/feature.d.ts.map +1 -1
  96. package/dist/types/entities/field/field.d.ts +7 -6
  97. package/dist/types/entities/framework/framework.d.ts +7 -6
  98. package/dist/types/entities/framework/framework.d.ts.map +1 -1
  99. package/dist/types/entities/history/history.d.ts +6 -6
  100. package/dist/types/entities/plugin/plugin.d.ts +3 -2
  101. package/dist/types/entities/plugin/plugin.d.ts.map +1 -1
  102. package/dist/types/entities/predicate/predicate.d.ts +4 -3
  103. package/dist/types/entities/predicate/predicate.d.ts.map +1 -1
  104. package/dist/types/entities/response/response-validation.d.ts +3 -2
  105. package/dist/types/entities/response/response.d.ts +12 -11
  106. package/dist/types/entities/session/session.d.ts +2 -1
  107. package/dist/types/entities/step/step.d.ts +6 -5
  108. package/dist/types/exports.d.ts +2 -1
  109. package/dist/types/exports.d.ts.map +1 -1
  110. package/dist/types/form/build-responses.d.ts +2 -1
  111. package/dist/types/form/create-field-evaluator.d.ts +4 -3
  112. package/dist/types/form/create-field-validation.d.ts +5 -4
  113. package/dist/types/form/create-form.d.ts +7 -6
  114. package/dist/types/form/create-submit-pipeline.d.ts +7 -6
  115. package/dist/types/form/get-step-fields.d.ts +3 -2
  116. package/dist/types/lifecycle/condition-selection.d.ts +14 -4
  117. package/dist/types/lifecycle/lifecycle.d.ts +4 -3
  118. package/dist/types/lifecycle/rules-logic.d.ts +4 -3
  119. package/dist/types/lifecycle/state-snapshot.d.ts +4 -3
  120. package/dist/types/picked.d.ts +64 -62
  121. package/dist/types/picked.d.ts.map +1 -1
  122. package/dist/types/ping.d.ts +1 -1
  123. package/dist/types/ping.d.ts.map +1 -1
  124. package/dist/types/plugins/create-experience/create-experience.d.ts +9 -8
  125. package/dist/types/plugins/create-experience/create-experience.d.ts.map +1 -1
  126. package/dist/types/plugins/create-experience/plugin.d.ts +3 -2
  127. package/dist/types/plugins/create-experience/plugin.d.ts.map +1 -1
  128. package/dist/types/plugins/create-experience/serialization.d.ts.map +1 -1
  129. package/dist/types/plugins/dom-observer/plugin.d.ts +1 -1
  130. package/dist/types/plugins/dynamic-experiences/plugin.d.ts +3 -3
  131. package/dist/types/plugins/dynamic-experiences/types.d.ts +8 -5
  132. package/dist/types/plugins/experience-browser-storage/plugin.d.ts +1 -1
  133. package/dist/types/plugins/experience-browser-storage/plugin.d.ts.map +1 -1
  134. package/dist/types/plugins/history-browser-storage/plugin.d.ts +1 -1
  135. package/dist/types/plugins/history-browser-storage/plugin.d.ts.map +1 -1
  136. package/dist/types/plugins/responses-browser-storage/plugin.d.ts +1 -1
  137. package/dist/types/plugins/responses-browser-storage/plugin.d.ts.map +1 -1
  138. package/dist/types/plugins/responses-indexeddb/plugin.d.ts +1 -1
  139. package/dist/types/plugins/single-page-app/plugin.d.ts +1 -1
  140. package/dist/types/plugins/single-page-app/plugin.d.ts.map +1 -1
  141. package/dist/types/plugins/web-components/plugin.d.ts +2 -1
  142. package/dist/types/plugins/window-object/plugin.d.ts +2 -2
  143. package/dist/types/preview-channel.d.ts +5 -1
  144. package/dist/types/preview-receiver/index.d.ts +18 -1
  145. package/dist/types/preview-receiver/protocol.d.ts +10 -1
  146. package/dist/types/server/index.d.ts +3 -3
  147. package/dist/types/server/serialize.d.ts +2 -2
  148. package/dist/types/utils/dom.d.ts +2 -1
  149. package/dist/types/utils/events.d.ts +6 -6
  150. package/dist/types/utils/events.d.ts.map +1 -1
  151. package/dist/types/utils/global-obj.d.ts +1 -1
  152. package/dist/types/utils/global-obj.d.ts.map +1 -1
  153. package/dist/types/utils/id.d.ts.map +1 -1
  154. package/dist/types/utils/logger.d.ts +2 -1
  155. package/dist/types/utils/logger.d.ts.map +1 -1
  156. package/dist/types/utils/node-tree/get-next-node-from-action.d.ts +2 -1
  157. package/dist/types/utils/node-tree/get-next-node-from-condition.d.ts +2 -1
  158. package/dist/types/utils/node-tree/get-next-node.d.ts +1 -1
  159. package/dist/types/utils/node-tree/get-next-node.d.ts.map +1 -1
  160. package/dist/types/utils/node-tree/get-origin-node.d.ts +1 -1
  161. package/dist/types/utils/node-tree/get-origin-node.d.ts.map +1 -1
  162. package/dist/types/utils/node-tree/min-max-steps-to-leaf.d.ts +1 -1
  163. package/dist/types/utils/node-tree/min-max-steps-to-leaf.d.ts.map +1 -1
  164. package/dist/types/utils/node-tree/min-max-steps-to-origin.d.ts +1 -1
  165. package/dist/types/utils/node-tree/min-max-steps-to-origin.d.ts.map +1 -1
  166. package/dist/types/utils/node-tree/next-reachable-step-nodes.d.ts +1 -1
  167. package/dist/types/utils/node-tree/next-reachable-step-nodes.d.ts.map +1 -1
  168. package/dist/types/utils/nodes.d.ts +1 -1
  169. package/dist/types/utils/picked-config.d.ts +2 -1
  170. package/dist/types/utils/rules-logic.d.ts +6 -2
  171. package/dist/types/utils/storage.d.ts +1 -1
  172. package/dist/types/utils/storage.d.ts.map +1 -1
  173. package/package.json +22 -23
  174. package/dist/api-CoUkIxIr.cjs +0 -1
  175. package/dist/api-ahdSBQ3Y.js +0 -40
  176. package/dist/experience-instance-DurC3pE-.cjs +0 -1
  177. package/dist/plugin-CASdGtTX.cjs +0 -1
@@ -4,13 +4,15 @@ layout: docs
4
4
 
5
5
  # Picked SDK
6
6
 
7
- The Picked SDK enables you to add dynamic, personalized experiences to your web applications with minimal code.
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 and works with React, Svelte, Vue, and vanilla JavaScript.
11
+ The SDK is framework-agnostic. It works with React, Svelte, Vue, and plain
12
+ JavaScript.
11
13
  :::
12
14
 
13
- ## Quick Start
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 server-only helpers, such as `serialize()`, that produce an SSR snapshot. It is a separate entry point so browser bundles never include it. See [Server-Side Rendering](/sdk/ssr).
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
- If you’re running Picked in a browser environment, access the Picked client in the console with `window.Picked`
51
+ In a browser, you can reach the Picked client from the console with `window.Picked`.
48
52
  :::
49
53
 
50
- ## Precondition & Condition
54
+ ## Precondition and condition
51
55
 
52
56
  | Category | Question | Description |
53
57
  | ------------ | ---------------------- | --------------------------------------------------------------------------------------------- |
54
- | Precondition | Should Picked start? | Use for environment checks, page filtering, or feature flags that completely disable the SDK. |
55
- | Condition | Is this user eligible? | Use for user segmentation, A/B test enrollment, or targeting specific user groups. |
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 picked config
83
+ // The rest of the Picked options go here.
80
84
  })
81
85
  ```
82
86
 
83
87
  :::warning
84
- A precondition or condition that references a flag reads its bucket, which allocates the visitor at that moment. Preconditions run at startup, so a flag referenced there is allocated for every visitor before anything else happens — including visitors who never reach the experience it gates.
85
-
86
- See [Allocation and exposure](/sdk/feature-allocation) before referencing a flag in a precondition.
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
- A plugin to register an array of experiences to the Picked client.
7
+ This plugin registers an array of experiences on the Picked client.
8
8
 
9
- ## Setup Plugin
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
- ## Defining Steps
29
+ ## Define a step
30
30
 
31
- Steps are defined using `createStep()`. They are added directly to an experience using `experience.addStep()`.
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 standalone steps
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 and add steps to build the flow
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 requires a `name` that identifies it in analytics events. An `id` is optional — one will be generated automatically if omitted.
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
- Steps can be used on multiple experiences.
62
+ You can use the same step on more than one experience.
61
63
 
62
- ### Adding Fields to Steps
64
+ ### Add a field to a step
63
65
 
64
- Fields are defined with `createField()`.
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
- Fields are added to a step with `step.addField()`, or an array of `fieldIds` when creating a step:
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
- ## Using conditions in an experience
102
+ ## Use a condition in an experience
100
103
 
101
- [Conditions](../conditions) are used to define when an experience will become active for the user.
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
- Navigation between steps is handled with [conditions](../conditions). Use the `create`
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
- A plugin that fetches your project configuration from the Picked API at runtime, letting you manage experiences, feature flags, steps, fields, and conditions entirely from the [dashboard](/experiences).
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
- ## Setup Plugin
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
- Unlike [createExperiencePlugin](./create-experience.md), you do not define experiences, steps, or fields in code. All configuration is managed from the [Picked dashboard](/experiences).
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 It Works
35
+ ## How it works
32
36
 
33
- When the client starts, the plugin resolves your project configuration using a three-tier strategy:
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** — When you [enter preview mode from the dashboard](/workspaces#preview), the plugin fetches fresh config from the API. Feature flag decisions use session storage so they are not persisted beyond the current tab.
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 config** — If `projectConfig` was passed as a plugin option, or the [Vite Plugin](/vite-plugin) injected config at build time, the plugin uses that config with no network request.
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 fetch** — Otherwise, the plugin fetches config from the API when the client starts.
49
+ 3. **Runtime read** — in every other case, the plugin reads the options from the API
50
+ when the client starts.
40
51
 
41
- Once resolved, the plugin registers all feature flags, conditions, fields, steps, and experiences with the client. Experiences then initialize according to their [conditions](/sdk/conditions) and [target selectors](/sdk/experiences).
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) eliminates the runtime API request in production by embedding your project config at build time. This is the recommended setup for Vite-based applications.
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 `weight` (0–100) that controls the percentage of traffic allocated to it. The plugin uses a deterministic hash of the user's session ID to produce a stable allocation value. A user whose value falls below the flag's weight is allocated to that flag — the same user always gets the same result.
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
- Feature flag decisions are cached in `localStorage` by default, or `sessionStorage` when in [preview mode](#preview-mode).
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
- ## Preview Mode
73
+ The plugin caches a feature flag decision in `localStorage` by default. In
74
+ [preview mode](#preview-mode) it uses `sessionStorage`.
54
75
 
55
- When you [enter preview mode from the dashboard](/workspaces#preview), the SDK receives a preview token and activates preview mode:
76
+ ## Preview mode
56
77
 
57
- - The plugin fetches fresh config from the API using the token, bypassing any build-time or cached config
58
- - Feature flag decisions use session storage, so closing the tab resets them
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
- Predicates enable reusable condition logic based on current application state. Each predicate is a pure function that receives a state snapshot containing responses, fields, browser context, and optional arguments.
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
- ## Defining Predicates
12
+ ## Define a predicate
10
13
 
11
- Define predicates when creating the Picked client by passing a `predicates` object. Each predicate requires a unique identifier and a function that receives a state snapshot:
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 // returns "trial"
28
+ return data.result // Returns "trial".
25
29
  },
26
30
  },
27
31
  })
28
32
  ```
29
33
 
30
- ## How Predicates Work
34
+ ## How predicates work
31
35
 
32
- When a predicate is registered and used in conditions, the SDK follows this execution flow:
36
+ After you register a predicate and a condition uses it, the SDK follows this flow:
33
37
 
34
- 1. **Registration**: Predicates are registered to the Picked client during initialization
35
- 2. **State Snapshot Creation**: When invoked, predicates receive a frozen state snapshot containing current responses, fields, and browser context
36
- 3. **Condition Evaluation**: Experience conditions invoke predicates with a fresh snapshot whenever called
37
- 4. **Return Flexibility**: Predicates can return boolean, string, string array, undefined, or promises that resolve to these types
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
- ## Using Predicates in Conditions
47
+ ## Use a predicate in a condition
40
48
 
41
- Predicates are most commonly used within condition logic via the `invoke` operator:
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', // Reference predicate that calls user endpoint
60
+ var: 'predicates.is-example-page', // Points at a predicate that calls your endpoint.
53
61
  },
54
62
  },
55
- true, // Check if return value is true
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', // If string matches, evaluate to true
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
- Predicates can also be used in `validation` logic
82
+ You can also use a predicate in `validation` logic.
75
83
  :::
76
84
 
77
- ## Using the State Snapshot
85
+ ## Use the state snapshot
78
86
 
79
- Each predicate receives a `StateSnapshot` object with the following properties:
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 instance
91
+ client: Picked // The Picked client.
84
92
  responses?: Array<{
85
- // All user responses
93
+ // Every response from the user.
86
94
  fieldId: string
87
95
  value: FieldValue | undefined
88
96
  }>
89
97
  args?: unknown[]
90
- fields?: Field[] // All registered fields
91
- location?: Location // Browser location (URL, pathname, search, etc.)
92
- document?: Document // Browser document object
93
- sessionStorage?: Storage // Browser sessionStorage API
94
- localStorage?: Storage // Browser localStorage API
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 or non-browser environments, `location`, `document`, `sessionStorage`,
100
- and `localStorage` will be `undefined`.
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 Practices
104
-
105
- - **Keep predicates pure**: Predicates should be deterministic and side-effect free. Given the same snapshot, they should always return the same result
106
- - **Handle missing data gracefully**: Always check if responses or fields exist before accessing their values. Return `undefined` when data is unavailable rather than throwing errors
107
- - **Avoid heavy computations**: Predicates may be invoked frequently. Keep logic lightweight and fast
108
- - **Use async sparingly**: While async predicates are supported, they can delay experience rendering. Prefer synchronous logic when possible
109
- - **Return consistent types**: Don't mix return types (e.g., sometimes boolean, sometimes string) for the same predicate
110
- - **Use meaningful names**: Predicate IDs should clearly describe what they evaluate (e.g., `is-premium-user`, `has-items-in-cart`)
111
- - **Consider caching expensive operations**: If a predicate needs to perform expensive operations, consider caching the result in `localStorage` or `sessionStorage` within the predicate logic
112
- - **Don't mutate the snapshot**: The state snapshot is frozen. Any attempt to modify it will throw an error in strict mode
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 Pages on your server for SEO, then hydrate them on the client. This page shows the end-to-end shape; the following pages cover each piece.
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
- Pages are SEO-critical, so their content must be in the HTML your server returns. Crawlers and social unfurls read that HTML directly and do not wait for client-side JavaScript. Rendering a Page on the server puts its content in the initial response, then the client takes over.
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 evaluates which experiences match the URL, fetches their content, and returns a plain-JSON `PickedSnapshot`. See [Server Snapshot](/sdk/ssr/serialize).
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 so the first paint matches the server markup, then hydrate. See [Rendering & Hydration](/sdk/ssr/rendering-and-hydration).
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
- Because the snapshot carries the exact config the server selected with and the memoized condition results, the client's first render reuses the server's decisions instead of re-evaluating them.
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 vs Svelte at a glance
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 bundles never pull them in:
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
- Continue to [Server Snapshot](/sdk/ssr/serialize) for the `serialize()` reference, [Rendering & Hydration](/sdk/ssr/rendering-and-hydration) for wiring React and Svelte, and [Slots & Placements](/sdk/ssr/slots-and-placements) for how a slot selects experiences. New to the SDK? Start with [Installation](/sdk).
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 & Hydration
5
+ # Rendering and Hydration
6
6
 
7
- Seed a Picked client from the server snapshot, render your slots, and hydrate. React and Svelte below.
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` supplies a client to descendant components. Its props are a union of two construction modes:
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 it from the snapshot. |
16
- | `{ client, snapshot? }` | Adopt | The provider uses an existing client, such as an app-wide global, and optionally seeds it from the snapshot. |
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 synchronously, so the server render and the client's first paint produce the same markup. In the browser it calls `client.start()` from an effect; `start()` is idempotent, so an already-started client is left untouched.
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` is the Svelte-idiomatic equivalent. Call it once at the top of a component's `<script>`. It takes the same two modes, `{ options, snapshot? }` or `{ client, snapshot? }`, and returns the client.
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
- ## Placing the slot
49
+ ## Place the slot
43
50
 
44
- Drop `<ExperienceSlot name="page" />` where the Page should render. The slot renders every experience that targets it and whose condition is active. See [Slots & Placements](/sdk/ssr/slots-and-placements) for how a slot selects and orders experiences.
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
- ## Rendering the page component
56
+ ## Render the page component
47
57
 
48
- The SDK does not map content to components. You own the component a Page renders and register it under the name the Page uses, which is the same name the dashboard Page Layout maps to. The component reads the resolved entry data through the content hook: `useStepContent()` in React and `getStepContent()` in Svelte.
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 entry's `data` object directly, or `null` while no content is registered. `getStepContent()` returns a store you read with `$content`.
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 verified page-slot wiring. The page client fetches the project config and renders whichever Page experience targets `.picked-slot-page` and matches the current URL.
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 end-to-end version with the server snapshot, see [Build a Personalized Page](/guides/build-a-personalized-page). For the snapshot reference, see [Server Snapshot](/sdk/ssr/serialize). For how experiences register, see [SDK Experiences](/sdk/experiences).
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).