@picked/client 0.0.70 → 0.0.71-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/api-B0ydpQBE.js +38 -0
  3. package/dist/api-CObostrU.cjs +1 -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/explanation/conditions-and-the-logic-engine.md +94 -0
  10. package/dist/docs/explanation/feature-flag-allocation.md +176 -0
  11. package/dist/docs/explanation/how-picked-works.md +102 -0
  12. package/dist/docs/explanation/how-pickedql-works.md +76 -0
  13. package/dist/docs/explanation/pages-and-flows.md +64 -0
  14. package/dist/docs/explanation/the-content-model.md +77 -0
  15. package/dist/docs/explanation/the-two-apis.md +68 -0
  16. package/dist/docs/explanation/why-server-side-rendering.md +76 -0
  17. package/dist/docs/explanation/workspaces-and-versions.md +71 -0
  18. package/dist/docs/how-to/add-a-step-field-or-action.md +43 -0
  19. package/dist/docs/{pickedql/examples.md → how-to/analytics/index.md} +19 -6
  20. package/dist/docs/how-to/archive-or-delete-a-workspace.md +46 -0
  21. package/dist/docs/how-to/authenticate-with-a-magic-link.md +40 -0
  22. package/dist/docs/how-to/authenticate-with-a-password.md +51 -0
  23. package/dist/docs/how-to/authenticate-with-an-api-key.md +51 -0
  24. package/dist/docs/how-to/authenticate-with-oauth.md +40 -0
  25. package/dist/docs/{flow-builder/building-flows.md → how-to/build-a-flow.md} +13 -9
  26. package/dist/docs/how-to/connect-the-mcp-server.md +38 -0
  27. package/dist/docs/how-to/create-a-project.md +29 -0
  28. package/dist/docs/how-to/create-an-experience.md +42 -0
  29. package/dist/docs/how-to/create-and-revoke-an-api-key.md +37 -0
  30. package/dist/docs/how-to/debug-with-the-chrome-extension.md +57 -0
  31. package/dist/docs/how-to/define-a-page-layout.md +39 -0
  32. package/dist/docs/how-to/fill-and-publish-a-page.md +50 -0
  33. package/dist/docs/{gtm-template.md → how-to/install-the-gtm-template.md} +9 -3
  34. package/dist/docs/how-to/install-the-sdk.md +68 -0
  35. package/dist/docs/how-to/install-the-vite-plugin.md +56 -0
  36. package/dist/docs/how-to/invite-and-remove-members.md +34 -0
  37. package/dist/docs/how-to/preview-a-workspace.md +28 -0
  38. package/dist/docs/how-to/publish-a-workspace.md +30 -0
  39. package/dist/docs/how-to/read-a-feature-flag.md +101 -0
  40. package/dist/docs/how-to/register-a-component.md +55 -0
  41. package/dist/docs/how-to/register-an-action-handler.md +67 -0
  42. package/dist/docs/how-to/render-a-step-form.md +132 -0
  43. package/dist/docs/how-to/render-an-unpublished-preview.md +41 -0
  44. package/dist/docs/how-to/reuse-a-section.md +37 -0
  45. package/dist/docs/how-to/reuse-a-step-field-or-action.md +28 -0
  46. package/dist/docs/how-to/roll-back-a-published-version.md +26 -0
  47. package/dist/docs/how-to/run-an-ab-test.md +49 -0
  48. package/dist/docs/how-to/run-code-on-a-form-submission.md +125 -0
  49. package/dist/docs/how-to/server-render-a-page.md +81 -0
  50. package/dist/docs/how-to/set-a-content-security-policy.md +32 -0
  51. package/dist/docs/how-to/set-up-mfa.md +69 -0
  52. package/dist/docs/how-to/upload-and-use-an-asset.mdx +73 -0
  53. package/dist/docs/how-to/validate-a-field.md +52 -0
  54. package/dist/docs/how-to/write-a-condition.md +45 -0
  55. package/dist/docs/how-to/write-a-predicate.md +60 -0
  56. package/dist/docs/index.md +41 -70
  57. package/dist/docs/reference/dashboard/actions.mdx +53 -0
  58. package/dist/docs/{api-keys.mdx → reference/dashboard/api-keys.mdx} +7 -15
  59. package/dist/docs/reference/dashboard/conditions.mdx +42 -0
  60. package/dist/docs/reference/dashboard/content/asset-library.mdx +63 -0
  61. package/dist/docs/{content → reference/dashboard/content}/content-entries.mdx +11 -3
  62. package/dist/docs/{content → reference/dashboard/content}/layouts.mdx +11 -35
  63. package/dist/docs/reference/dashboard/content/pages.mdx +35 -0
  64. package/dist/docs/reference/dashboard/content/sections.mdx +28 -0
  65. package/dist/docs/{experiences.mdx → reference/dashboard/experiences.mdx} +9 -16
  66. package/dist/docs/{features.mdx → reference/dashboard/features.mdx} +26 -41
  67. package/dist/docs/{fields.mdx → reference/dashboard/fields.mdx} +26 -20
  68. package/dist/docs/{flow-builder/overview.mdx → reference/dashboard/flow-builder/canvas.mdx} +8 -13
  69. package/dist/docs/{flow-builder → reference/dashboard/flow-builder}/condition-builder.md +5 -13
  70. package/dist/docs/{flow-builder → reference/dashboard/flow-builder}/keyboard-shortcuts.md +7 -1
  71. package/dist/docs/{flow-builder → reference/dashboard/flow-builder}/nodes.md +10 -3
  72. package/dist/docs/reference/dashboard/projects.mdx +44 -0
  73. package/dist/docs/reference/dashboard/reports.mdx +43 -0
  74. package/dist/docs/reference/dashboard/steps.mdx +37 -0
  75. package/dist/docs/{team-management.md → reference/dashboard/team-management.md} +5 -22
  76. package/dist/docs/{workspace-versioning.mdx → reference/dashboard/workspace-versioning.mdx} +6 -10
  77. package/dist/docs/reference/dashboard/workspaces.mdx +76 -0
  78. package/dist/docs/{delivery-api → reference/delivery-api}/experience-content.md +11 -4
  79. package/dist/docs/reference/delivery-api/index.md +45 -0
  80. package/dist/docs/{delivery-api → reference/delivery-api}/preview-caching-crawling.md +12 -5
  81. package/dist/docs/{delivery-api → reference/delivery-api}/project-config.md +11 -4
  82. package/dist/docs/reference/glossary.md +173 -0
  83. package/dist/docs/reference/management-api/authentication.md +74 -0
  84. package/dist/docs/{api → reference/management-api}/authorization-and-scopes.md +10 -3
  85. package/dist/docs/{api → reference/management-api}/error-handling.md +9 -3
  86. package/dist/docs/{api → reference/management-api}/pagination.md +6 -0
  87. package/dist/docs/{pickedql → reference/pickedql}/data-sources.md +16 -9
  88. package/dist/docs/{pickedql → reference/pickedql}/dimensions.md +7 -0
  89. package/dist/docs/{pickedql → reference/pickedql}/filtering.md +7 -0
  90. package/dist/docs/reference/pickedql/index.md +18 -0
  91. package/dist/docs/{pickedql → reference/pickedql}/schema.md +13 -6
  92. package/dist/docs/{pickedql → reference/pickedql}/virtual-functions.md +7 -0
  93. package/dist/docs/reference/sdk/actions.md +30 -0
  94. package/dist/docs/{sdk → reference/sdk}/conditions.md +8 -1
  95. package/dist/docs/reference/sdk/content-security-policy.md +21 -0
  96. package/dist/docs/reference/sdk/experiences.md +51 -0
  97. package/dist/docs/{sdk → reference/sdk}/features.md +30 -117
  98. package/dist/docs/{sdk → reference/sdk}/fields.md +10 -88
  99. package/dist/docs/reference/sdk/index.md +68 -0
  100. package/dist/docs/{sdk → reference/sdk}/plugins/create-experience.md +10 -22
  101. package/dist/docs/reference/sdk/plugins/dynamic-experiences.md +26 -0
  102. package/dist/docs/reference/sdk/plugins/index.md +29 -0
  103. package/dist/docs/{sdk → reference/sdk}/plugins/window-object.md +6 -0
  104. package/dist/docs/reference/sdk/predicates.md +43 -0
  105. package/dist/docs/reference/sdk/ssr/index.md +33 -0
  106. package/dist/docs/{sdk → reference/sdk}/ssr/rendering-and-hydration.md +4 -63
  107. package/dist/docs/{sdk → reference/sdk}/ssr/serialize.md +6 -33
  108. package/dist/docs/{sdk → reference/sdk}/ssr/slots-and-placements.md +7 -1
  109. package/dist/docs/reference/sdk/steps.md +87 -0
  110. package/dist/docs/reference/tools/chrome-extension.md +16 -0
  111. package/dist/docs/{image-optimisation-service.md → reference/tools/image-optimisation-service.md} +8 -0
  112. package/dist/docs/{mcp.md → reference/tools/mcp.md} +5 -45
  113. package/dist/docs/{vite-plugin.md → reference/tools/vite-plugin.md} +8 -38
  114. package/dist/docs/{api/index.md → tutorials/first-api-call.md} +27 -15
  115. package/dist/docs/tutorials/first-experience-in-the-dashboard.mdx +143 -0
  116. package/dist/docs/tutorials/first-experience.mdx +255 -0
  117. package/dist/docs/tutorials/first-server-rendered-page.mdx +186 -0
  118. package/dist/{dom-BwmTHeT7.js → dom-BqcToNL0.js} +1 -3
  119. package/dist/{dom-CDrHc_j9.cjs → dom-DlLH3FxV.cjs} +1 -1
  120. package/dist/esm/form/index.js +2 -4
  121. package/dist/esm/index.js +306 -298
  122. package/dist/esm/plugins/dom-observer/index.js +1 -1
  123. package/dist/esm/plugins/dynamic-experiences/index.js +2 -2
  124. package/dist/esm/preview/index.js +1 -1
  125. package/dist/esm/server/index.js +12 -10
  126. package/dist/{experience-instance-DOJShRk5.js → experience-instance-DGk11d8a.js} +3 -3
  127. package/dist/experience-instance-Dej3P1sE.cjs +1 -0
  128. package/dist/plugin-CNna0UjB.cjs +1 -0
  129. package/dist/{plugin-ChPkTj__.js → plugin-DUOJpHaq.js} +2 -2
  130. package/dist/{preview-channel-DW9J8t4B.js → preview-channel-CSr96XAo.js} +1 -1
  131. package/dist/{protocol-DSqOlW2r.js → protocol-C5KkkiZc.js} +1 -1
  132. package/dist/types/client.d.ts +7 -6
  133. package/dist/types/entities/action/action.d.ts +5 -4
  134. package/dist/types/entities/action/action.d.ts.map +1 -1
  135. package/dist/types/entities/component/component.d.ts +6 -5
  136. package/dist/types/entities/component/component.d.ts.map +1 -1
  137. package/dist/types/entities/condition/condition.d.ts +5 -5
  138. package/dist/types/entities/content/content.d.ts +4 -3
  139. package/dist/types/entities/experience/experience.d.ts +11 -10
  140. package/dist/types/entities/experience/experience.d.ts.map +1 -1
  141. package/dist/types/entities/experience-instance/experience-instance.d.ts +10 -9
  142. package/dist/types/entities/feature/feature.d.ts +19 -2
  143. package/dist/types/entities/feature/feature.d.ts.map +1 -1
  144. package/dist/types/entities/field/field.d.ts +7 -6
  145. package/dist/types/entities/framework/framework.d.ts +7 -6
  146. package/dist/types/entities/framework/framework.d.ts.map +1 -1
  147. package/dist/types/entities/history/history.d.ts +6 -6
  148. package/dist/types/entities/plugin/plugin.d.ts +3 -2
  149. package/dist/types/entities/plugin/plugin.d.ts.map +1 -1
  150. package/dist/types/entities/predicate/predicate.d.ts +4 -3
  151. package/dist/types/entities/predicate/predicate.d.ts.map +1 -1
  152. package/dist/types/entities/response/response-validation.d.ts +3 -2
  153. package/dist/types/entities/response/response.d.ts +12 -11
  154. package/dist/types/entities/session/session.d.ts +2 -1
  155. package/dist/types/entities/step/step.d.ts +6 -5
  156. package/dist/types/exports.d.ts +2 -1
  157. package/dist/types/exports.d.ts.map +1 -1
  158. package/dist/types/form/build-responses.d.ts +2 -1
  159. package/dist/types/form/create-field-evaluator.d.ts +4 -3
  160. package/dist/types/form/create-field-validation.d.ts +5 -4
  161. package/dist/types/form/create-form.d.ts +7 -6
  162. package/dist/types/form/create-submit-pipeline.d.ts +7 -6
  163. package/dist/types/form/get-step-fields.d.ts +3 -2
  164. package/dist/types/lifecycle/condition-selection.d.ts +14 -4
  165. package/dist/types/lifecycle/lifecycle.d.ts +4 -3
  166. package/dist/types/lifecycle/rules-logic.d.ts +4 -3
  167. package/dist/types/lifecycle/state-snapshot.d.ts +4 -3
  168. package/dist/types/picked.d.ts +64 -62
  169. package/dist/types/picked.d.ts.map +1 -1
  170. package/dist/types/ping.d.ts +1 -1
  171. package/dist/types/ping.d.ts.map +1 -1
  172. package/dist/types/plugins/create-experience/create-experience.d.ts +9 -8
  173. package/dist/types/plugins/create-experience/create-experience.d.ts.map +1 -1
  174. package/dist/types/plugins/create-experience/plugin.d.ts +3 -2
  175. package/dist/types/plugins/create-experience/plugin.d.ts.map +1 -1
  176. package/dist/types/plugins/create-experience/serialization.d.ts.map +1 -1
  177. package/dist/types/plugins/dom-observer/plugin.d.ts +1 -1
  178. package/dist/types/plugins/dynamic-experiences/plugin.d.ts +3 -3
  179. package/dist/types/plugins/dynamic-experiences/types.d.ts +8 -5
  180. package/dist/types/plugins/experience-browser-storage/plugin.d.ts +1 -1
  181. package/dist/types/plugins/experience-browser-storage/plugin.d.ts.map +1 -1
  182. package/dist/types/plugins/history-browser-storage/plugin.d.ts +1 -1
  183. package/dist/types/plugins/history-browser-storage/plugin.d.ts.map +1 -1
  184. package/dist/types/plugins/responses-browser-storage/plugin.d.ts +1 -1
  185. package/dist/types/plugins/responses-browser-storage/plugin.d.ts.map +1 -1
  186. package/dist/types/plugins/responses-indexeddb/plugin.d.ts +1 -1
  187. package/dist/types/plugins/single-page-app/plugin.d.ts +1 -1
  188. package/dist/types/plugins/single-page-app/plugin.d.ts.map +1 -1
  189. package/dist/types/plugins/web-components/plugin.d.ts +2 -1
  190. package/dist/types/plugins/window-object/plugin.d.ts +2 -2
  191. package/dist/types/preview-channel.d.ts +5 -1
  192. package/dist/types/preview-receiver/index.d.ts +18 -1
  193. package/dist/types/preview-receiver/protocol.d.ts +10 -1
  194. package/dist/types/server/index.d.ts +3 -3
  195. package/dist/types/server/serialize.d.ts +2 -2
  196. package/dist/types/utils/dom.d.ts +2 -1
  197. package/dist/types/utils/events.d.ts +6 -6
  198. package/dist/types/utils/events.d.ts.map +1 -1
  199. package/dist/types/utils/global-obj.d.ts +1 -1
  200. package/dist/types/utils/global-obj.d.ts.map +1 -1
  201. package/dist/types/utils/id.d.ts.map +1 -1
  202. package/dist/types/utils/logger.d.ts +2 -1
  203. package/dist/types/utils/logger.d.ts.map +1 -1
  204. package/dist/types/utils/node-tree/get-next-node-from-action.d.ts +2 -1
  205. package/dist/types/utils/node-tree/get-next-node-from-condition.d.ts +2 -1
  206. package/dist/types/utils/node-tree/get-next-node.d.ts +1 -1
  207. package/dist/types/utils/node-tree/get-next-node.d.ts.map +1 -1
  208. package/dist/types/utils/node-tree/get-origin-node.d.ts +1 -1
  209. package/dist/types/utils/node-tree/get-origin-node.d.ts.map +1 -1
  210. package/dist/types/utils/node-tree/min-max-steps-to-leaf.d.ts +1 -1
  211. package/dist/types/utils/node-tree/min-max-steps-to-leaf.d.ts.map +1 -1
  212. package/dist/types/utils/node-tree/min-max-steps-to-origin.d.ts +1 -1
  213. package/dist/types/utils/node-tree/min-max-steps-to-origin.d.ts.map +1 -1
  214. package/dist/types/utils/node-tree/next-reachable-step-nodes.d.ts +1 -1
  215. package/dist/types/utils/node-tree/next-reachable-step-nodes.d.ts.map +1 -1
  216. package/dist/types/utils/nodes.d.ts +1 -1
  217. package/dist/types/utils/picked-config.d.ts +2 -1
  218. package/dist/types/utils/rules-logic.d.ts +6 -2
  219. package/dist/types/utils/storage.d.ts +1 -1
  220. package/dist/types/utils/storage.d.ts.map +1 -1
  221. package/package.json +22 -23
  222. package/dist/api-CPy9G49C.js +0 -40
  223. package/dist/api-Ce6xYI--.cjs +0 -1
  224. package/dist/docs/actions.mdx +0 -98
  225. package/dist/docs/api/authentication.md +0 -403
  226. package/dist/docs/chrome-extension.md +0 -68
  227. package/dist/docs/conditions.mdx +0 -72
  228. package/dist/docs/content/asset-library.mdx +0 -124
  229. package/dist/docs/content/pages.mdx +0 -82
  230. package/dist/docs/content/sections.mdx +0 -44
  231. package/dist/docs/delivery-api/index.md +0 -53
  232. package/dist/docs/guides/build-a-personalized-page.mdx +0 -161
  233. package/dist/docs/pickedql/index.md +0 -54
  234. package/dist/docs/projects.mdx +0 -44
  235. package/dist/docs/reports.mdx +0 -20
  236. package/dist/docs/sdk/actions.md +0 -82
  237. package/dist/docs/sdk/content-security-policy.md +0 -33
  238. package/dist/docs/sdk/experiences.md +0 -64
  239. package/dist/docs/sdk/feature-allocation.md +0 -161
  240. package/dist/docs/sdk/index.md +0 -98
  241. package/dist/docs/sdk/plugins/dynamic-experiences.md +0 -84
  242. package/dist/docs/sdk/plugins/index.md +0 -9
  243. package/dist/docs/sdk/predicates.md +0 -128
  244. package/dist/docs/sdk/ssr/index.md +0 -79
  245. package/dist/docs/sdk/steps.md +0 -479
  246. package/dist/docs/steps.mdx +0 -53
  247. package/dist/docs/workspaces.mdx +0 -133
  248. package/dist/experience-instance-DurC3pE-.cjs +0 -1
  249. package/dist/plugin-DHn11gn6.cjs +0 -1
@@ -1,33 +0,0 @@
1
- ---
2
- layout: docs
3
- ---
4
-
5
- # Content Security Policy
6
-
7
- The Picked SDK loads resources from the domains below. Add each domain to the CSP
8
- directive that the table gives.
9
-
10
- ## Required domains
11
-
12
- | Domain | Directive | Purpose |
13
- | --- | --- | --- |
14
- | `api.picked.app` | `connect-src` | API requests, project options, experiences, and the record of each session |
15
- | `evnts.pckd.dev` | `connect-src` | Analytics events, which Snowplow collects |
16
- | `img.picked-content.com` | `img-src` | Optimised images, which the [image optimisation service](../image-optimisation-service.md) serves |
17
-
18
- ## Configuration
19
-
20
- As an HTTP header:
21
-
22
- ```
23
- Content-Security-Policy: connect-src 'self' https://api.picked.app https://evnts.pckd.dev; img-src 'self' https://img.picked-content.com;
24
- ```
25
-
26
- Or as a `<meta>` tag:
27
-
28
- ```html
29
- <meta
30
- http-equiv="Content-Security-Policy"
31
- content="connect-src 'self' https://api.picked.app https://evnts.pckd.dev; img-src 'self' https://img.picked-content.com;"
32
- />
33
- ```
@@ -1,64 +0,0 @@
1
- ---
2
- layout: docs
3
- ---
4
-
5
- # Experiences
6
-
7
- Define an experience in code with `createExperience()`. You can also load an
8
- experience from the dashboard at runtime with the
9
- [Dynamic Experiences Plugin](./plugins/dynamic-experiences).
10
-
11
- The flow below points at a DOM selector and renders into it. A Page experience
12
- renders through a named slot instead. Point it at `.picked-slot-<name>` and render
13
- `<ExperienceSlot name="<name>" />` on your server. Use the slot path for a Page that
14
- the server renders and that search engines must read. Use the selector path for a
15
- Flow that the browser renders. See [Server-Side Rendering](/sdk/ssr) and
16
- [Slots and Placements](/sdk/ssr/slots-and-placements).
17
-
18
- ## Create an experience
19
-
20
- ```ts
21
- import { createExperience } from '@picked/client/plugins/create-experience'
22
- import { adaptSvelteComponent } from '@picked/svelte'
23
- import appCss from '../app.css?inline'
24
-
25
- export const productPageExperience = createExperience({
26
- id: 'product-page-experience',
27
- name: 'Product Page Experience',
28
- condition: {
29
- '==': [
30
- { var: 'location.pathname' },
31
- '/product/',
32
- ],
33
- },
34
- targets: [{ selector: 'body', placement: 'after' }],
35
- component: adaptSvelteComponent(
36
- import('../ui/ProductPageExperience.svelte'),
37
- { css: [appCss] },
38
- ),
39
- })
40
- ```
41
-
42
- To register an experience that you define in code, use the
43
- [Create Experience Plugin](./plugins/create-experience). To read an experience from
44
- the dashboard at runtime and control it there, use the
45
- [Dynamic Experiences Plugin](./plugins/dynamic-experiences).
46
-
47
- ## How experiences work
48
-
49
- After you register an experience, the SDK follows this flow:
50
-
51
- 1. **Registration**: You register the experience on the Picked client. Use
52
- `createExperiencePlugin` for an experience that you define in code. Use
53
- `dynamicExperiencesPlugin` for one that you control from the dashboard.
54
- 2. **Condition evaluation**: The SDK evaluates the `condition` of the experience with
55
- the logic engine. The result decides whether a visitor sees it.
56
- 3. **Target resolution**: The SDK searches the DOM for an element that matches a
57
- selector in `targets`. Examples are `body`, `.product-page`, and `#checkout`.
58
- 4. **Initialization**: The SDK starts the experience when the condition is `true` and
59
- at least one target element is there.
60
- 5. **Component rendering**: The SDK renders the component of the experience. It puts
61
- the component into the DOM at the target and the placement that you set. A
62
- placement is `before`, `after`, `append`, `prepend`, or `replace`.
63
- 6. **Flow execution**: An experience can hold steps, fields, and conditions. A user
64
- then moves through the flow. The flow checks each answer and takes a branch.
@@ -1,161 +0,0 @@
1
- ---
2
- layout: docs
3
- ---
4
-
5
- # Allocation and exposure
6
-
7
- A read of the bucket of a feature flag is what allocates a visitor to that feature flag. This
8
- page covers when that happens and how to watch it. It also covers how to be sure that
9
- Picked allocates a visitor before you report on that visitor.
10
-
11
- ## A registration is not a decision
12
-
13
- The SDK loads every feature flag in your project at startup, but it allocates no visitor
14
- then. A feature flag stays undecided until something reads its bucket. The first read runs the
15
- `decide` function, stores the result, and reports the decision.
16
-
17
- You therefore choose when Picked commits a visitor to a feature flag. You choose it by the
18
- point at which you read the feature flag.
19
-
20
- ## A read of a bucket records an exposure
21
-
22
- The first read is the decision, so a read of a bucket is the moment when the visitor
23
- enters the test. If you read every feature flag at startup, every visitor counts as exposed
24
- to every test. This includes a test that the visitor never reached.
25
-
26
- :::warning
27
- Do not loop over every feature flag at startup to read its bucket. That commits every visitor
28
- to every test immediately. You then have no way to wait for the data that a decision
29
- needs.
30
- :::
31
-
32
- Read a feature flag at the point where the decision matters. That is after the cart loads,
33
- after the quote returns, or after you know who the visitor is.
34
-
35
- ## Check a feature flag without a decision
36
-
37
- `client.features.listStore` holds every registered feature flag. Each entry reports whether
38
- the feature flag is decided yet, so you can check a feature flag and commit nobody:
39
-
40
- ```ts
41
- for (const feature flag of client.features.listStore.getState()) {
42
- console.log(feature flag.id, feature flag.isResolved)
43
- }
44
- ```
45
-
46
- `getBucket()` behaves differently for each state:
47
-
48
- - **Resolved** — it returns `{ id, bucket }` at once. The read changes nothing.
49
- - **Not resolved** — it returns a promise. An `await` runs `decide` and commits the
50
- visitor.
51
-
52
- `await` works for both states, so you can treat them the same way when you do intend
53
- to decide.
54
-
55
- ## When `feature_decide` fires
56
-
57
- The SDK sends a `feature_decide` event the first time that it decides a feature flag for a
58
- visitor, and only then.
59
-
60
- :::warning
61
- On a later page load the SDK restores the decision from browser storage, and it
62
- sends **no event**. A visitor who returns produces no `feature_decide` event at all,
63
- although Picked still allocates that visitor.
64
-
65
- An integration that reads only `feature_decide` therefore reports nothing for a
66
- visitor who returns.
67
- :::
68
-
69
- Use the correct source for your question:
70
-
71
- | Question | Use |
72
- | --- | --- |
73
- | What is this visitor allocated to now? | `client.features.listStore` |
74
- | Did the SDK make a new decision? | `feature_decide` on `client.eventBus` |
75
- | Which decisions happened before I subscribed? | `client.events` |
76
-
77
- ## Watch for decisions
78
-
79
- `listStore` updates each time that a feature flag resolves. It does this whether the SDK
80
- restored the feature flag from storage or decided it now. A watch on `listStore` therefore
81
- always gives you the current state:
82
-
83
- ```ts
84
- const buckets = new Map<string, string | boolean>()
85
-
86
- client.features.listStore.watch((feature flags) => {
87
- for (const feature flag of feature flags) {
88
- if (!feature flag.isResolved) continue
89
- buckets.set(feature flag.id, feature flag.getBucket().bucket)
90
- }
91
- })
92
- ```
93
-
94
- Use `feature_decide` when you need to know that the SDK *made* a decision, and not
95
- what the current value is. An example is your own exposure event:
96
-
97
- ```ts
98
- client.eventBus.watch((event) => {
99
- if (event.type !== 'feature_decide') return
100
- analytics.track('Experiment exposure', {
101
- feature flag: event.feature.id,
102
- bucket: event.feature.bucket,
103
- })
104
- })
105
- ```
106
-
107
- An earlier event is still available when you subscribe after startup. `client.events`
108
- holds every event since the client started:
109
-
110
- ```ts
111
- const earlier = client.events
112
- .getState()
113
- .filter((entry) => entry.payload.type === 'feature_decide')
114
- ```
115
-
116
- ## Decide before you report
117
-
118
- To be sure that the SDK decides a feature flag before a later event fires, `await` the feature flag
119
- first:
120
-
121
- ```ts
122
- const feature flag = client.features.getById('quote-experiment').getState()
123
-
124
- if (feature flag) {
125
- const { bucket } = await feature flag.getBucket()
126
- trackViewQuote({ experiment: bucket })
127
- }
128
- ```
129
-
130
- An `await` on `getBucket()` resolves the feature flag when it is undecided, and returns the
131
- stored value when it is decided. In both cases the bucket is known before the next
132
- line runs.
133
-
134
- ## What Picked reports for you
135
-
136
- Read this before you build a report of your own.
137
-
138
- Every Picked analytics event carries a `feature_list` context. The context describes
139
- each feature flag that this visitor resolved:
140
-
141
- | Field | Description |
142
- | --- | --- |
143
- | `ab_feature` | The feature flag ID |
144
- | `ab_name` | The feature flag name |
145
- | `ab_key` | The version key, such as `v3` |
146
- | `ab_bucket` | The resolved bucket |
147
-
148
- Only a resolved feature flag appears there. The context therefore shows what the visitor was
149
- exposed to at the time when the event fired.
150
-
151
- The [Sentry plugin](https://www.npmjs.com/package/@picked/sentry) is optional. When
152
- it is installed, each decision also tags the Sentry scope as
153
- `picked.feature.<feature flag-id>`, with the bucket as the value. You can then filter your
154
- errors by the variant.
155
-
156
- ## Preview mode
157
-
158
- In [preview mode](/workspaces#preview) the SDK stores a decision in session storage
159
- and not in local storage. The decision does not stay after you close the tab, and
160
- Picked can allocate a preview visitor differently from a production visitor. Treat a
161
- preview allocation as disposable.
@@ -1,98 +0,0 @@
1
- ---
2
- layout: docs
3
- ---
4
-
5
- # Picked SDK
6
-
7
- The Picked SDK lets you add a dynamic, personalized experience to your web
8
- application with very little code.
9
-
10
- :::tip
11
- The SDK is framework-agnostic. It works with React, Svelte, Vue, and plain
12
- JavaScript.
13
- :::
14
-
15
- ## Quick start
16
-
17
- :::code-group
18
-
19
- ```typescript [Browser]
20
- import { createClient } from '@picked/client'
21
- import { windowObjectPlugin } from '@picked/client/plugins/window-object'
22
- import { singlePageAppPlugin } from '@picked/client/plugins/single-page-app'
23
-
24
- const picked = createClient({
25
- projectId: 'your-project-id',
26
- plugins: [windowObjectPlugin(), singlePageAppPlugin()],
27
- })
28
-
29
- picked.init()
30
- ```
31
-
32
- ```ts [Server]
33
- import { createClient } from '@picked/client'
34
- import { serialize } from '@picked/client/server'
35
-
36
- const client = createClient({ projectId: 'your-project-id' })
37
-
38
- const snapshot = await serialize(client, {
39
- projectConfig,
40
- attributes: { location: { pathname: '/promo' } },
41
- })
42
- ```
43
-
44
- :::
45
-
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).
49
-
50
- :::tip
51
- In a browser, you can reach the Picked client from the console with `window.Picked`.
52
- :::
53
-
54
- ## Precondition and condition
55
-
56
- | Category | Question | Description |
57
- | ------------ | ---------------------- | --------------------------------------------------------------------------------------------- |
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. |
60
-
61
- ```ts
62
- const picked = createClient({
63
- precondition: {
64
- and: [
65
- {
66
- or: [
67
- {
68
- invoke: {
69
- var: 'features.aa-test',
70
- },
71
- },
72
- true,
73
- ],
74
- },
75
- { invoke: { var: 'predicates.shopify-is-product-page' } },
76
- ],
77
- },
78
- condition: {
79
- invoke: {
80
- var: 'features.convincely-enabled',
81
- },
82
- },
83
- // The rest of the Picked options go here.
84
- })
85
- ```
86
-
87
- :::warning
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.
96
- :::
97
-
98
- See [conditions](./conditions.md) for more detail.
@@ -1,84 +0,0 @@
1
- ---
2
- layout: docs
3
- ---
4
-
5
- # Dynamic Experiences Plugin
6
-
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.
10
-
11
- ## Set the plugin up
12
-
13
- :::code-group
14
-
15
- ```ts [app.ts]
16
- import { createClient } from '@picked/client'
17
- import { dynamicExperiencesPlugin } from '@picked/client/plugins/dynamic-experiences'
18
-
19
- const client = createClient({
20
- projectId: 'your-project-id',
21
- plugins: [dynamicExperiencesPlugin()],
22
- })
23
-
24
- client.start()
25
- ```
26
-
27
- :::
28
-
29
- :::tip
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).
33
- :::
34
-
35
- ## How it works
36
-
37
- When the client starts, the plugin resolves the options of your project. It tries
38
- three sources, in this order:
39
-
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.
43
-
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.
48
-
49
- 3. **Runtime read** — in every other case, the plugin reads the options from the API
50
- when the client starts.
51
-
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.
56
-
57
- :::note
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.
61
- :::
62
-
63
- ## Feature flag allocation
64
-
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.
68
-
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.
72
-
73
- The plugin caches a feature flag decision in `localStorage` by default. In
74
- [preview mode](#preview-mode) it uses `sessionStorage`.
75
-
76
- ## Preview mode
77
-
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.
@@ -1,9 +0,0 @@
1
- ---
2
- layout: docs
3
- ---
4
-
5
- # Plugins
6
-
7
- [Create Experience](./create-experience.md) — Define experiences, steps, and fields in code.
8
-
9
- [Dynamic Experiences](./dynamic-experiences.md) — Fetch and register experiences from the Picked API at runtime.
@@ -1,128 +0,0 @@
1
- ---
2
- layout: docs
3
- ---
4
-
5
- # Predicates
6
-
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.
11
-
12
- ## Define a predicate
13
-
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:
16
-
17
- ```ts
18
- import { createClient } from '@picked/client'
19
-
20
- const client = createClient({
21
- projectId: 'your-project-id',
22
- predicates: {
23
- 'is-example-page': ({ location }) => {
24
- return location?.pathname === '/example'
25
- },
26
- 'get-account-tier': async () => {
27
- const data = await fetch('/api/get-account-tier').then((r) => r.json())
28
- return data.result // Returns "trial".
29
- },
30
- },
31
- })
32
- ```
33
-
34
- ## How predicates work
35
-
36
- After you register a predicate and a condition uses it, the SDK follows this flow:
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.
46
-
47
- ## Use a predicate in a condition
48
-
49
- Most predicates are used inside condition logic, through the `invoke` operator:
50
-
51
- ```ts
52
- const myExperience = createExperience({
53
- // ... other config
54
- condition: {
55
- and: [
56
- {
57
- '==': [
58
- {
59
- invoke: {
60
- var: 'predicates.is-example-page', // Points at a predicate that calls your endpoint.
61
- },
62
- },
63
- true, // Check that the return value is true.
64
- ],
65
- },
66
- {
67
- '==': [
68
- {
69
- invoke: {
70
- var: 'predicates.account-tier',
71
- },
72
- },
73
- '/trial', // The condition is true when the string matches.
74
- ],
75
- },
76
- ],
77
- },
78
- })
79
- ```
80
-
81
- :::tip
82
- You can also use a predicate in `validation` logic.
83
- :::
84
-
85
- ## Use the state snapshot
86
-
87
- Each predicate takes a `StateSnapshot` object with these properties:
88
-
89
- ```ts
90
- interface StateSnapshot {
91
- client: Picked // The Picked client.
92
- responses?: Array<{
93
- // Every response from the user.
94
- fieldId: string
95
- value: FieldValue | undefined
96
- }>
97
- args?: unknown[]
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.
103
- }
104
- ```
105
-
106
- :::warning
107
- In server-side rendering, and in any environment that is not a browser, `location`,
108
- `document`, `sessionStorage`, and `localStorage` are `undefined`.
109
- :::
110
-
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.
@@ -1,79 +0,0 @@
1
- ---
2
- layout: docs
3
- ---
4
-
5
- # Server-Side Rendering
6
-
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.
10
-
11
- ## Why server-side rendering
12
-
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.
18
-
19
- ## The flow
20
-
21
- ```
22
- projectConfig (Delivery API)
23
- -> serialize(client, params) on the server -> PickedSnapshot
24
- -> embed snapshot in HTML
25
- -> client seeds from snapshot -> renders the same markup -> hydrates
26
- ```
27
-
28
- 1. Fetch your published `projectConfig` from the [Delivery API](/delivery-api).
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).
32
- 3. Embed the snapshot in your HTML payload.
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).
36
-
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.
40
-
41
- ## React and Svelte side by side
42
-
43
- :::code-group
44
-
45
- ```tsx [React]
46
- import { PickedProvider, ExperienceSlot } from '@picked/react'
47
-
48
- <PickedProvider options={options} snapshot={snapshot}>
49
- <ExperienceSlot name="page" />
50
- </PickedProvider>
51
- ```
52
-
53
- ```svelte [Svelte]
54
- <script lang="ts">
55
- import { ExperienceSlot, setPickedContext } from '@picked/svelte'
56
-
57
- setPickedContext({ options, snapshot })
58
- </script>
59
-
60
- <ExperienceSlot name="page" />
61
- ```
62
-
63
- :::
64
-
65
- ## The server entry point
66
-
67
- The server helpers live in a dedicated entry point, so a browser bundle never holds
68
- them:
69
-
70
- ```ts
71
- import { serialize, registerProjectConfig } from '@picked/client/server'
72
- import type { PickedSnapshot } from '@picked/client/server'
73
- ```
74
-
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.