@delmaredigital/payload-experiments 0.0.0-stage → 0.1.0-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +582 -2
- package/dist/access.d.ts +1 -0
- package/dist/access.js +2 -0
- package/dist/admin/AllocationField.d.ts +2 -0
- package/dist/admin/AllocationField.js +80 -0
- package/dist/admin/FlagPointSelect.d.ts +2 -0
- package/dist/admin/FlagPointSelect.js +54 -0
- package/dist/admin/LayerUsage.d.ts +2 -0
- package/dist/admin/LayerUsage.js +24 -0
- package/dist/admin/LockedFieldNotice.d.ts +2 -0
- package/dist/admin/LockedFieldNotice.js +26 -0
- package/dist/admin/MetricSourceSelect.d.ts +2 -0
- package/dist/admin/MetricSourceSelect.js +32 -0
- package/dist/admin/PhaseHistory.d.ts +2 -0
- package/dist/admin/PhaseHistory.js +76 -0
- package/dist/admin/PreviewLinks.d.ts +11 -0
- package/dist/admin/PreviewLinks.js +69 -0
- package/dist/admin/StatusCell.d.ts +3 -0
- package/dist/admin/StatusCell.js +19 -0
- package/dist/admin/SummaryCell.d.ts +2 -0
- package/dist/admin/SummaryCell.js +32 -0
- package/dist/admin/actions/AddMetricDialog.d.ts +6 -0
- package/dist/admin/actions/AddMetricDialog.js +138 -0
- package/dist/admin/actions/AllocateDialog.d.ts +6 -0
- package/dist/admin/actions/AllocateDialog.js +81 -0
- package/dist/admin/actions/ConfirmDialog.d.ts +14 -0
- package/dist/admin/actions/ConfirmDialog.js +192 -0
- package/dist/admin/actions/DecideDialog.d.ts +7 -0
- package/dist/admin/actions/DecideDialog.js +173 -0
- package/dist/admin/actions/ExperimentActions.d.ts +3 -0
- package/dist/admin/actions/ExperimentActions.js +110 -0
- package/dist/admin/actions/MakeContentResult.d.ts +6 -0
- package/dist/admin/actions/MakeContentResult.js +50 -0
- package/dist/admin/actions/actions.model.d.ts +37 -0
- package/dist/admin/actions/actions.model.js +71 -0
- package/dist/admin/actions/decide.model.d.ts +21 -0
- package/dist/admin/actions/decide.model.js +38 -0
- package/dist/admin/allocation.model.d.ts +20 -0
- package/dist/admin/allocation.model.js +38 -0
- package/dist/admin/api.d.ts +32 -0
- package/dist/admin/api.js +95 -0
- package/dist/admin/armSelect/ArmSelectField.d.ts +2 -0
- package/dist/admin/armSelect/ArmSelectField.js +185 -0
- package/dist/admin/armSelect/armSelect.model.d.ts +30 -0
- package/dist/admin/armSelect/armSelect.model.js +20 -0
- package/dist/admin/arms/ArmsField.d.ts +2 -0
- package/dist/admin/arms/ArmsField.js +310 -0
- package/dist/admin/arms/ParamsEditor.d.ts +16 -0
- package/dist/admin/arms/ParamsEditor.js +79 -0
- package/dist/admin/arms/arms.model.d.ts +21 -0
- package/dist/admin/arms/arms.model.js +153 -0
- package/dist/admin/arms/pageTarget.model.d.ts +4 -0
- package/dist/admin/arms/pageTarget.model.js +19 -0
- package/dist/admin/arms/params.model.d.ts +7 -0
- package/dist/admin/arms/params.model.js +35 -0
- package/dist/admin/copy/actions.d.ts +38 -0
- package/dist/admin/copy/actions.js +38 -0
- package/dist/admin/copy/armSelect.d.ts +11 -0
- package/dist/admin/copy/armSelect.js +11 -0
- package/dist/admin/copy/arms.d.ts +37 -0
- package/dist/admin/copy/arms.js +47 -0
- package/dist/admin/copy/common.d.ts +20 -0
- package/dist/admin/copy/common.js +27 -0
- package/dist/admin/copy/history.d.ts +9 -0
- package/dist/admin/copy/history.js +9 -0
- package/dist/admin/copy/metrics.d.ts +9 -0
- package/dist/admin/copy/metrics.js +9 -0
- package/dist/admin/copy/results.d.ts +78 -0
- package/dist/admin/copy/results.js +81 -0
- package/dist/admin/copy/review.d.ts +33 -0
- package/dist/admin/copy/review.js +127 -0
- package/dist/admin/copy/status.d.ts +8 -0
- package/dist/admin/copy/status.js +8 -0
- package/dist/admin/copy/tabs.d.ts +7 -0
- package/dist/admin/copy/tabs.js +7 -0
- package/dist/admin/copy/who.d.ts +36 -0
- package/dist/admin/copy/who.js +36 -0
- package/dist/admin/labels.d.ts +45 -0
- package/dist/admin/labels.js +53 -0
- package/dist/admin/layerUsage.model.d.ts +15 -0
- package/dist/admin/layerUsage.model.js +18 -0
- package/dist/admin/lockedNotice.model.d.ts +3 -0
- package/dist/admin/lockedNotice.model.js +18 -0
- package/dist/admin/metricSource.model.d.ts +7 -0
- package/dist/admin/metricSource.model.js +11 -0
- package/dist/admin/paths.d.ts +16 -0
- package/dist/admin/paths.js +16 -0
- package/dist/admin/permissions.model.d.ts +10 -0
- package/dist/admin/permissions.model.js +4 -0
- package/dist/admin/phaseHistory.model.d.ts +19 -0
- package/dist/admin/phaseHistory.model.js +19 -0
- package/dist/admin/reload.d.ts +1 -0
- package/dist/admin/reload.js +1 -0
- package/dist/admin/results/ArmTable.d.ts +8 -0
- package/dist/admin/results/ArmTable.js +112 -0
- package/dist/admin/results/CappedCell.d.ts +4 -0
- package/dist/admin/results/CappedCell.js +24 -0
- package/dist/admin/results/CiChart.d.ts +10 -0
- package/dist/admin/results/CiChart.js +178 -0
- package/dist/admin/results/DataQualityPanel.d.ts +4 -0
- package/dist/admin/results/DataQualityPanel.js +40 -0
- package/dist/admin/results/ResultsView.d.ts +3 -0
- package/dist/admin/results/ResultsView.js +312 -0
- package/dist/admin/results/VerdictCard.d.ts +9 -0
- package/dist/admin/results/VerdictCard.js +49 -0
- package/dist/admin/results/chart.model.d.ts +32 -0
- package/dist/admin/results/chart.model.js +52 -0
- package/dist/admin/results/format.d.ts +10 -0
- package/dist/admin/results/format.js +55 -0
- package/dist/admin/results/results.model.d.ts +105 -0
- package/dist/admin/results/results.model.js +175 -0
- package/dist/admin/review/ReviewPanel.d.ts +2 -0
- package/dist/admin/review/ReviewPanel.js +268 -0
- package/dist/admin/review/review.model.d.ts +31 -0
- package/dist/admin/review/review.model.js +40 -0
- package/dist/admin/status.model.d.ts +16 -0
- package/dist/admin/status.model.js +17 -0
- package/dist/admin/styles.css +10 -0
- package/dist/admin/styles.d.ts +1 -0
- package/dist/admin/styles.js +2 -0
- package/dist/admin/summaryBatcher.d.ts +8 -0
- package/dist/admin/summaryBatcher.js +33 -0
- package/dist/admin/targeting/TargetingAttributeField.d.ts +2 -0
- package/dist/admin/targeting/TargetingAttributeField.js +31 -0
- package/dist/admin/targeting/TargetingOpField.d.ts +2 -0
- package/dist/admin/targeting/TargetingOpField.js +45 -0
- package/dist/admin/targeting/TargetingValueField.d.ts +2 -0
- package/dist/admin/targeting/TargetingValueField.js +142 -0
- package/dist/admin/targeting/targeting.model.d.ts +18 -0
- package/dist/admin/targeting/targeting.model.js +48 -0
- package/dist/admin/useApi.d.ts +20 -0
- package/dist/admin/useApi.js +10 -0
- package/dist/admin/useLayerUsage.d.ts +15 -0
- package/dist/admin/useLayerUsage.js +92 -0
- package/dist/client/ExperimentPageview.d.ts +1 -0
- package/dist/client/ExperimentPageview.js +27 -0
- package/dist/client/ExperimentsProvider.d.ts +6 -0
- package/dist/client/ExperimentsProvider.js +22 -0
- package/dist/client/ViewportExposure.d.ts +9 -0
- package/dist/client/ViewportExposure.js +69 -0
- package/dist/client/beacon.d.ts +17 -0
- package/dist/client/beacon.js +71 -0
- package/dist/client/context.d.ts +2 -0
- package/dist/client/context.js +2 -0
- package/dist/client/idempotency.d.ts +2 -0
- package/dist/client/idempotency.js +13 -0
- package/dist/client/trackConversionBeacon.d.ts +3 -0
- package/dist/client/trackConversionBeacon.js +16 -0
- package/dist/client/useExperiment.d.ts +5 -0
- package/dist/client/useExperiment.js +27 -0
- package/dist/client/useFlagPoint.d.ts +9 -0
- package/dist/client/useFlagPoint.js +32 -0
- package/dist/collections/access.d.ts +21 -0
- package/dist/collections/access.js +25 -0
- package/dist/collections/events.d.ts +3 -0
- package/dist/collections/events.js +117 -0
- package/dist/collections/experiments/armInvariants.d.ts +12 -0
- package/dist/collections/experiments/armInvariants.js +89 -0
- package/dist/collections/experiments/config.d.ts +3 -0
- package/dist/collections/experiments/config.js +574 -0
- package/dist/collections/experiments/createDefaults.d.ts +2 -0
- package/dist/collections/experiments/createDefaults.js +7 -0
- package/dist/collections/experiments/deleteGuard.d.ts +3 -0
- package/dist/collections/experiments/deleteGuard.js +8 -0
- package/dist/collections/experiments/editability.d.ts +4 -0
- package/dist/collections/experiments/editability.js +35 -0
- package/dist/collections/experiments/flagControlParams.d.ts +2 -0
- package/dist/collections/experiments/flagControlParams.js +32 -0
- package/dist/collections/experiments/lockHook.d.ts +2 -0
- package/dist/collections/experiments/lockHook.js +134 -0
- package/dist/collections/identities.d.ts +3 -0
- package/dist/collections/identities.js +30 -0
- package/dist/collections/index.d.ts +6 -0
- package/dist/collections/index.js +24 -0
- package/dist/collections/layers.d.ts +3 -0
- package/dist/collections/layers.js +77 -0
- package/dist/collections/metrics.d.ts +3 -0
- package/dist/collections/metrics.js +115 -0
- package/dist/collections/phases.d.ts +3 -0
- package/dist/collections/phases.js +59 -0
- package/dist/collections/serialize.d.ts +5 -0
- package/dist/collections/serialize.js +40 -0
- package/dist/collections/snapshots.d.ts +3 -0
- package/dist/collections/snapshots.js +47 -0
- package/dist/constants.d.ts +7 -0
- package/dist/constants.js +7 -0
- package/dist/db/adapter.d.ts +3 -0
- package/dist/db/adapter.js +9 -0
- package/dist/db/appends.d.ts +58 -0
- package/dist/db/appends.js +116 -0
- package/dist/db/delete.d.ts +17 -0
- package/dist/db/delete.js +35 -0
- package/dist/db/erase.d.ts +12 -0
- package/dist/db/erase.js +55 -0
- package/dist/db/events.d.ts +21 -0
- package/dist/db/events.js +100 -0
- package/dist/db/executor.d.ts +10 -0
- package/dist/db/executor.js +37 -0
- package/dist/db/identities.d.ts +11 -0
- package/dist/db/identities.js +29 -0
- package/dist/db/locks.d.ts +15 -0
- package/dist/db/locks.js +23 -0
- package/dist/db/manifest.d.ts +8 -0
- package/dist/db/manifest.js +8 -0
- package/dist/db/names.d.ts +271 -0
- package/dist/db/names.js +270 -0
- package/dist/db/pgExecutor.d.ts +41 -0
- package/dist/db/pgExecutor.js +250 -0
- package/dist/db/results.d.ts +53 -0
- package/dist/db/results.js +103 -0
- package/dist/db/slices.d.ts +18 -0
- package/dist/db/slices.js +51 -0
- package/dist/db/snapshots.d.ts +47 -0
- package/dist/db/snapshots.js +89 -0
- package/dist/db/sql.d.ts +5 -0
- package/dist/db/sql.js +5 -0
- package/dist/db/state.d.ts +12 -0
- package/dist/db/state.js +27 -0
- package/dist/db/sweep.d.ts +13 -0
- package/dist/db/sweep.js +90 -0
- package/dist/db/txid.d.ts +7 -0
- package/dist/db/txid.js +12 -0
- package/dist/define.d.ts +3 -0
- package/dist/define.js +6 -0
- package/dist/endpoints/events.d.ts +7 -0
- package/dist/endpoints/events.js +98 -0
- package/dist/endpoints/index.d.ts +18 -0
- package/dist/endpoints/index.js +160 -0
- package/dist/endpoints/manifest.d.ts +7 -0
- package/dist/endpoints/manifest.js +30 -0
- package/dist/endpoints/preflight.d.ts +8 -0
- package/dist/endpoints/preflight.js +25 -0
- package/dist/endpoints/previewToken.d.ts +7 -0
- package/dist/endpoints/previewToken.js +54 -0
- package/dist/endpoints/recompute.d.ts +7 -0
- package/dist/endpoints/recompute.js +41 -0
- package/dist/endpoints/results.d.ts +24 -0
- package/dist/endpoints/results.js +136 -0
- package/dist/endpoints/summary.d.ts +7 -0
- package/dist/endpoints/summary.js +40 -0
- package/dist/endpoints/transitions.d.ts +42 -0
- package/dist/endpoints/transitions.js +154 -0
- package/dist/errors.d.ts +27 -0
- package/dist/errors.js +33 -0
- package/dist/events/body.d.ts +7 -0
- package/dist/events/body.js +43 -0
- package/dist/events/dimensions.d.ts +4 -0
- package/dist/events/dimensions.js +51 -0
- package/dist/events/preview.d.ts +2 -0
- package/dist/events/preview.js +9 -0
- package/dist/events/request.d.ts +3 -0
- package/dist/events/request.js +14 -0
- package/dist/events/rows.d.ts +7 -0
- package/dist/events/rows.js +65 -0
- package/dist/exports/adminClient.d.ts +16 -0
- package/dist/exports/adminClient.js +17 -0
- package/dist/exports/client.d.ts +7 -0
- package/dist/exports/client.js +7 -0
- package/dist/exports/index.d.ts +4 -0
- package/dist/exports/index.js +3 -0
- package/dist/exports/proxy.d.ts +4 -0
- package/dist/exports/proxy.js +3 -0
- package/dist/exports/puck.d.ts +4 -0
- package/dist/exports/puck.js +3 -0
- package/dist/exports/puckEditor.d.ts +1 -0
- package/dist/exports/puckEditor.js +2 -0
- package/dist/exports/rsc.d.ts +13 -0
- package/dist/exports/rsc.js +13 -0
- package/dist/exports/runtimeProxy.d.ts +4 -0
- package/dist/exports/runtimeProxy.js +2 -0
- package/dist/exports/runtimeServer.d.ts +2 -0
- package/dist/exports/runtimeServer.js +1 -0
- package/dist/fields/armSelect.d.ts +7 -0
- package/dist/fields/armSelect.js +33 -0
- package/dist/globals/state.d.ts +3 -0
- package/dist/globals/state.js +31 -0
- package/dist/hooks/contentChanges.d.ts +3 -0
- package/dist/hooks/contentChanges.js +10 -0
- package/dist/hooks/hostHook.d.ts +5 -0
- package/dist/hooks/hostHook.js +20 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -0
- package/dist/manifest/cache.d.ts +10 -0
- package/dist/manifest/cache.js +51 -0
- package/dist/manifest/compile.d.ts +63 -0
- package/dist/manifest/compile.js +237 -0
- package/dist/onInit.d.ts +5 -0
- package/dist/onInit.js +13 -0
- package/dist/options/fromReq.d.ts +3 -0
- package/dist/options/fromReq.js +5 -0
- package/dist/options.d.ts +9 -0
- package/dist/options.js +99 -0
- package/dist/plugin.d.ts +7 -0
- package/dist/plugin.js +62 -0
- package/dist/proxy/apply.d.ts +3 -0
- package/dist/proxy/apply.js +5 -0
- package/dist/proxy/consentFallback.d.ts +5 -0
- package/dist/proxy/consentFallback.js +27 -0
- package/dist/proxy/control.d.ts +6 -0
- package/dist/proxy/control.js +36 -0
- package/dist/proxy/createProxy.d.ts +3 -0
- package/dist/proxy/createProxy.js +15 -0
- package/dist/proxy/pageRewrite.d.ts +4 -0
- package/dist/proxy/pageRewrite.js +41 -0
- package/dist/proxy/resolve.d.ts +13 -0
- package/dist/proxy/resolve.js +163 -0
- package/dist/proxy/skip.d.ts +1 -0
- package/dist/proxy/skip.js +9 -0
- package/dist/puck/ExperimentPicker.d.ts +5 -0
- package/dist/puck/ExperimentPicker.js +88 -0
- package/dist/puck/ExperimentSlot.d.ts +3 -0
- package/dist/puck/ExperimentSlot.js +16 -0
- package/dist/puck/copy.d.ts +14 -0
- package/dist/puck/copy.js +14 -0
- package/dist/puck/editor/CreateExperimentDrawer.d.ts +5 -0
- package/dist/puck/editor/CreateExperimentDrawer.js +120 -0
- package/dist/puck/editor/ExperimentSlotEditor.d.ts +3 -0
- package/dist/puck/editor/ExperimentSlotEditor.js +225 -0
- package/dist/puck/editor/cloneSlot.d.ts +3 -0
- package/dist/puck/editor/cloneSlot.js +41 -0
- package/dist/puck/editor/copy.d.ts +25 -0
- package/dist/puck/editor/copy.js +25 -0
- package/dist/puck/editor/editor.model.d.ts +16 -0
- package/dist/puck/editor/editor.model.js +33 -0
- package/dist/puck/experimentSlotField.d.ts +5 -0
- package/dist/puck/experimentSlotField.js +13 -0
- package/dist/puck/slot.model.d.ts +2 -0
- package/dist/puck/slot.model.js +11 -0
- package/dist/puck/slotConfig.d.ts +5 -0
- package/dist/puck/slotConfig.js +25 -0
- package/dist/results/aggregate.d.ts +34 -0
- package/dist/results/aggregate.js +66 -0
- package/dist/results/analyse.d.ts +79 -0
- package/dist/results/analyse.js +378 -0
- package/dist/results/cuped.d.ts +16 -0
- package/dist/results/cuped.js +31 -0
- package/dist/results/decideGuard.d.ts +3 -0
- package/dist/results/decideGuard.js +8 -0
- package/dist/results/guardrails.d.ts +17 -0
- package/dist/results/guardrails.js +10 -0
- package/dist/results/history.d.ts +13 -0
- package/dist/results/history.js +39 -0
- package/dist/results/identityCredit.d.ts +16 -0
- package/dist/results/identityCredit.js +46 -0
- package/dist/results/power.d.ts +15 -0
- package/dist/results/power.js +60 -0
- package/dist/results/rolloutUnits.d.ts +17 -0
- package/dist/results/rolloutUnits.js +26 -0
- package/dist/results/snapshot.d.ts +9 -0
- package/dist/results/snapshot.js +154 -0
- package/dist/results/srm.d.ts +9 -0
- package/dist/results/srm.js +22 -0
- package/dist/results/verdict.d.ts +82 -0
- package/dist/results/verdict.js +85 -0
- package/dist/runtime/assign.d.ts +23 -0
- package/dist/runtime/assign.js +104 -0
- package/dist/runtime/bot.d.ts +1 -0
- package/dist/runtime/bot.js +7 -0
- package/dist/runtime/cookie.d.ts +17 -0
- package/dist/runtime/cookie.js +35 -0
- package/dist/runtime/eligibility.d.ts +22 -0
- package/dist/runtime/eligibility.js +20 -0
- package/dist/runtime/header.d.ts +17 -0
- package/dist/runtime/header.js +97 -0
- package/dist/runtime/loaders.d.ts +17 -0
- package/dist/runtime/loaders.js +29 -0
- package/dist/runtime/normalisePath.d.ts +7 -0
- package/dist/runtime/normalisePath.js +15 -0
- package/dist/runtime/previewToken.d.ts +9 -0
- package/dist/runtime/previewToken.js +23 -0
- package/dist/runtime/proxyRuntime.d.ts +2 -0
- package/dist/runtime/proxyRuntime.js +22 -0
- package/dist/runtime/seed.d.ts +2 -0
- package/dist/runtime/seed.js +4 -0
- package/dist/runtime/signing.d.ts +3 -0
- package/dist/runtime/signing.js +16 -0
- package/dist/runtime/targeting.d.ts +6 -0
- package/dist/runtime/targeting.js +9 -0
- package/dist/server/context.d.ts +10 -0
- package/dist/server/context.js +59 -0
- package/dist/server/eraseUser.d.ts +7 -0
- package/dist/server/eraseUser.js +4 -0
- package/dist/server/getAssignments.d.ts +9 -0
- package/dist/server/getAssignments.js +227 -0
- package/dist/server/getFlagPoint.d.ts +11 -0
- package/dist/server/getFlagPoint.js +36 -0
- package/dist/server/headerRevision.d.ts +6 -0
- package/dist/server/headerRevision.js +10 -0
- package/dist/server/linkIdentity.d.ts +9 -0
- package/dist/server/linkIdentity.js +34 -0
- package/dist/server/metadata.d.ts +7 -0
- package/dist/server/metadata.js +24 -0
- package/dist/server/previewDraft.d.ts +7 -0
- package/dist/server/previewDraft.js +51 -0
- package/dist/server/recordContentChange.d.ts +7 -0
- package/dist/server/recordContentChange.js +150 -0
- package/dist/server/recordExposures.d.ts +7 -0
- package/dist/server/recordExposures.js +56 -0
- package/dist/server/request.d.ts +7 -0
- package/dist/server/request.js +39 -0
- package/dist/server/resolveExperimentData.d.ts +6 -0
- package/dist/server/resolveExperimentData.js +64 -0
- package/dist/server/runResults.d.ts +22 -0
- package/dist/server/runResults.js +307 -0
- package/dist/server/serverRuntime.d.ts +7 -0
- package/dist/server/serverRuntime.js +20 -0
- package/dist/server/sitemap.d.ts +3 -0
- package/dist/server/sitemap.js +6 -0
- package/dist/server/toClientAssignments.d.ts +7 -0
- package/dist/server/toClientAssignments.js +56 -0
- package/dist/server/trackConversion.d.ts +12 -0
- package/dist/server/trackConversion.js +69 -0
- package/dist/targeting/ops.d.ts +8 -0
- package/dist/targeting/ops.js +58 -0
- package/dist/transitions/addMetric.d.ts +16 -0
- package/dist/transitions/addMetric.js +126 -0
- package/dist/transitions/allocate.d.ts +9 -0
- package/dist/transitions/allocate.js +84 -0
- package/dist/transitions/archive.d.ts +9 -0
- package/dist/transitions/archive.js +46 -0
- package/dist/transitions/checkStart.d.ts +61 -0
- package/dist/transitions/checkStart.js +104 -0
- package/dist/transitions/confirmPhrases.d.ts +5 -0
- package/dist/transitions/confirmPhrases.js +18 -0
- package/dist/transitions/decide.d.ts +9 -0
- package/dist/transitions/decide.js +138 -0
- package/dist/transitions/deleteExperiment.d.ts +5 -0
- package/dist/transitions/deleteExperiment.js +66 -0
- package/dist/transitions/kernel.d.ts +31 -0
- package/dist/transitions/kernel.js +69 -0
- package/dist/transitions/makeContent.d.ts +20 -0
- package/dist/transitions/makeContent.js +411 -0
- package/dist/transitions/phaseRecord.d.ts +27 -0
- package/dist/transitions/phaseRecord.js +81 -0
- package/dist/transitions/placements.d.ts +26 -0
- package/dist/transitions/placements.js +89 -0
- package/dist/transitions/preflightData.d.ts +21 -0
- package/dist/transitions/preflightData.js +317 -0
- package/dist/transitions/releaseHoldout.d.ts +8 -0
- package/dist/transitions/releaseHoldout.js +30 -0
- package/dist/transitions/resolveCaps.d.ts +28 -0
- package/dist/transitions/resolveCaps.js +68 -0
- package/dist/transitions/start.d.ts +26 -0
- package/dist/transitions/start.js +220 -0
- package/dist/transitions/stop.d.ts +8 -0
- package/dist/transitions/stop.js +25 -0
- package/dist/types/admin.d.ts +20 -0
- package/dist/types/admin.js +1 -0
- package/dist/types/assignments.d.ts +57 -0
- package/dist/types/assignments.js +1 -0
- package/dist/types/contracts.d.ts +133 -0
- package/dist/types/contracts.js +2 -0
- package/dist/types/endpoints.d.ts +135 -0
- package/dist/types/endpoints.js +1 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.js +8 -0
- package/dist/types/manifest.d.ts +42 -0
- package/dist/types/manifest.js +1 -0
- package/dist/types/puck.d.ts +23 -0
- package/dist/types/puck.js +1 -0
- package/dist/types/runtime.d.ts +59 -0
- package/dist/types/runtime.js +1 -0
- package/dist/types/snapshot.d.ts +117 -0
- package/dist/types/snapshot.js +1 -0
- package/package.json +137 -3
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { MetricSource, Status } from '../../types/contracts.js';
|
|
2
|
+
import type { PreflightResult } from '../../types/endpoints.js';
|
|
3
|
+
export declare function reviewView(preflight: PreflightResult, { status, primaryMetricKind }: {
|
|
4
|
+
status: Status;
|
|
5
|
+
primaryMetricKind?: MetricSource['kind'];
|
|
6
|
+
}): {
|
|
7
|
+
checks: {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
done: string | undefined;
|
|
10
|
+
hint: string | undefined;
|
|
11
|
+
detail: string | undefined;
|
|
12
|
+
}[];
|
|
13
|
+
placements: {
|
|
14
|
+
previewAvailable: boolean;
|
|
15
|
+
collection: string;
|
|
16
|
+
id: import("../../types/contracts.js").DocId;
|
|
17
|
+
title: string;
|
|
18
|
+
url?: string;
|
|
19
|
+
ok: boolean;
|
|
20
|
+
detail?: string;
|
|
21
|
+
}[];
|
|
22
|
+
baseline: string;
|
|
23
|
+
traffic: string;
|
|
24
|
+
estimate: string;
|
|
25
|
+
estimateWarning: boolean;
|
|
26
|
+
canStart: boolean;
|
|
27
|
+
applyValues: {
|
|
28
|
+
plannedDurationDays: number;
|
|
29
|
+
sequentialTuningParameter: number;
|
|
30
|
+
} | null;
|
|
31
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { REVIEW_COPY as C, REVIEW_DONE, REVIEW_HINTS } from '../copy/review.js';
|
|
2
|
+
const number = (value)=>new Intl.NumberFormat('en-US', {
|
|
3
|
+
maximumFractionDigits: 1
|
|
4
|
+
}).format(value);
|
|
5
|
+
export function reviewView(preflight, { status, primaryMetricKind }) {
|
|
6
|
+
const { baseline, estimate } = preflight;
|
|
7
|
+
const weeks = estimate ? Math.round(estimate.weeks * 10) / 10 : 0;
|
|
8
|
+
const formatted = baseline ? new Intl.NumberFormat('en-US', primaryMetricKind === 'binomial' ? {
|
|
9
|
+
style: 'percent',
|
|
10
|
+
maximumFractionDigits: 1
|
|
11
|
+
} : {
|
|
12
|
+
minimumFractionDigits: primaryMetricKind === 'revenue' ? 2 : 0,
|
|
13
|
+
maximumFractionDigits: primaryMetricKind === 'revenue' ? 2 : 1
|
|
14
|
+
}).format(baseline.value) : null;
|
|
15
|
+
return {
|
|
16
|
+
checks: preflight.checks.map((check)=>{
|
|
17
|
+
const detail = typeof check.detail === 'string' ? check.detail : check.detail && typeof check.detail === 'object' && 'message' in check.detail && typeof check.detail.message === 'string' ? check.detail.message : undefined;
|
|
18
|
+
const hint = check.ok ? undefined : REVIEW_HINTS[check.code] ?? detail ?? C.checkFallback;
|
|
19
|
+
return {
|
|
20
|
+
ok: check.ok,
|
|
21
|
+
done: check.ok ? REVIEW_DONE[check.code] ?? C.checkDoneFallback : undefined,
|
|
22
|
+
hint,
|
|
23
|
+
detail: detail === hint ? undefined : detail
|
|
24
|
+
};
|
|
25
|
+
}),
|
|
26
|
+
placements: preflight.placements.map((placement)=>({
|
|
27
|
+
...placement,
|
|
28
|
+
previewAvailable: Boolean(placement.url)
|
|
29
|
+
})),
|
|
30
|
+
baseline: formatted === null ? C.noBaseline : `Over the last 28 days: ${formatted}`,
|
|
31
|
+
traffic: preflight.trafficPerDay === null ? C.unknownTraffic : `About ${number(preflight.trafficPerDay)} people a day`,
|
|
32
|
+
estimate: !estimate ? C.noEstimate : !estimate.feasible ? C.infeasible : `≈ ${number(weeks)} weeks`,
|
|
33
|
+
estimateWarning: Boolean(estimate && (weeks > 8 || !estimate.feasible)),
|
|
34
|
+
canStart: preflight.checks.every((check)=>check.ok) && (status === 'draft' || status === 'stopped'),
|
|
35
|
+
applyValues: estimate ? {
|
|
36
|
+
plannedDurationDays: estimate.plannedDurationDays,
|
|
37
|
+
sequentialTuningParameter: estimate.sequentialTuningParameter
|
|
38
|
+
} : null
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Status } from '../types/contracts.js';
|
|
2
|
+
export type PillTone = 'neutral' | 'success' | 'warning' | 'info' | 'muted';
|
|
3
|
+
export declare function statusPill({ status, arms, decision }: {
|
|
4
|
+
status: Status;
|
|
5
|
+
arms?: readonly {
|
|
6
|
+
key: string;
|
|
7
|
+
}[] | null;
|
|
8
|
+
decision?: {
|
|
9
|
+
winnerArm?: string | null;
|
|
10
|
+
holdoutReleasedAt?: string | null;
|
|
11
|
+
} | null;
|
|
12
|
+
}): {
|
|
13
|
+
label: string;
|
|
14
|
+
tone: PillTone;
|
|
15
|
+
tooltip: string;
|
|
16
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { statusLabel } from './labels.js';
|
|
2
|
+
import { STATUS_TOOLTIPS } from './copy/status.js';
|
|
3
|
+
const TONES = {
|
|
4
|
+
draft: 'neutral',
|
|
5
|
+
running: 'success',
|
|
6
|
+
stopped: 'warning',
|
|
7
|
+
decided: 'info',
|
|
8
|
+
archived: 'muted'
|
|
9
|
+
};
|
|
10
|
+
export function statusPill({ status, arms = [], decision }) {
|
|
11
|
+
const holdout = status === 'decided' && arms?.some((group)=>group.key === 'holdout') && Boolean(decision?.winnerArm) && decision?.winnerArm !== 'control' && !decision?.holdoutReleasedAt;
|
|
12
|
+
return {
|
|
13
|
+
label: statusLabel(status),
|
|
14
|
+
tone: TONES[status],
|
|
15
|
+
tooltip: holdout ? STATUS_TOOLTIPS.decidedWithHoldout : STATUS_TOOLTIPS[status]
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* Admin styles for the experiments plugin; loaded by src/admin/styles.ts. */
|
|
2
|
+
.xp-status-pill { font-weight: 600; }
|
|
3
|
+
.xp-status-pill--neutral { background: var(--theme-elevation-150); color: var(--theme-elevation-800); }
|
|
4
|
+
.xp-status-pill--success { background: var(--theme-success-150); color: var(--theme-success-850); }
|
|
5
|
+
.xp-status-pill--warning { background: var(--theme-warning-150); color: var(--theme-warning-850); }
|
|
6
|
+
.xp-status-pill--info { background: var(--theme-elevation-200); color: var(--theme-elevation-900); }
|
|
7
|
+
.xp-status-pill--muted { background: var(--theme-elevation-100); color: var(--theme-elevation-600); }
|
|
8
|
+
.xp-locked-notice, .xp-notice { padding: 0.75rem; border-inline-start: 3px solid var(--theme-elevation-400); background: var(--theme-elevation-50); color: var(--theme-elevation-800); }
|
|
9
|
+
.xp-notice--warning { border-color: var(--theme-warning-500); background: var(--theme-warning-100); }
|
|
10
|
+
.xp-notice--error { border-color: var(--theme-error-500); background: var(--theme-error-100); }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './styles.css';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DocId } from '../types/contracts.js';
|
|
2
|
+
import type { SummaryResponse } from '../types/endpoints.js';
|
|
3
|
+
import type { createApi } from './api.js';
|
|
4
|
+
export type SummaryEntry = SummaryResponse[DocId];
|
|
5
|
+
type Api = Pick<ReturnType<typeof createApi>, 'summary'>;
|
|
6
|
+
/** All list cells mounted in this tick share a request, even with separate useApi instances. */
|
|
7
|
+
export declare function requestSummary(api: Api, id: DocId): Promise<SummaryEntry | null>;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
let pending = [];
|
|
2
|
+
/** All list cells mounted in this tick share a request, even with separate useApi instances. */ export function requestSummary(api, id) {
|
|
3
|
+
return new Promise((resolve)=>{
|
|
4
|
+
pending.push({
|
|
5
|
+
id,
|
|
6
|
+
resolve
|
|
7
|
+
});
|
|
8
|
+
if (pending.length !== 1) return;
|
|
9
|
+
queueMicrotask(()=>{
|
|
10
|
+
const batch = pending;
|
|
11
|
+
pending = [];
|
|
12
|
+
const ids = [
|
|
13
|
+
...new Map(batch.map((item)=>[
|
|
14
|
+
String(item.id),
|
|
15
|
+
item.id
|
|
16
|
+
])).values()
|
|
17
|
+
];
|
|
18
|
+
for(let offset = 0; offset < ids.length; offset += 100){
|
|
19
|
+
const chunk = ids.slice(offset, offset + 100);
|
|
20
|
+
const keys = new Set(chunk.map(String));
|
|
21
|
+
const callers = batch.filter((item)=>keys.has(String(item.id)));
|
|
22
|
+
void (async ()=>{
|
|
23
|
+
try {
|
|
24
|
+
const result = await api.summary(chunk);
|
|
25
|
+
for (const caller of callers)caller.resolve(result.ok ? result.data?.[caller.id] ?? null : null);
|
|
26
|
+
} catch {
|
|
27
|
+
for (const caller of callers)caller.resolve(null);
|
|
28
|
+
}
|
|
29
|
+
})();
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { SelectInput, useField, useFormFields } from '@payloadcms/ui';
|
|
4
|
+
import { isEditable } from '../../collections/experiments/editability.js';
|
|
5
|
+
import { WHO_COPY } from '../copy/who.js';
|
|
6
|
+
import { humaniseAttribute } from './targeting.model.js';
|
|
7
|
+
export function TargetingAttributeField({ path, field, readOnly }) {
|
|
8
|
+
const { value, setValue } = useField({
|
|
9
|
+
path
|
|
10
|
+
});
|
|
11
|
+
const status = useFormFields(([fields])=>fields.status?.value ?? 'draft');
|
|
12
|
+
const keys = field.admin?.custom?.attributeKeys ?? {};
|
|
13
|
+
const locked = Boolean(readOnly || field.admin?.readOnly || !isEditable(status, 'targeting'));
|
|
14
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
15
|
+
children: /*#__PURE__*/ _jsx(SelectInput, {
|
|
16
|
+
path: path,
|
|
17
|
+
name: path,
|
|
18
|
+
label: WHO_COPY.attribute,
|
|
19
|
+
required: field.required,
|
|
20
|
+
options: Object.keys(keys).map((key)=>({
|
|
21
|
+
label: humaniseAttribute(key),
|
|
22
|
+
value: key
|
|
23
|
+
})),
|
|
24
|
+
value: value ?? '',
|
|
25
|
+
readOnly: locked,
|
|
26
|
+
onChange: (option)=>{
|
|
27
|
+
if (!locked) setValue(option && !Array.isArray(option) ? option.value : null);
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
});
|
|
31
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { SelectInput, useField, useFormFields } from '@payloadcms/ui';
|
|
4
|
+
import { useEffect, useRef } from 'react';
|
|
5
|
+
import { isEditable } from '../../collections/experiments/editability.js';
|
|
6
|
+
import { isOpValid } from '../../targeting/ops.js';
|
|
7
|
+
import { WHO_COPY } from '../copy/who.js';
|
|
8
|
+
import { opLabel, opsFor, rowPath } from './targeting.model.js';
|
|
9
|
+
export function TargetingOpField({ path, field, readOnly }) {
|
|
10
|
+
const { value, setValue } = useField({
|
|
11
|
+
path
|
|
12
|
+
});
|
|
13
|
+
const row = rowPath(path);
|
|
14
|
+
const attribute = useFormFields(([fields])=>fields[`${row}.attribute`]?.value);
|
|
15
|
+
const status = useFormFields(([fields])=>fields.status?.value ?? 'draft');
|
|
16
|
+
const keys = field.admin?.custom?.attributeKeys ?? {};
|
|
17
|
+
const type = keys[attribute ?? '']?.type;
|
|
18
|
+
const previousType = useRef(type);
|
|
19
|
+
const locked = Boolean(readOnly || field.admin?.readOnly || !isEditable(status, 'targeting'));
|
|
20
|
+
useEffect(()=>{
|
|
21
|
+
const changed = previousType.current !== type;
|
|
22
|
+
previousType.current = type;
|
|
23
|
+
if (!locked && type && (changed || !isOpValid(type, value ?? ''))) setValue(opsFor(type)[0]);
|
|
24
|
+
}, [
|
|
25
|
+
type,
|
|
26
|
+
value,
|
|
27
|
+
locked,
|
|
28
|
+
setValue
|
|
29
|
+
]);
|
|
30
|
+
return /*#__PURE__*/ _jsx(SelectInput, {
|
|
31
|
+
path: path,
|
|
32
|
+
name: path,
|
|
33
|
+
label: WHO_COPY.operator,
|
|
34
|
+
required: field.required,
|
|
35
|
+
options: opsFor(type).map((op)=>({
|
|
36
|
+
label: opLabel(type, op),
|
|
37
|
+
value: op
|
|
38
|
+
})),
|
|
39
|
+
value: value ?? '',
|
|
40
|
+
readOnly: locked || !type,
|
|
41
|
+
onChange: (option)=>{
|
|
42
|
+
if (!locked) setValue(option && !Array.isArray(option) ? option.value : null);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { FieldError, SelectInput, TextInput, useField, useFormFields } from '@payloadcms/ui';
|
|
4
|
+
import { useEffect, useRef, useState } from 'react';
|
|
5
|
+
import { isEditable } from '../../collections/experiments/editability.js';
|
|
6
|
+
import { isOpValid } from '../../targeting/ops.js';
|
|
7
|
+
import { WHO_COPY } from '../copy/who.js';
|
|
8
|
+
import { coerceValue, emptyListHint, rowPath, valueInput } from './targeting.model.js';
|
|
9
|
+
// Preserve an unfinished comma list while the form receives its typed JSON value.
|
|
10
|
+
function CommaList({ path, value, setValue, readOnly, required }) {
|
|
11
|
+
const formatted = Array.isArray(value) ? value.join(', ') : String(value ?? '');
|
|
12
|
+
const [text, setText] = useState(formatted);
|
|
13
|
+
const lastWritten = useRef(undefined);
|
|
14
|
+
useEffect(()=>{
|
|
15
|
+
if (JSON.stringify(value) !== lastWritten.current) setText(formatted);
|
|
16
|
+
}, [
|
|
17
|
+
value,
|
|
18
|
+
formatted
|
|
19
|
+
]);
|
|
20
|
+
return /*#__PURE__*/ _jsx(TextInput, {
|
|
21
|
+
path: path,
|
|
22
|
+
label: WHO_COPY.value,
|
|
23
|
+
value: text,
|
|
24
|
+
readOnly: readOnly,
|
|
25
|
+
required: required,
|
|
26
|
+
onChange: (event)=>{
|
|
27
|
+
if (readOnly) return;
|
|
28
|
+
setText(event.target.value);
|
|
29
|
+
const next = coerceValue('string', 'in', event.target.value);
|
|
30
|
+
lastWritten.current = JSON.stringify(next);
|
|
31
|
+
setValue(next);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export function TargetingValueField({ path, field, readOnly }) {
|
|
36
|
+
const { value, setValue } = useField({
|
|
37
|
+
path
|
|
38
|
+
});
|
|
39
|
+
const row = rowPath(path);
|
|
40
|
+
const attribute = useFormFields(([fields])=>fields[`${row}.attribute`]?.value);
|
|
41
|
+
const op = useFormFields(([fields])=>fields[`${row}.op`]?.value);
|
|
42
|
+
const status = useFormFields(([fields])=>fields.status?.value ?? 'draft');
|
|
43
|
+
const keys = field.admin?.custom?.attributeKeys ?? {};
|
|
44
|
+
const definition = keys[attribute ?? ''];
|
|
45
|
+
const candidate = op ?? '';
|
|
46
|
+
const valid = Boolean(definition && isOpValid(definition.type, candidate));
|
|
47
|
+
const operator = definition && isOpValid(definition.type, candidate) ? candidate : 'eq';
|
|
48
|
+
const input = valueInput(definition, operator);
|
|
49
|
+
const locked = Boolean(readOnly || field.admin?.readOnly || !isEditable(status, 'targeting') || !valid);
|
|
50
|
+
const type = definition?.type;
|
|
51
|
+
useEffect(()=>{
|
|
52
|
+
if (locked || !type || !isOpValid(type, candidate) || value == null) return;
|
|
53
|
+
const next = coerceValue(type, candidate, value);
|
|
54
|
+
if (JSON.stringify(next) !== JSON.stringify(value)) setValue(next);
|
|
55
|
+
}, [
|
|
56
|
+
type,
|
|
57
|
+
candidate,
|
|
58
|
+
value,
|
|
59
|
+
locked,
|
|
60
|
+
setValue
|
|
61
|
+
]);
|
|
62
|
+
const write = (raw)=>{
|
|
63
|
+
if (!locked && definition && isOpValid(definition.type, operator)) setValue(coerceValue(definition.type, operator, raw));
|
|
64
|
+
};
|
|
65
|
+
const many = operator === 'in' || operator === 'nin';
|
|
66
|
+
const hint = emptyListHint(operator, value);
|
|
67
|
+
const props = {
|
|
68
|
+
path,
|
|
69
|
+
label: WHO_COPY.value,
|
|
70
|
+
readOnly: locked,
|
|
71
|
+
required: field.required
|
|
72
|
+
};
|
|
73
|
+
// position: relative anchors Payload's absolutely positioned FieldError tooltip to this field.
|
|
74
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
75
|
+
style: {
|
|
76
|
+
position: 'relative'
|
|
77
|
+
},
|
|
78
|
+
children: [
|
|
79
|
+
input.kind === 'select' || input.kind === 'multiselect' || input.kind === 'boolean' ? /*#__PURE__*/ _jsx(SelectInput, {
|
|
80
|
+
...props,
|
|
81
|
+
name: path,
|
|
82
|
+
hasMany: input.kind === 'multiselect',
|
|
83
|
+
options: input.kind === 'boolean' ? [
|
|
84
|
+
{
|
|
85
|
+
label: WHO_COPY.true,
|
|
86
|
+
value: 'true'
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
label: WHO_COPY.false,
|
|
90
|
+
value: 'false'
|
|
91
|
+
}
|
|
92
|
+
] : input.options,
|
|
93
|
+
value: many ? Array.isArray(value) ? value.map(String) : [] : value == null ? '' : String(value),
|
|
94
|
+
onChange: (option)=>write(Array.isArray(option) ? option.map((item)=>item.value) : option?.value)
|
|
95
|
+
}) : input.kind === 'number' ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
96
|
+
children: [
|
|
97
|
+
/*#__PURE__*/ _jsx("label", {
|
|
98
|
+
htmlFor: path,
|
|
99
|
+
children: WHO_COPY.value
|
|
100
|
+
}),
|
|
101
|
+
/*#__PURE__*/ _jsx("input", {
|
|
102
|
+
id: path,
|
|
103
|
+
name: path,
|
|
104
|
+
type: "number",
|
|
105
|
+
step: "any",
|
|
106
|
+
required: field.required,
|
|
107
|
+
readOnly: locked,
|
|
108
|
+
value: typeof value === 'number' || typeof value === 'string' ? value : '',
|
|
109
|
+
style: {
|
|
110
|
+
display: 'block',
|
|
111
|
+
color: 'var(--theme-text)',
|
|
112
|
+
background: 'var(--theme-input-bg)',
|
|
113
|
+
border: '1px solid var(--theme-elevation-200)',
|
|
114
|
+
padding: '0.5rem'
|
|
115
|
+
},
|
|
116
|
+
onChange: (event)=>write(event.target.value)
|
|
117
|
+
}),
|
|
118
|
+
/*#__PURE__*/ _jsx(FieldError, {
|
|
119
|
+
path: path
|
|
120
|
+
})
|
|
121
|
+
]
|
|
122
|
+
}) : many ? /*#__PURE__*/ _jsx(CommaList, {
|
|
123
|
+
...props,
|
|
124
|
+
value: value,
|
|
125
|
+
setValue: write
|
|
126
|
+
}, `${path}:${attribute}:${operator}`) : /*#__PURE__*/ _jsx(TextInput, {
|
|
127
|
+
...props,
|
|
128
|
+
value: typeof value === 'string' ? value : '',
|
|
129
|
+
onChange: (event)=>write(event.target.value)
|
|
130
|
+
}),
|
|
131
|
+
definition?.type === 'string' ? /*#__PURE__*/ _jsx("p", {
|
|
132
|
+
children: WHO_COPY.caseNote
|
|
133
|
+
}) : null,
|
|
134
|
+
many && input.kind === 'text' ? /*#__PURE__*/ _jsx("p", {
|
|
135
|
+
children: WHO_COPY.listNote
|
|
136
|
+
}) : null,
|
|
137
|
+
hint ? /*#__PURE__*/ _jsx("p", {
|
|
138
|
+
children: hint
|
|
139
|
+
}) : null
|
|
140
|
+
]
|
|
141
|
+
});
|
|
142
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { SerializedAttributeKeys } from '../../types/admin.js';
|
|
2
|
+
import type { TargetingOp } from '../../targeting/ops.js';
|
|
3
|
+
type Attribute = SerializedAttributeKeys[string];
|
|
4
|
+
export type AttributeType = Attribute['type'];
|
|
5
|
+
export declare function opsFor(type?: AttributeType): readonly TargetingOp[];
|
|
6
|
+
export declare function opLabel(type: AttributeType, op: TargetingOp): string;
|
|
7
|
+
export declare function valueInput(attribute?: Attribute, op?: TargetingOp): {
|
|
8
|
+
kind: 'text' | 'number' | 'boolean' | 'select' | 'multiselect';
|
|
9
|
+
options?: {
|
|
10
|
+
label: string;
|
|
11
|
+
value: string;
|
|
12
|
+
}[];
|
|
13
|
+
};
|
|
14
|
+
export declare function coerceValue(type: AttributeType, op: TargetingOp, raw: unknown): string | number | boolean | string[] | null;
|
|
15
|
+
export declare function rowPath(path: string): string;
|
|
16
|
+
export declare function emptyListHint(op: TargetingOp, value: unknown): string | undefined;
|
|
17
|
+
export declare function humaniseAttribute(key: string): string;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { TARGETING_OPS, isOpValid } from '../../targeting/ops.js';
|
|
2
|
+
import { NUMBER_OP_LABELS, STRING_OP_LABELS, WHO_COPY } from '../copy/who.js';
|
|
3
|
+
export function opsFor(type) {
|
|
4
|
+
return type ? TARGETING_OPS[type] : [];
|
|
5
|
+
}
|
|
6
|
+
export function opLabel(type, op) {
|
|
7
|
+
if (!isOpValid(type, op)) return '';
|
|
8
|
+
if (type === 'boolean') return STRING_OP_LABELS.eq;
|
|
9
|
+
return type === 'number' ? NUMBER_OP_LABELS[op] : STRING_OP_LABELS[op];
|
|
10
|
+
}
|
|
11
|
+
export function valueInput(attribute, op = 'eq') {
|
|
12
|
+
const many = op === 'in' || op === 'nin';
|
|
13
|
+
if (attribute?.values) return {
|
|
14
|
+
kind: many ? 'multiselect' : 'select',
|
|
15
|
+
options: attribute.values.map((value)=>({
|
|
16
|
+
label: value,
|
|
17
|
+
value
|
|
18
|
+
}))
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
kind: attribute?.type === 'number' ? 'number' : attribute?.type === 'boolean' ? 'boolean' : 'text'
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function coerceValue(type, op, raw) {
|
|
25
|
+
if (!isOpValid(type, op)) return null;
|
|
26
|
+
if (type === 'string') {
|
|
27
|
+
if (op === 'in' || op === 'nin') return (Array.isArray(raw) ? raw.map(String) : String(raw ?? '').split(',')).map((value)=>value.trim()).filter(Boolean);
|
|
28
|
+
return raw == null ? '' : String(raw);
|
|
29
|
+
}
|
|
30
|
+
if (type === 'number') {
|
|
31
|
+
if (raw == null || typeof raw === 'string' && !raw.trim()) return null;
|
|
32
|
+
const value = Number(raw);
|
|
33
|
+
return Number.isFinite(value) ? value : null;
|
|
34
|
+
}
|
|
35
|
+
return raw === true || raw === 'true' ? true : raw === false || raw === 'false' ? false : null;
|
|
36
|
+
}
|
|
37
|
+
export function rowPath(path) {
|
|
38
|
+
return path.slice(0, Math.max(0, path.lastIndexOf('.')));
|
|
39
|
+
}
|
|
40
|
+
export function emptyListHint(op, value) {
|
|
41
|
+
if (op !== 'in' && op !== 'nin') return undefined;
|
|
42
|
+
const list = coerceValue('string', op, value);
|
|
43
|
+
return Array.isArray(list) && list.length === 0 ? WHO_COPY.emptyListNote : undefined;
|
|
44
|
+
}
|
|
45
|
+
export function humaniseAttribute(key) {
|
|
46
|
+
const words = key.replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/[_-]+/g, ' ');
|
|
47
|
+
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
48
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare function useApi(): {
|
|
2
|
+
start: (id: import("../types/contracts.js").DocId) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").TransitionSuccess>>;
|
|
3
|
+
allocate: (id: import("../types/contracts.js").DocId, body: import("../types/endpoints.js").AllocateBody) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").TransitionSuccess>>;
|
|
4
|
+
addMetric: (id: import("../types/contracts.js").DocId, body: import("../types/endpoints.js").AddMetricBody) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").TransitionSuccess>>;
|
|
5
|
+
stop: (id: import("../types/contracts.js").DocId) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").TransitionSuccess>>;
|
|
6
|
+
decide: (id: import("../types/contracts.js").DocId, body: import("../types/endpoints.js").DecideBody) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").TransitionSuccess>>;
|
|
7
|
+
releaseHoldout: (id: import("../types/contracts.js").DocId) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").TransitionSuccess>>;
|
|
8
|
+
makeContent: (id: import("../types/contracts.js").DocId) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").MakeContentResult>>;
|
|
9
|
+
archive: (id: import("../types/contracts.js").DocId, body: import("../types/endpoints.js").ArchiveBody) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").TransitionSuccess>>;
|
|
10
|
+
preflight: (id: import("../types/contracts.js").DocId) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").PreflightResult>>;
|
|
11
|
+
previewToken: (id: import("../types/contracts.js").DocId, arm: string) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").PreviewTokenResult>>;
|
|
12
|
+
results: (id: import("../types/contracts.js").DocId, options?: {
|
|
13
|
+
phase?: number;
|
|
14
|
+
series?: boolean;
|
|
15
|
+
}) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").ResultsResponse>>;
|
|
16
|
+
recompute: (id: import("../types/contracts.js").DocId) => Promise<import("./api.js").ApiResult<{
|
|
17
|
+
snapshotId: string;
|
|
18
|
+
}>>;
|
|
19
|
+
summary: (ids: readonly import("../types/contracts.js").DocId[]) => Promise<import("./api.js").ApiResult<import("../types/endpoints.js").SummaryResponse>>;
|
|
20
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { useConfig } from '@payloadcms/ui';
|
|
4
|
+
import { createApi } from './api.js';
|
|
5
|
+
export function useApi() {
|
|
6
|
+
const { config } = useConfig();
|
|
7
|
+
return useMemo(()=>createApi(config.routes.api), [
|
|
8
|
+
config.routes.api
|
|
9
|
+
]);
|
|
10
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DocId } from '../types/contracts.js';
|
|
2
|
+
export declare function useLayerExperiments(layerId: DocId | null): {
|
|
3
|
+
used: number;
|
|
4
|
+
free: number;
|
|
5
|
+
rows: {
|
|
6
|
+
name: string;
|
|
7
|
+
status: import("../types/contracts.js").Status;
|
|
8
|
+
slices: number;
|
|
9
|
+
}[];
|
|
10
|
+
} | null;
|
|
11
|
+
export declare function useLayerUsage(layerId: DocId | null): {
|
|
12
|
+
name: string;
|
|
13
|
+
used: number;
|
|
14
|
+
free: number;
|
|
15
|
+
} | null;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useConfig } from '@payloadcms/ui';
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
import { layerUsage } from './layerUsage.model.js';
|
|
5
|
+
// Shared by the layer document and the allocation field; all requests use the editor's access.
|
|
6
|
+
export function useLayerExperiments(layerId) {
|
|
7
|
+
const { config } = useConfig();
|
|
8
|
+
const api = config.routes.api;
|
|
9
|
+
const id = layerId == null ? null : String(layerId);
|
|
10
|
+
const [state, setState] = useState(null);
|
|
11
|
+
useEffect(()=>{
|
|
12
|
+
setState(null);
|
|
13
|
+
if (id == null) return;
|
|
14
|
+
const controller = new AbortController();
|
|
15
|
+
async function load() {
|
|
16
|
+
const docs = [];
|
|
17
|
+
let page = 1;
|
|
18
|
+
while(!controller.signal.aborted){
|
|
19
|
+
const query = new URLSearchParams({
|
|
20
|
+
depth: '0',
|
|
21
|
+
limit: '100',
|
|
22
|
+
page: String(page),
|
|
23
|
+
'where[layer][equals]': id,
|
|
24
|
+
'where[status][in]': 'running,decided'
|
|
25
|
+
});
|
|
26
|
+
const response = await fetch(`${api}/xp-experiments?${query}`, {
|
|
27
|
+
credentials: 'include',
|
|
28
|
+
signal: controller.signal
|
|
29
|
+
});
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
if (!controller.signal.aborted) setState(null);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const data = await response.json();
|
|
35
|
+
docs.push(...data.docs);
|
|
36
|
+
if (!data.hasNextPage) break;
|
|
37
|
+
page++;
|
|
38
|
+
}
|
|
39
|
+
if (!controller.signal.aborted) setState({
|
|
40
|
+
api,
|
|
41
|
+
id: id,
|
|
42
|
+
usage: layerUsage(docs)
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
void load().catch(()=>{
|
|
46
|
+
if (!controller.signal.aborted) setState(null);
|
|
47
|
+
});
|
|
48
|
+
return ()=>controller.abort();
|
|
49
|
+
}, [
|
|
50
|
+
api,
|
|
51
|
+
id
|
|
52
|
+
]);
|
|
53
|
+
return id != null && state?.api === api && state.id === id ? state.usage : null;
|
|
54
|
+
}
|
|
55
|
+
export function useLayerUsage(layerId) {
|
|
56
|
+
const { config } = useConfig();
|
|
57
|
+
const api = config.routes.api;
|
|
58
|
+
const id = layerId == null ? null : String(layerId);
|
|
59
|
+
const usage = useLayerExperiments(layerId);
|
|
60
|
+
const [state, setState] = useState(null);
|
|
61
|
+
useEffect(()=>{
|
|
62
|
+
setState(null);
|
|
63
|
+
if (id == null) return;
|
|
64
|
+
const controller = new AbortController();
|
|
65
|
+
void fetch(`${api}/xp-layers/${encodeURIComponent(id)}?depth=0`, {
|
|
66
|
+
credentials: 'include',
|
|
67
|
+
signal: controller.signal
|
|
68
|
+
}).then(async (response)=>{
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
if (!controller.signal.aborted) setState(null);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const doc = await response.json();
|
|
74
|
+
if (!controller.signal.aborted) setState({
|
|
75
|
+
api,
|
|
76
|
+
id,
|
|
77
|
+
name: doc.name
|
|
78
|
+
});
|
|
79
|
+
}).catch(()=>{
|
|
80
|
+
if (!controller.signal.aborted) setState(null);
|
|
81
|
+
});
|
|
82
|
+
return ()=>controller.abort();
|
|
83
|
+
}, [
|
|
84
|
+
api,
|
|
85
|
+
id
|
|
86
|
+
]);
|
|
87
|
+
return usage && id != null && state?.api === api && state.id === id ? {
|
|
88
|
+
name: state.name,
|
|
89
|
+
used: usage.used,
|
|
90
|
+
free: usage.free
|
|
91
|
+
} : null;
|
|
92
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function ExperimentPageview(): null;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useContext, useEffect } from 'react';
|
|
3
|
+
import { sendExposure } from './beacon.js';
|
|
4
|
+
import { ExperimentsContext } from './context.js';
|
|
5
|
+
export function ExperimentPageview() {
|
|
6
|
+
const value = useContext(ExperimentsContext);
|
|
7
|
+
const pageviews = value?.pageviews;
|
|
8
|
+
const apiRoute = value?.apiRoute;
|
|
9
|
+
useEffect(()=>{
|
|
10
|
+
if (!pageviews || apiRoute === undefined) return;
|
|
11
|
+
// Read the path at commit time: on an App Router soft navigation the new page renders before the URL updates.
|
|
12
|
+
const pathname = window.location.pathname;
|
|
13
|
+
for (const { slug, phase, arm } of pageviews){
|
|
14
|
+
sendExposure(apiRoute, {
|
|
15
|
+
type: 'exposure',
|
|
16
|
+
experiment: slug,
|
|
17
|
+
phase,
|
|
18
|
+
arm,
|
|
19
|
+
policy: 'pageview'
|
|
20
|
+
}, pathname);
|
|
21
|
+
}
|
|
22
|
+
}, [
|
|
23
|
+
pageviews,
|
|
24
|
+
apiRoute
|
|
25
|
+
]);
|
|
26
|
+
return null;
|
|
27
|
+
}
|