@delmaredigital/payload-experiments 0.3.0-rc.4 → 0.4.0-rc.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.
- package/README.md +122 -27
- package/dist/admin/MetricSourceSelect.js +3 -3
- package/dist/admin/PlacementStatus.d.ts +3 -0
- package/dist/admin/PlacementStatus.js +54 -0
- package/dist/admin/PreviewLinks.d.ts +2 -1
- package/dist/admin/PreviewLinks.js +2 -2
- package/dist/admin/TestsDashboardCards.d.ts +1 -0
- package/dist/admin/TestsDashboardCards.js +4 -8
- package/dist/admin/TestsNavLink.d.ts +1 -0
- package/dist/admin/TestsNavLink.js +4 -10
- package/dist/admin/actions/ConfirmDialog.d.ts +2 -1
- package/dist/admin/actions/ConfirmDialog.js +2 -2
- package/dist/admin/actions/DecideDialog.d.ts +2 -1
- package/dist/admin/actions/DecideDialog.js +2 -2
- package/dist/admin/api.d.ts +18 -2
- package/dist/admin/api.js +47 -2
- package/dist/admin/blocks/BlockPanel.d.ts +4 -0
- package/dist/admin/blocks/BlockPanel.js +199 -0
- package/dist/admin/blocks/TestBlockField.d.ts +5 -0
- package/dist/admin/blocks/TestBlockField.js +163 -0
- package/dist/admin/blocks/formApi.d.ts +46 -0
- package/dist/admin/blocks/formApi.js +159 -0
- package/dist/admin/blocks/inject.d.ts +7 -0
- package/dist/admin/blocks/inject.js +41 -0
- package/dist/admin/blocks/model.d.ts +17 -0
- package/dist/admin/blocks/model.js +45 -0
- package/dist/admin/blocks/statusCache.d.ts +9 -0
- package/dist/admin/blocks/statusCache.js +92 -0
- package/dist/admin/blocks/useBlockForm.d.ts +17 -0
- package/dist/admin/blocks/useBlockForm.js +46 -0
- package/dist/admin/copy/actions.d.ts +2 -0
- package/dist/admin/copy/actions.js +9 -0
- package/dist/admin/copy/blocks.d.ts +43 -0
- package/dist/admin/copy/blocks.js +64 -0
- package/dist/admin/copy/common.d.ts +1 -0
- package/dist/admin/copy/common.js +1 -1
- package/dist/admin/copy/goals.d.ts +2 -0
- package/dist/admin/copy/goals.js +4 -1
- package/dist/admin/copy/newTest.d.ts +10 -0
- package/dist/admin/copy/newTest.js +10 -0
- package/dist/admin/copy/pages.d.ts +24 -0
- package/dist/admin/copy/pages.js +24 -0
- package/dist/admin/copy/readiness.d.ts +23 -3
- package/dist/admin/copy/readiness.js +24 -3
- package/dist/admin/copy/results.d.ts +1 -0
- package/dist/admin/copy/results.js +1 -0
- package/dist/admin/copy/testPage.d.ts +4 -0
- package/dist/admin/copy/testPage.js +4 -0
- package/dist/admin/goals/XpGoalField.d.ts +4 -0
- package/dist/admin/goals/XpGoalField.js +168 -0
- package/dist/admin/goals/xpGoalField.css +48 -0
- package/dist/admin/metricSource.model.d.ts +2 -0
- package/dist/admin/metricSource.model.js +4 -0
- package/dist/admin/pageCollections.d.ts +10 -0
- package/dist/admin/pageCollections.js +20 -0
- package/dist/admin/paths.d.ts +9 -0
- package/dist/admin/paths.js +9 -0
- package/dist/admin/placementStatus.model.d.ts +2 -0
- package/dist/admin/placementStatus.model.js +19 -0
- package/dist/admin/placements/placementLabels.d.ts +11 -0
- package/dist/admin/placements/placementLabels.js +21 -0
- package/dist/admin/placements/placementLabels.model.d.ts +13 -0
- package/dist/admin/placements/placementLabels.model.js +12 -0
- package/dist/admin/placements/usePlacementLabels.d.ts +12 -0
- package/dist/admin/placements/usePlacementLabels.js +102 -0
- package/dist/admin/results/DataQualityPanel.js +4 -1
- package/dist/admin/results/results.model.d.ts +3 -0
- package/dist/admin/results/results.model.js +8 -4
- package/dist/admin/surface/TestsHome.js +39 -10
- package/dist/admin/surface/TestsSurface.js +7 -6
- package/dist/admin/surface/contracts.d.ts +6 -5
- package/dist/admin/surface/goals/GoalForm.js +71 -44
- package/dist/admin/surface/goals/GoalsList.js +5 -4
- package/dist/admin/surface/goals/data.js +4 -1
- package/dist/admin/surface/goals/goals.model.d.ts +1 -2
- package/dist/admin/surface/goals/goals.model.js +1 -3
- package/dist/admin/surface/home.model.d.ts +3 -3
- package/dist/admin/surface/home.model.js +8 -4
- package/dist/admin/surface/newTest/NewTest.js +56 -17
- package/dist/admin/surface/newTest/PageWizard.d.ts +7 -0
- package/dist/admin/surface/newTest/PageWizard.js +214 -0
- package/dist/admin/surface/pagePicker/CopyRoute.d.ts +6 -0
- package/dist/admin/surface/pagePicker/CopyRoute.js +53 -0
- package/dist/admin/surface/pagePicker/PageCombobox.d.ts +10 -0
- package/dist/admin/surface/pagePicker/PageCombobox.js +395 -0
- package/dist/admin/surface/pagePicker/pagePicker.css +10 -0
- package/dist/admin/surface/pagePicker/pagePicker.model.d.ts +27 -0
- package/dist/admin/surface/pagePicker/pagePicker.model.js +25 -0
- package/dist/admin/surface/readiness/ActionDialogs.js +99 -64
- package/dist/admin/surface/readiness/Banner.js +30 -6
- package/dist/admin/surface/readiness/readiness.model.d.ts +8 -1
- package/dist/admin/surface/readiness/readiness.model.js +92 -9
- package/dist/admin/surface/results/ReleasePages.d.ts +3 -1
- package/dist/admin/surface/results/ReleasePages.js +3 -7
- package/dist/admin/surface/results/ResultsBody.js +21 -2
- package/dist/admin/surface/results/ResultsHeader.js +5 -13
- package/dist/admin/surface/results/useResults.d.ts +20 -1
- package/dist/admin/surface/test/AdvancedSection.js +111 -104
- package/dist/admin/surface/test/GoalSection.js +94 -14
- package/dist/admin/surface/test/LengthSection.js +1 -1
- package/dist/admin/surface/test/PageHoldout.d.ts +3 -0
- package/dist/admin/surface/test/PageHoldout.js +67 -0
- package/dist/admin/surface/test/TestPage.js +5 -9
- package/dist/admin/surface/test/VersionsSection.js +12 -5
- package/dist/admin/surface/test/fields.d.ts +13 -4
- package/dist/admin/surface/test/fields.js +46 -14
- package/dist/admin/surface/test/jumpToSection.d.ts +1 -1
- package/dist/admin/surface/test/jumpToSection.js +4 -3
- package/dist/admin/surface/test/pageParts.d.ts +4 -1
- package/dist/admin/surface/test/pageParts.js +49 -1
- package/dist/admin/surface/test/testPage.css +3 -1
- package/dist/admin/surface/testDocument.model.d.ts +1 -0
- package/dist/admin/surface/testDocument.model.js +2 -1
- package/dist/admin/surface/useTestDocument.d.ts +15 -2
- package/dist/admin/surface/useTestDocument.js +53 -15
- package/dist/admin/surfaceUrls.d.ts +7 -0
- package/dist/admin/surfaceUrls.js +19 -0
- package/dist/admin/theme.css +2 -0
- package/dist/admin/useApi.d.ts +21 -2
- package/dist/admin/useApi.js +3 -5
- package/dist/admin/views/CollectionListRedirect.d.ts +1 -0
- package/dist/admin/views/CollectionListRedirect.js +1 -1
- package/dist/admin/views/ExperimentsRootView.js +28 -133
- package/dist/admin/views/GoalEditRedirect.d.ts +4 -0
- package/dist/admin/views/GoalEditRedirect.js +14 -0
- package/dist/admin/views/GoalsCollectionListView.d.ts +2 -0
- package/dist/admin/views/GoalsCollectionListView.js +4 -0
- package/dist/admin/views/TestsCollectionListView.d.ts +2 -0
- package/dist/admin/views/TestsCollectionListView.js +4 -0
- package/dist/admin/views/TestsNavLinkServer.d.ts +1 -0
- package/dist/admin/views/TestsNavLinkServer.js +1 -1
- package/dist/admin/views/buildSurfaceProps.d.ts +8 -0
- package/dist/admin/views/buildSurfaceProps.js +143 -0
- package/dist/admin/views/renderCollectionList.d.ts +2 -0
- package/dist/admin/views/renderCollectionList.js +34 -0
- package/dist/admin/views/renderTestsSurface.d.ts +3 -0
- package/dist/admin/views/renderTestsSurface.js +34 -0
- package/dist/blocks/bindings.d.ts +3 -0
- package/dist/blocks/bindings.js +34 -0
- package/dist/blocks/createExperimentBlock.d.ts +6 -0
- package/dist/blocks/createExperimentBlock.js +77 -0
- package/dist/blocks/formState.d.ts +31 -0
- package/dist/blocks/formState.js +146 -0
- package/dist/blocks/model.d.ts +41 -0
- package/dist/blocks/model.js +34 -0
- package/dist/client/ExperimentGroup.d.ts +6 -0
- package/dist/client/ExperimentGroup.js +9 -0
- package/dist/client/ExperimentsProvider.js +20 -4
- package/dist/client/clickGoals.d.ts +4 -0
- package/dist/client/clickGoals.js +43 -0
- package/dist/collections/experiments/config.js +21 -7
- package/dist/collections/experiments/deleteGuard.d.ts +2 -1
- package/dist/collections/experiments/deleteGuard.js +12 -1
- package/dist/collections/experiments/editability.js +4 -2
- package/dist/collections/experiments/pickSlug.d.ts +2 -0
- package/dist/collections/experiments/pickSlug.js +8 -0
- package/dist/collections/labels.d.ts +3 -0
- package/dist/collections/labels.js +20 -0
- package/dist/collections/metrics.js +28 -6
- package/dist/collections/phases.js +1 -0
- package/dist/collections/serialize.js +7 -0
- package/dist/db/clickGoals.d.ts +9 -0
- package/dist/db/clickGoals.js +22 -0
- package/dist/db/delete.d.ts +1 -0
- package/dist/db/delete.js +1 -0
- package/dist/db/locks.d.ts +8 -0
- package/dist/db/locks.js +27 -4
- package/dist/db/names.d.ts +4 -0
- package/dist/db/names.js +4 -0
- package/dist/db/pageBindings.d.ts +4 -0
- package/dist/db/pageBindings.js +17 -0
- package/dist/db/pageBusy.d.ts +2 -0
- package/dist/db/pageBusy.js +10 -0
- package/dist/db/pageContent.d.ts +10 -0
- package/dist/db/pageContent.js +79 -0
- package/dist/db/placements.d.ts +33 -0
- package/dist/db/placements.js +54 -0
- package/dist/db/slugs.d.ts +8 -0
- package/dist/db/slugs.js +40 -0
- package/dist/db/snapshots.d.ts +6 -0
- package/dist/db/snapshots.js +8 -5
- package/dist/endpoints/clickKeys.d.ts +5 -0
- package/dist/endpoints/clickKeys.js +17 -0
- package/dist/endpoints/events.js +4 -1
- package/dist/endpoints/index.js +28 -0
- package/dist/endpoints/pages.d.ts +5 -0
- package/dist/endpoints/pages.js +120 -0
- package/dist/endpoints/placementStatus.d.ts +6 -0
- package/dist/endpoints/placementStatus.js +238 -0
- package/dist/endpoints/readBody.d.ts +2 -0
- package/dist/endpoints/readBody.js +24 -0
- package/dist/endpoints/results.js +5 -1
- package/dist/endpoints/summary.js +11 -4
- package/dist/endpoints/wrapBlock.d.ts +6 -0
- package/dist/endpoints/wrapBlock.js +76 -0
- package/dist/errors.d.ts +3 -3
- package/dist/errors.js +4 -4
- package/dist/events/rows.d.ts +1 -1
- package/dist/events/rows.js +5 -3
- package/dist/exports/adminClient.d.ts +5 -0
- package/dist/exports/adminClient.js +5 -0
- package/dist/exports/adminRsc.d.ts +3 -0
- package/dist/exports/adminRsc.js +3 -0
- package/dist/exports/blocks.d.ts +2 -0
- package/dist/exports/blocks.js +1 -0
- package/dist/exports/client.d.ts +1 -0
- package/dist/exports/client.js +1 -0
- package/dist/exports/index.d.ts +6 -1
- package/dist/exports/index.js +3 -0
- package/dist/exports/puck.d.ts +3 -1
- package/dist/exports/puck.js +2 -0
- package/dist/exports/puckEditor.d.ts +3 -0
- package/dist/exports/puckEditor.js +2 -0
- package/dist/exports/rsc.d.ts +3 -0
- package/dist/exports/rsc.js +2 -0
- package/dist/fields/xpGoal.d.ts +3 -0
- package/dist/fields/xpGoal.js +16 -0
- package/dist/globals/state.js +10 -0
- package/dist/goals/cache.d.ts +3 -0
- package/dist/goals/cache.js +35 -0
- package/dist/goals/fairness.d.ts +40 -0
- package/dist/goals/fairness.js +200 -0
- package/dist/goals/key.d.ts +4 -0
- package/dist/goals/key.js +3 -0
- package/dist/goals/picker/clickGoalPicker.model.d.ts +29 -0
- package/dist/goals/picker/clickGoalPicker.model.js +45 -0
- package/dist/goals/picker/copy.d.ts +16 -0
- package/dist/goals/picker/copy.js +15 -0
- package/dist/goals/picker/useClickGoalPicker.d.ts +30 -0
- package/dist/goals/picker/useClickGoalPicker.js +220 -0
- package/dist/goals/validate.d.ts +3 -0
- package/dist/goals/validate.js +14 -0
- package/dist/hooks/pageGuard.d.ts +5 -1
- package/dist/hooks/pageGuard.js +157 -95
- package/dist/hooks/puckGoals.d.ts +3 -0
- package/dist/hooks/puckGoals.js +38 -0
- package/dist/onInit.js +29 -0
- package/dist/options.d.ts +2 -0
- package/dist/options.js +41 -1
- package/dist/plugin.js +50 -43
- package/dist/puck/copy.d.ts +1 -0
- package/dist/puck/copy.js +1 -0
- package/dist/puck/editor/editor.chrome.css +14 -0
- package/dist/puck/editor/editor.css +106 -0
- package/dist/puck/editor/editor.model.d.ts +4 -2
- package/dist/puck/editor/editor.model.js +6 -3
- package/dist/puck/editor/integration.d.ts +10 -0
- package/dist/puck/editor/integration.js +30 -0
- package/dist/puck/editor/xpGoalPuckEditorField.d.ts +7 -0
- package/dist/puck/editor/xpGoalPuckEditorField.js +142 -0
- package/dist/puck/integration.d.ts +6 -0
- package/dist/puck/integration.js +21 -0
- package/dist/puck/xpGoalField.d.ts +3 -0
- package/dist/puck/xpGoalField.js +6 -0
- package/dist/results/health.d.ts +4 -0
- package/dist/results/health.js +27 -0
- package/dist/results/snapshot.js +15 -2
- package/dist/sanitizeReadinessDetail.js +43 -0
- package/dist/server/pageKindSeoContract.d.ts +27 -0
- package/dist/server/pageKindSeoContract.js +80 -0
- package/dist/server/recordContentChange.js +58 -8
- package/dist/server/resolveExperimentBlocks.d.ts +5 -0
- package/dist/server/resolveExperimentBlocks.js +54 -0
- package/dist/server/runResults.js +56 -15
- package/dist/server/toClientAssignments.js +2 -1
- package/dist/transitions/addMetric.d.ts +2 -1
- package/dist/transitions/addMetric.js +28 -1
- package/dist/transitions/checkStart.d.ts +14 -1
- package/dist/transitions/checkStart.js +92 -8
- package/dist/transitions/decide.js +25 -0
- package/dist/transitions/deleteExperiment.d.ts +3 -1
- package/dist/transitions/deleteExperiment.js +27 -1
- package/dist/transitions/kernel.js +4 -0
- package/dist/transitions/makeContent.d.ts +5 -0
- package/dist/transitions/makeContent.js +39 -11
- package/dist/transitions/pageCollection.d.ts +10 -0
- package/dist/transitions/pageCollection.js +20 -0
- package/dist/transitions/pageVersions.d.ts +10 -0
- package/dist/transitions/pageVersions.js +44 -0
- package/dist/transitions/phaseRecord.d.ts +1 -0
- package/dist/transitions/placementInteractions.d.ts +40 -0
- package/dist/transitions/placementInteractions.js +94 -0
- package/dist/transitions/placements.d.ts +8 -0
- package/dist/transitions/placements.js +148 -1
- package/dist/transitions/preflightData.d.ts +2 -2
- package/dist/transitions/preflightData.js +137 -5
- package/dist/transitions/resolveCaps.d.ts +1 -0
- package/dist/transitions/resolveCaps.js +6 -2
- package/dist/transitions/start.js +46 -1
- package/dist/types/admin.d.ts +1 -1
- package/dist/types/assignments.d.ts +1 -0
- package/dist/types/contracts.d.ts +15 -0
- package/dist/types/endpoints.d.ts +92 -5
- package/dist/types/index.d.ts +3 -0
- package/dist/types/puck.d.ts +7 -1
- package/dist/types/snapshot.d.ts +17 -4
- package/dist/types/surface.d.ts +13 -0
- package/dist/ui/ui.css +4 -0
- package/package.json +8 -1
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# @delmaredigital/payload-experiments
|
|
2
2
|
|
|
3
|
+
> ⚠️ Upgrading to 0.4? Run `payload migrate:create` for four nullable plugin columns (`xp_experiments.page_collection` and its `_xp_experiments_v` counterpart, `xp_phases.placement_docs`, `xp_state.retired_slugs`), plus the Experiment block and `xpGoal` columns if you adopt them, then `payload generate:importmap`. Saves on pages, blocks and Puck collections can now be refused while a test runs on them (`page-busy`, `interacting-tests`); Puck tests whose slot is only on a draft can't start until the page is published; host `metricSources` ids may not start with `click:`; `admin.editorUrl` now receives a required `editor` argument. See the CHANGELOG's BREAKING section.
|
|
4
|
+
|
|
3
5
|
> ⚠️ Upgrading to 0.3? For 0.3.0-rc.1, every `linkIdentity` call must supply `verified`; it now returns an outcome. Run `payload migrate:create` for the nullable `xp_identities.verified_at` column and `user_id` index, plus the release-pages columns listed in the CHANGELOG (`decision_pages_released_at` / `decision_pages_released_by` on `xp_experiments` and their `version_` counterparts on `_xp_experiments_v`). Existing identity links become unverified; see the CHANGELOG for optional backfill.
|
|
4
6
|
|
|
5
7
|
> ⚠️ Upgrading to 0.2? After upgrading, run `payload generate:importmap`: the Tests surface is registered through admin.dependencies, and without it the Tests routes show a 'not installed' message instead of the Tests screen. In 0.2.0-rc.2, the Tests server views moved from `/rsc` to `/admin/rsc`, so the import map must be regenerated when upgrading from rc.1 too. The old experiments edit-form components are gone from `/admin/client`, and the `xp-experiments` admin list and edit views now redirect to the Tests surface.
|
|
@@ -426,6 +428,7 @@ schema. Optional callbacks integrate host-owned content:
|
|
|
426
428
|
|
|
427
429
|
A `MetricSource`, registered in `metricSources`, has `id`, `label`,
|
|
428
430
|
`kind: 'binomial' | 'count' | 'revenue'`, and `keyedBy: 'visitor' | 'user'`.
|
|
431
|
+
Source ids starting with `click:` are reserved for click goals and refused at boot.
|
|
429
432
|
`clientWritable` optionally permits client conversions (revenue is always
|
|
430
433
|
server-only). `query({ from, to, unitIds? })` returns a promise of
|
|
431
434
|
`[{ unitId, value, ts }]`, **one row per event**, with `ts` a `Date`; use the
|
|
@@ -456,6 +459,21 @@ are recorded with `trackConversion`. Beacon conversions for a non-writable name
|
|
|
456
459
|
are dropped silently. Only listed names become `events:<name>` sources. They
|
|
457
460
|
support visitor and user units and do not represent counts or revenue in v1.
|
|
458
461
|
|
|
462
|
+
### Testing page SEO in the host
|
|
463
|
+
|
|
464
|
+
`pageKindSeoContract({ secret, original, versions, state, winner? })` from `/rsc`
|
|
465
|
+
returns `{ manifest, rows }`. States are `running`, `decided` and `archived`;
|
|
466
|
+
provide a winner path for the latter two. Build the host's test runtime with
|
|
467
|
+
`loadManifest: async () => manifest` and the same secret. For each row, set
|
|
468
|
+
`next/headers` to its signed `headers`, render metadata for `servedPath`, and
|
|
469
|
+
assert `expect.robots`, `expect.canonical` and `expect.inSitemap` against the
|
|
470
|
+
host's metadata and sitemap functions. This includes the Original served as
|
|
471
|
+
each version, including the homepage.
|
|
472
|
+
|
|
473
|
+
A host-level test mocks four boundaries: `next/headers`, the page loader,
|
|
474
|
+
the runtime and the site URL. Set `metadataBase` so relative canonical paths
|
|
475
|
+
resolve to your site URL.
|
|
476
|
+
|
|
459
477
|
### Whole-page options
|
|
460
478
|
|
|
461
479
|
| Option | Purpose |
|
|
@@ -489,7 +507,18 @@ pages: {
|
|
|
489
507
|
Adapt both callbacks for the site's homepage path. These checks run before Start;
|
|
490
508
|
they do not protect pages from later deletion, unpublishing or renaming. Keep an
|
|
491
509
|
archived winner published at the same address: archived test URLs still point to it.
|
|
492
|
-
|
|
510
|
+
Whole-page registrations also install the sidebar test status and content-change hooks.
|
|
511
|
+
For 0.4.0, run `payload migrate:create` for `xp_state.retired_slugs`,
|
|
512
|
+
`xp_experiments.page_collection` and its version column, then
|
|
513
|
+
`payload generate:importmap`.
|
|
514
|
+
|
|
515
|
+
`contentOf(doc)` optionally selects the content your site renders (for example,
|
|
516
|
+
`doc.layout`). Published copies are compared with nested `id` and `blockName`
|
|
517
|
+
keys removed. Without it, content checks and page content-change comparisons
|
|
518
|
+
are skipped. `copyHint` (up to 200 characters), `copyUrl` (a root-relative path)
|
|
519
|
+
and `copyLabel` (up to 40 characters, requires `copyUrl`) customize the guidance
|
|
520
|
+
for making version pages. The default copy link opens the registered collection's
|
|
521
|
+
list under the configured admin route.
|
|
493
522
|
|
|
494
523
|
### `admin` options
|
|
495
524
|
|
|
@@ -504,7 +533,7 @@ For example, pass:
|
|
|
504
533
|
|
|
505
534
|
```ts
|
|
506
535
|
admin: {
|
|
507
|
-
editorUrl: ({ collection, id, draft }) =>
|
|
536
|
+
editorUrl: ({ collection, id, draft, editor, doc }) =>
|
|
508
537
|
`/admin/puck-editor/${encodeURIComponent(collection)}/${encodeURIComponent(String(id))}`,
|
|
509
538
|
previewUrl: async ({ collection, slug }) => {
|
|
510
539
|
if (collection !== 'pages' || slug === undefined) return null
|
|
@@ -570,7 +599,10 @@ separately. Only manual, viewport and pageview exposure policies are accepted.
|
|
|
570
599
|
Conversions require a client-writable source, never revenue; client values are
|
|
571
600
|
stored as `1`, attributes are allow-listed, and the host's `events.rateLimit`
|
|
572
601
|
adapter runs when supplied. Accepted and dropped events both return `202 {}`;
|
|
573
|
-
malformed requests can return errors.
|
|
602
|
+
malformed requests can return errors. `click:<key>` conversions are also accepted
|
|
603
|
+
for defined click-goal keys matching `[a-z0-9][a-z0-9_-]{0,63}`; undefined or
|
|
604
|
+
invalid keys are dropped with `202`. Clicks carry no attributes, and defined
|
|
605
|
+
keys may be cached for up to 30 seconds.
|
|
574
606
|
|
|
575
607
|
**The real bound on slow or method-override bodies is the host firewall/platform
|
|
576
608
|
timeout.** A stream under the byte limit can remain open, and Payload can read
|
|
@@ -588,13 +620,15 @@ for these patterns.
|
|
|
588
620
|
|
|
589
621
|
## Admin and client
|
|
590
622
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
623
|
+
**Tests** is a regular navigation group for managers only, with **All tests**
|
|
624
|
+
and **Goals**. It is the first named group, after Payload's default
|
|
625
|
+
Collections/Globals groups when the host has ungrouped entities. Payload's own
|
|
626
|
+
dashboard shows the Tests cards. A host collection with `group: 'Tests'` joins
|
|
627
|
+
the plugin's group.
|
|
628
|
+
|
|
629
|
+
After upgrading, run `payload generate:importmap` to register the two list views
|
|
630
|
+
and the Goals edit redirect. Without regeneration, both list URLs and the Goal
|
|
631
|
+
create/edit URLs show Payload's raw views.
|
|
598
632
|
|
|
599
633
|
### Client assignments and exposure
|
|
600
634
|
|
|
@@ -647,32 +681,61 @@ configured to allow client writes; record revenue on the server.
|
|
|
647
681
|
|
|
648
682
|
### Puck render and editor configs
|
|
649
683
|
|
|
650
|
-
|
|
651
|
-
|
|
684
|
+
Works with: payload-puck ≥0.10.3 (experiments ≥0.4.0).
|
|
685
|
+
|
|
686
|
+
For payload-puck hosts, add one integration object on each side. In the render
|
|
687
|
+
config, pass `integrations` to `extendConfig`:
|
|
652
688
|
|
|
653
689
|
```ts
|
|
654
|
-
import {
|
|
655
|
-
import {
|
|
690
|
+
import { extendConfig } from '@delmaredigital/payload-puck/config'
|
|
691
|
+
import { experimentsPuck } from '@delmaredigital/payload-experiments/puck'
|
|
692
|
+
import { baseConfig } from './baseConfig'
|
|
656
693
|
|
|
657
|
-
const
|
|
658
|
-
|
|
694
|
+
export const renderConfig = extendConfig({
|
|
695
|
+
base: baseConfig,
|
|
696
|
+
integrations: [experimentsPuck()],
|
|
697
|
+
})
|
|
698
|
+
```
|
|
659
699
|
|
|
660
|
-
|
|
661
|
-
|
|
700
|
+
Call `experimentsPuckEditor()` once at module scope in a `'use client'` module,
|
|
701
|
+
then pass that array to `PuckConfigProvider`. A new object per render rebuilds
|
|
702
|
+
Puck's config and plugin list.
|
|
662
703
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
```
|
|
704
|
+
```tsx
|
|
705
|
+
'use client'
|
|
666
706
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
in an unused slot stays visible in the outline and gets a warning in the block.
|
|
707
|
+
import { PuckConfigProvider } from '@delmaredigital/payload-puck/client'
|
|
708
|
+
import { experimentsPuckEditor } from '@delmaredigital/payload-experiments/puck/editor'
|
|
709
|
+
import { editorConfig } from './editorConfig'
|
|
671
710
|
|
|
672
|
-
|
|
673
|
-
|
|
711
|
+
const integrations = [experimentsPuckEditor()]
|
|
712
|
+
|
|
713
|
+
export function PuckProvider({ children }: { children: React.ReactNode }) {
|
|
714
|
+
return <PuckConfigProvider config={editorConfig} integrations={integrations}>
|
|
715
|
+
{children}
|
|
716
|
+
</PuckConfigProvider>
|
|
717
|
+
}
|
|
674
718
|
```
|
|
675
719
|
|
|
720
|
+
Each object includes the test block and its **A/B tests** sidebar category. The
|
|
721
|
+
editor object also includes the outline plugin: it hides empty version slots
|
|
722
|
+
that the block's test doesn't use, while content in unused slots stays visible
|
|
723
|
+
with a warning. Both helpers accept `{ routes }`; the editor also accepts
|
|
724
|
+
`accent`. Use the host's Payload routes and the plugin's `theme.accent`.
|
|
725
|
+
|
|
726
|
+
#### Wiring Puck by hand
|
|
727
|
+
|
|
728
|
+
The separate pieces stay exported:
|
|
729
|
+
|
|
730
|
+
- `/puck`: `ExperimentSlot`, `createExperimentSlot({ routes })`,
|
|
731
|
+
`EXPERIMENT_SLOT_TYPE` and `createExperimentSlotField({ routes })`.
|
|
732
|
+
- `/puck/editor`: `ExperimentSlotEditor`,
|
|
733
|
+
`createExperimentSlotEditor({ routes, accent })` and `experimentOutlinePlugin()`.
|
|
734
|
+
|
|
735
|
+
Register the render and editor blocks under the same `EXPERIMENT_SLOT_TYPE` key (import it from `/puck`),
|
|
736
|
+
add a sidebar category listing it, and add `experimentOutlinePlugin()` to Puck's
|
|
737
|
+
`plugins`. It composes with the host's outline override.
|
|
738
|
+
|
|
676
739
|
Routes default to Payload's `/admin` and `/api` when omitted. The existing
|
|
677
740
|
`ExperimentSlot` and `ExperimentSlotEditor` exports remain available with these
|
|
678
741
|
defaults. Hosts on non-default routes switch to the factories. The standalone
|
|
@@ -815,3 +878,35 @@ The WCAG 2.x gate requires at least 4.5:1 **foreground text on the accent fill**
|
|
|
815
878
|
in each theme (the accent is used as a fill, not as page text). A failing
|
|
816
879
|
colour is refused with its ratio. Status chips retain Payload semantic colours.
|
|
817
880
|
`deriveAccent` is also exported from `/puck/editor` for editor integrations.
|
|
881
|
+
|
|
882
|
+
## Click goals
|
|
883
|
+
|
|
884
|
+
Click goals use a goal's short name as their key (`click:<short-name>`). Add
|
|
885
|
+
`xpGoalField()` from the root entry to a host block or array row. Its manager
|
|
886
|
+
picker includes inline creation with "+ New click goal"; run
|
|
887
|
+
`payload generate:importmap` to register the picker. For Puck's `xpGoal` prop,
|
|
888
|
+
use `xpGoalPuckEditorField()` from `/puck/editor` in your editor config and
|
|
889
|
+
`xpGoalPuckField()` from `/puck` in your render config. Run the
|
|
890
|
+
host's `payload migrate:create`: each table using the Payload field (and its
|
|
891
|
+
version table) gains a nullable text column. Render the value on the actual
|
|
892
|
+
interactive element as `data-xp-goal={xpGoal}`; code can also set
|
|
893
|
+
`data-xp-goal="join"` directly. Define a click goal with that short name first.
|
|
894
|
+
For managers, an undefined key blocks the save through the Payload field's
|
|
895
|
+
server validation, refreshing a cached miss immediately.
|
|
896
|
+
Draft saves are also refused when the collection validates drafts
|
|
897
|
+
(`versions.drafts.validate`). Editors without goal-management access see a
|
|
898
|
+
plain text input. Goals in
|
|
899
|
+
related documents and keys placed only in code are invisible to the content
|
|
900
|
+
fairness check.
|
|
901
|
+
|
|
902
|
+
`ExperimentsProvider` records native clicks when its server assignments arm
|
|
903
|
+
click goals. Nested providers share one document listener; the deepest armed
|
|
904
|
+
provider supplies the route and pageview id. Enter on links/buttons and Space
|
|
905
|
+
on buttons count through the browser's native click. A focusable `div` needs
|
|
906
|
+
`role="button"` and the host's own keyboard handler. Beacons use `sendBeacon`
|
|
907
|
+
(with a keepalive fallback) so navigation/unload does not discard the click.
|
|
908
|
+
Click and exposure beacons share `events.rateLimit`; size the adapter for both.
|
|
909
|
+
New click goals are accepted within 30 seconds. A click goal may be primary;
|
|
910
|
+
primary and guardrail goals must appear in every tested version or outside all
|
|
911
|
+
of them. Secondary asymmetry warns, and results health checks published rows
|
|
912
|
+
again on every run.
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { SelectInput, useField } from '@payloadcms/ui';
|
|
4
|
-
import { metricSourceHint, metricSourceOptions } from './metricSource.model.js';
|
|
4
|
+
import { metricSourceHint, metricSourceOptions, sourceFor } from './metricSource.model.js';
|
|
5
5
|
import { METRICS_COPY } from './copy/metrics.js';
|
|
6
6
|
export function MetricSourceSelect({ path, field, readOnly }) {
|
|
7
7
|
const { value, setValue } = useField({
|
|
8
8
|
path
|
|
9
9
|
});
|
|
10
10
|
const sources = field.admin?.custom?.sources ?? [];
|
|
11
|
-
const hint = metricSourceHint(
|
|
11
|
+
const hint = metricSourceHint(sourceFor(value, sources));
|
|
12
12
|
const locked = Boolean(readOnly || field.admin?.readOnly);
|
|
13
13
|
return /*#__PURE__*/ _jsxs("div", {
|
|
14
14
|
children: [
|
|
@@ -19,7 +19,7 @@ export function MetricSourceSelect({ path, field, readOnly }) {
|
|
|
19
19
|
description: field.admin?.description,
|
|
20
20
|
required: field.required,
|
|
21
21
|
options: metricSourceOptions(sources),
|
|
22
|
-
value: value ?? '',
|
|
22
|
+
value: sourceFor(value, sources)?.id ?? value ?? '',
|
|
23
23
|
readOnly: locked,
|
|
24
24
|
onChange: (option)=>{
|
|
25
25
|
if (!locked) setValue(option && !Array.isArray(option) ? option.value : null);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useConfig, useDocumentInfo } from '@payloadcms/ui';
|
|
4
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
5
|
+
import { formatAdminURL } from 'payload/shared';
|
|
6
|
+
import { createApi } from './api.js';
|
|
7
|
+
import { pageStatusCopy } from './placementStatus.model.js';
|
|
8
|
+
/** Manage-only endpoint: access-filtered editors see no status or error text. */ export function PlacementStatus(_props) {
|
|
9
|
+
const { id, collectionSlug } = useDocumentInfo();
|
|
10
|
+
const { config } = useConfig();
|
|
11
|
+
const api = useMemo(()=>createApi(config.routes.api), [
|
|
12
|
+
config.routes.api
|
|
13
|
+
]);
|
|
14
|
+
const [tests, setTests] = useState([]);
|
|
15
|
+
useEffect(()=>{
|
|
16
|
+
let active = true;
|
|
17
|
+
setTests([]);
|
|
18
|
+
if (id != null && collectionSlug) void api.placementStatus({
|
|
19
|
+
docs: [
|
|
20
|
+
{
|
|
21
|
+
collection: collectionSlug,
|
|
22
|
+
id
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}).then((result)=>{
|
|
26
|
+
if (active && result.ok) setTests(result.data?.docs[`${collectionSlug}:${id}`]?.tests ?? []);
|
|
27
|
+
});
|
|
28
|
+
return ()=>{
|
|
29
|
+
active = false;
|
|
30
|
+
};
|
|
31
|
+
}, [
|
|
32
|
+
api,
|
|
33
|
+
collectionSlug,
|
|
34
|
+
id
|
|
35
|
+
]);
|
|
36
|
+
return /*#__PURE__*/ _jsx(_Fragment, {
|
|
37
|
+
children: tests.map((test)=>{
|
|
38
|
+
const copy = pageStatusCopy(test);
|
|
39
|
+
return copy ? /*#__PURE__*/ _jsxs("p", {
|
|
40
|
+
children: [
|
|
41
|
+
copy,
|
|
42
|
+
' ',
|
|
43
|
+
/*#__PURE__*/ _jsx("a", {
|
|
44
|
+
href: formatAdminURL({
|
|
45
|
+
adminRoute: config.routes.admin,
|
|
46
|
+
path: `/experiments/by-slug/${encodeURIComponent(test.slug)}`
|
|
47
|
+
}),
|
|
48
|
+
children: "Back to the test"
|
|
49
|
+
})
|
|
50
|
+
]
|
|
51
|
+
}, test.slug) : null;
|
|
52
|
+
})
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { DocId } from '../types/contracts.js';
|
|
2
2
|
import type { PreflightResult } from '../types/endpoints.js';
|
|
3
|
-
export declare function PreviewLinks({ experimentId, placements, arms, flagPoint }: {
|
|
3
|
+
export declare function PreviewLinks({ experimentId, placements, arms, flagPoint, apiRoute }: {
|
|
4
|
+
apiRoute: string;
|
|
4
5
|
flagPoint?: string;
|
|
5
6
|
experimentId: DocId;
|
|
6
7
|
placements: PreflightResult['placements'];
|
|
@@ -8,8 +8,8 @@ import { REVIEW_COPY as C } from './copy/review.js';
|
|
|
8
8
|
import { TEST_PAGE } from './copy/testPage.js';
|
|
9
9
|
import { API_MESSAGES, refusalMessage } from './copy/common.js';
|
|
10
10
|
import { actionablePreviewPages } from './previewLinks.model.js';
|
|
11
|
-
export function PreviewLinks({ experimentId, placements, arms, flagPoint }) {
|
|
12
|
-
const api = useApi();
|
|
11
|
+
export function PreviewLinks({ experimentId, placements, arms, flagPoint, apiRoute }) {
|
|
12
|
+
const api = useApi(apiRoute);
|
|
13
13
|
const inFlight = useRef(false);
|
|
14
14
|
const [busy, setBusy] = useState(false);
|
|
15
15
|
const [error, setError] = useState(null);
|
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { surfaceUrls } from './surfaceUrls.js';
|
|
3
4
|
import './theme.css';
|
|
4
5
|
import './dashboard.css';
|
|
5
6
|
import { Card, useAuth, useConfig } from '@payloadcms/ui';
|
|
6
|
-
import { formatAdminURL } from 'payload/shared';
|
|
7
7
|
import { SURFACE_COPY } from './copy/surface.js';
|
|
8
|
-
export function TestsDashboardCards() {
|
|
8
|
+
/** @deprecated Retained for existing import maps until 0.5.0. */ export function TestsDashboardCards() {
|
|
9
9
|
const { config } = useConfig();
|
|
10
10
|
const { permissions } = useAuth();
|
|
11
11
|
const testPermissions = permissions?.collections?.['xp-experiments'];
|
|
12
12
|
const canManage = testPermissions?.update === true;
|
|
13
13
|
if (!canManage) return null;
|
|
14
|
-
const href = (path)=>formatAdminURL({
|
|
15
|
-
adminRoute: config.routes.admin,
|
|
16
|
-
path
|
|
17
|
-
});
|
|
18
14
|
return /*#__PURE__*/ _jsxs("section", {
|
|
19
15
|
className: "xp-dash-group",
|
|
20
16
|
"aria-labelledby": "xp-dash-title",
|
|
@@ -32,7 +28,7 @@ export function TestsDashboardCards() {
|
|
|
32
28
|
title: SURFACE_COPY.navAllTests,
|
|
33
29
|
titleAs: "h3",
|
|
34
30
|
buttonAriaLabel: SURFACE_COPY.dashShowTests,
|
|
35
|
-
href:
|
|
31
|
+
href: surfaceUrls.home(config.routes.admin),
|
|
36
32
|
id: "card-xp-tests"
|
|
37
33
|
})
|
|
38
34
|
}),
|
|
@@ -41,7 +37,7 @@ export function TestsDashboardCards() {
|
|
|
41
37
|
title: SURFACE_COPY.navGoals,
|
|
42
38
|
titleAs: "h3",
|
|
43
39
|
buttonAriaLabel: SURFACE_COPY.dashShowGoals,
|
|
44
|
-
href:
|
|
40
|
+
href: surfaceUrls.goals(config.routes.admin),
|
|
45
41
|
id: "card-xp-goals"
|
|
46
42
|
})
|
|
47
43
|
})
|
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { surfaceUrls } from './surfaceUrls.js';
|
|
3
4
|
import './theme.css';
|
|
4
5
|
import { Link, NavGroup, useAuth, useConfig } from '@payloadcms/ui';
|
|
5
|
-
import { formatAdminURL } from 'payload/shared';
|
|
6
6
|
import { usePathname } from 'next/navigation.js';
|
|
7
7
|
import { SURFACE_COPY } from './copy/surface.js';
|
|
8
|
-
function TestsNav({ isOpen = true }) {
|
|
8
|
+
/** @deprecated Retained for existing import maps until 0.5.0. */ function TestsNav({ isOpen = true }) {
|
|
9
9
|
const { config } = useConfig();
|
|
10
10
|
const { permissions } = useAuth();
|
|
11
11
|
const pathname = usePathname();
|
|
12
|
-
const href =
|
|
13
|
-
|
|
14
|
-
path: '/experiments'
|
|
15
|
-
});
|
|
16
|
-
const goalsHref = formatAdminURL({
|
|
17
|
-
adminRoute: config.routes.admin,
|
|
18
|
-
path: '/experiments/goals'
|
|
19
|
-
});
|
|
12
|
+
const href = surfaceUrls.home(config.routes.admin);
|
|
13
|
+
const goalsHref = surfaceUrls.goals(config.routes.admin);
|
|
20
14
|
const base = href.replace(/\/$/, '');
|
|
21
15
|
const goalsBase = goalsHref.replace(/\/$/, '');
|
|
22
16
|
const goalsActive = pathname === goalsBase || Boolean(pathname?.startsWith(`${goalsBase}/`));
|
|
@@ -8,7 +8,8 @@ export declare function DialogFrame({ title, onClose, children, initialFocus, di
|
|
|
8
8
|
onClose: () => void;
|
|
9
9
|
children: ReactNode;
|
|
10
10
|
}): import("react").JSX.Element;
|
|
11
|
-
export declare function ConfirmDialog({ id, action, winnerLabel, onClose, canManage, isDecided }: {
|
|
11
|
+
export declare function ConfirmDialog({ id, action, winnerLabel, onClose, canManage, isDecided, apiRoute }: {
|
|
12
|
+
apiRoute: string;
|
|
12
13
|
canManage?: boolean;
|
|
13
14
|
isDecided?: boolean;
|
|
14
15
|
id: DocId;
|
|
@@ -20,8 +20,8 @@ export function DialogFrame({ title, onClose, children, initialFocus, dismissibl
|
|
|
20
20
|
children: children
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
|
-
export function ConfirmDialog({ id, action, winnerLabel, onClose, canManage = false, isDecided = false }) {
|
|
24
|
-
const api = useApi();
|
|
23
|
+
export function ConfirmDialog({ id, action, winnerLabel, onClose, canManage = false, isDecided = false, apiRoute }) {
|
|
24
|
+
const api = useApi(apiRoute);
|
|
25
25
|
const [typed, setTyped] = useState('');
|
|
26
26
|
const [busy, setBusy] = useState(false);
|
|
27
27
|
const [reportBusy, setReportBusy] = useState(false);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { DocId } from '../../types/contracts.js';
|
|
2
2
|
import type { PreflightResult } from '../../types/endpoints.js';
|
|
3
3
|
import type { ActionGroup } from './actions.model.js';
|
|
4
|
-
export declare function DecideDialog({ id, arms, checks, onClose }: {
|
|
4
|
+
export declare function DecideDialog({ id, arms, checks, onClose, apiRoute }: {
|
|
5
|
+
apiRoute: string;
|
|
5
6
|
id: DocId;
|
|
6
7
|
arms: readonly ActionGroup[];
|
|
7
8
|
checks?: PreflightResult['checks'];
|
|
@@ -10,8 +10,8 @@ import { RESULTS_COPY } from '../copy/results.js';
|
|
|
10
10
|
import { ACTIONS_COPY as C, decideCopy } from '../copy/actions.js';
|
|
11
11
|
import { decideState } from './decide.model.js';
|
|
12
12
|
import { DialogFrame } from './ConfirmDialog.js';
|
|
13
|
-
export function DecideDialog({ id, arms, checks = [], onClose }) {
|
|
14
|
-
const api = useApi();
|
|
13
|
+
export function DecideDialog({ id, arms, checks = [], onClose, apiRoute }) {
|
|
14
|
+
const api = useApi(apiRoute);
|
|
15
15
|
const [snapshot, setSnapshot] = useState(null);
|
|
16
16
|
const [chosenArm, setChosenArm] = useState(null);
|
|
17
17
|
const [note, setNote] = useState('');
|
package/dist/admin/api.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DocId } from '../types/contracts.js';
|
|
2
|
-
import type { RestoreContentEntryBody, RestoreContentEntryResult, DismissContentEntryBody, DismissContentEntryResult, AddMetricBody, AllocateBody, ArchiveBody, DecideBody, MakeContentResult, PreflightResult, PreviewTokenResult, RefusalCode, ResultsResponse, SummaryResponse, TransitionSuccess } from '../types/endpoints.js';
|
|
2
|
+
import type { WrapBlockBody, PagesResult, PlacementStatusBody, PlacementStatusResult, RestoreContentEntryBody, RestoreContentEntryResult, DismissContentEntryBody, DismissContentEntryResult, AddMetricBody, AllocateBody, ArchiveBody, DecideBody, MakeContentResult, AddMetricResult, ClickKeysResult, PreflightResult, PreviewTokenResult, RefusalCode, ResultsResponse, SummaryResponse, TransitionSuccess } from '../types/endpoints.js';
|
|
3
3
|
import { refusalMessage } from './copy/common.js';
|
|
4
4
|
export type ApiResult<T> = {
|
|
5
5
|
ok: true;
|
|
@@ -13,9 +13,24 @@ export type ApiResult<T> = {
|
|
|
13
13
|
};
|
|
14
14
|
type RequestOptions = NonNullable<Parameters<typeof refusalMessage>[3]>;
|
|
15
15
|
export declare function createApi(apiBase: string): {
|
|
16
|
+
findTestIdBySlug: (slug: string) => Promise<Extract<ApiResult<DocId>, {
|
|
17
|
+
ok: false;
|
|
18
|
+
}> | {
|
|
19
|
+
ok: true;
|
|
20
|
+
data: DocId;
|
|
21
|
+
}>;
|
|
22
|
+
deleteTest: (id: DocId) => Promise<ApiResult<unknown>>;
|
|
23
|
+
wrapBlock: (body: WrapBlockBody) => Promise<ApiResult<Record<string, unknown>>>;
|
|
24
|
+
pages: (query: {
|
|
25
|
+
collection: string;
|
|
26
|
+
q?: string;
|
|
27
|
+
path?: string;
|
|
28
|
+
page?: number;
|
|
29
|
+
}, signal?: AbortSignal) => Promise<ApiResult<PagesResult>>;
|
|
30
|
+
placementStatus: (body: PlacementStatusBody) => Promise<ApiResult<PlacementStatusResult>>;
|
|
16
31
|
start: (id: DocId) => Promise<ApiResult<TransitionSuccess>>;
|
|
17
32
|
allocate: (id: DocId, body: AllocateBody, options?: RequestOptions) => Promise<ApiResult<TransitionSuccess>>;
|
|
18
|
-
addMetric: (id: DocId, body: AddMetricBody, options?: RequestOptions) => Promise<ApiResult<
|
|
33
|
+
addMetric: (id: DocId, body: AddMetricBody, options?: RequestOptions) => Promise<ApiResult<AddMetricResult>>;
|
|
19
34
|
stop: (id: DocId) => Promise<ApiResult<TransitionSuccess>>;
|
|
20
35
|
decide: (id: DocId, body: DecideBody) => Promise<ApiResult<TransitionSuccess>>;
|
|
21
36
|
releaseHoldout: (id: DocId) => Promise<ApiResult<TransitionSuccess>>;
|
|
@@ -24,6 +39,7 @@ export declare function createApi(apiBase: string): {
|
|
|
24
39
|
dismissContentEntry: (id: DocId, body: DismissContentEntryBody) => Promise<ApiResult<DismissContentEntryResult>>;
|
|
25
40
|
restoreContentEntry: (id: DocId, body: RestoreContentEntryBody) => Promise<ApiResult<RestoreContentEntryResult>>;
|
|
26
41
|
archive: (id: DocId, body: ArchiveBody) => Promise<ApiResult<TransitionSuccess>>;
|
|
42
|
+
clickKeys: (id: DocId) => Promise<ApiResult<ClickKeysResult>>;
|
|
27
43
|
preflight: (id: DocId) => Promise<ApiResult<PreflightResult>>;
|
|
28
44
|
previewToken: (id: DocId, arm: string) => Promise<ApiResult<PreviewTokenResult>>;
|
|
29
45
|
results: (id: DocId, options?: {
|
package/dist/admin/api.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { API_MESSAGES, refusalMessage } from './copy/common.js';
|
|
2
|
+
import { BLOCK_COPY } from './copy/blocks.js';
|
|
2
3
|
import { CONTENT_ENTRY_REFUSALS } from './copy/actions.js';
|
|
3
4
|
export function createApi(apiBase) {
|
|
5
|
+
const collectionBase = `${apiBase.replace(/\/$/, '')}/xp-experiments`;
|
|
4
6
|
const base = `${apiBase.replace(/\/$/, '')}/xp/experiments`;
|
|
5
7
|
const route = (id, action)=>`${base}/${encodeURIComponent(id)}/${action}`;
|
|
6
|
-
async function request(path, method, body, options) {
|
|
8
|
+
async function request(path, method, body, options, signal) {
|
|
7
9
|
try {
|
|
8
10
|
const response = await fetch(path(), {
|
|
9
11
|
method,
|
|
10
12
|
credentials: 'include',
|
|
13
|
+
...signal ? {
|
|
14
|
+
signal
|
|
15
|
+
} : {},
|
|
11
16
|
...body === undefined ? {} : {
|
|
12
17
|
headers: {
|
|
13
18
|
'Content-Type': 'application/json'
|
|
@@ -48,7 +53,7 @@ export function createApi(apiBase) {
|
|
|
48
53
|
code
|
|
49
54
|
} : {},
|
|
50
55
|
message: refusalMessage(code, response.status, typeof error?.message === 'string' ? error.message : undefined, options),
|
|
51
|
-
retryable: code === 'commit-unverified' || response.status >= 500 && response.status !== 501
|
|
56
|
+
retryable: code === 'page-busy' || code === 'commit-unverified' || response.status >= 500 && response.status !== 501
|
|
52
57
|
};
|
|
53
58
|
} catch {
|
|
54
59
|
return {
|
|
@@ -61,6 +66,45 @@ export function createApi(apiBase) {
|
|
|
61
66
|
}
|
|
62
67
|
const post = (id, action, body, options)=>request(()=>route(id, action), 'POST', body, options);
|
|
63
68
|
return {
|
|
69
|
+
findTestIdBySlug: async (slug)=>{
|
|
70
|
+
const result = await request(()=>`${collectionBase}?where[slug][equals]=${encodeURIComponent(slug)}&depth=0&limit=1`, 'GET');
|
|
71
|
+
if (!result.ok) return result;
|
|
72
|
+
const id = result.data?.docs?.[0]?.id;
|
|
73
|
+
if (id === undefined) return {
|
|
74
|
+
ok: false,
|
|
75
|
+
status: 404,
|
|
76
|
+
code: 'not-found',
|
|
77
|
+
message: refusalMessage('not-found', 404, undefined, {
|
|
78
|
+
notFound: BLOCK_COPY.alreadyDeleted
|
|
79
|
+
}),
|
|
80
|
+
retryable: false
|
|
81
|
+
};
|
|
82
|
+
return {
|
|
83
|
+
ok: true,
|
|
84
|
+
data: id
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
deleteTest: (id)=>request(()=>`${collectionBase}/${encodeURIComponent(id)}`, 'DELETE', undefined, {
|
|
88
|
+
notFound: BLOCK_COPY.noLongerDraft,
|
|
89
|
+
forbidden: BLOCK_COPY.noLongerDraft,
|
|
90
|
+
codes: {
|
|
91
|
+
'invalid-status': BLOCK_COPY.noLongerDraft
|
|
92
|
+
}
|
|
93
|
+
}),
|
|
94
|
+
wrapBlock: (body)=>request(()=>`${apiBase.replace(/\/$/, '')}/xp/blocks/wrap`, 'POST', body),
|
|
95
|
+
pages: (query, signal)=>request(()=>`${apiBase.replace(/\/$/, '')}/xp/pages?${new URLSearchParams({
|
|
96
|
+
collection: query.collection,
|
|
97
|
+
...query.q ? {
|
|
98
|
+
q: query.q
|
|
99
|
+
} : {},
|
|
100
|
+
...query.path !== undefined ? {
|
|
101
|
+
path: query.path
|
|
102
|
+
} : {},
|
|
103
|
+
...query.page ? {
|
|
104
|
+
page: String(query.page)
|
|
105
|
+
} : {}
|
|
106
|
+
})}`, 'GET', undefined, undefined, signal),
|
|
107
|
+
placementStatus: (body)=>request(()=>`${apiBase.replace(/\/$/, '')}/xp/placements/status`, 'POST', body),
|
|
64
108
|
start: (id)=>post(id, 'start'),
|
|
65
109
|
allocate: (id, body, options)=>post(id, 'allocate', body, options),
|
|
66
110
|
addMetric: (id, body, options)=>post(id, 'add-metric', body, options),
|
|
@@ -72,6 +116,7 @@ export function createApi(apiBase) {
|
|
|
72
116
|
dismissContentEntry: (id, body)=>post(id, 'content-report/dismiss', body, CONTENT_ENTRY_REFUSALS),
|
|
73
117
|
restoreContentEntry: (id, body)=>post(id, 'content-report/restore', body, CONTENT_ENTRY_REFUSALS),
|
|
74
118
|
archive: (id, body)=>post(id, 'archive', body),
|
|
119
|
+
clickKeys: (id)=>request(()=>route(id, 'click-keys'), 'GET'),
|
|
75
120
|
preflight: (id)=>request(()=>route(id, 'preflight'), 'GET'),
|
|
76
121
|
previewToken: (id, arm)=>request(()=>`${route(id, 'preview-token')}?arm=${encodeURIComponent(arm)}`, 'GET'),
|
|
77
122
|
results: (id, options = {})=>request(()=>{
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { UIFieldClientProps } from 'payload';
|
|
2
|
+
export declare function BlockStatusField({ path }: UIFieldClientProps): import("react").JSX.Element | null;
|
|
3
|
+
export declare function BlockSlotRowLabel(): import("react").JSX.Element;
|
|
4
|
+
export declare function CopyOriginalField({ path }: UIFieldClientProps): import("react").JSX.Element | null;
|