@delmaredigital/payload-experiments 0.2.0-rc.7 → 0.3.0-rc.4
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 +202 -26
- package/dist/admin/PreviewLinks.d.ts +2 -1
- package/dist/admin/PreviewLinks.js +50 -21
- package/dist/admin/TestsNavLink.d.ts +3 -1
- package/dist/admin/TestsNavLink.js +2 -2
- package/dist/admin/actions/ConfirmDialog.d.ts +7 -4
- package/dist/admin/actions/ConfirmDialog.js +37 -82
- package/dist/admin/actions/DecideDialog.d.ts +3 -1
- package/dist/admin/actions/DecideDialog.js +30 -13
- package/dist/admin/actions/MakeContentResult.d.ts +13 -2
- package/dist/admin/actions/MakeContentResult.js +115 -16
- package/dist/admin/actions/actions.model.d.ts +2 -4
- package/dist/admin/actions/actions.model.js +2 -3
- package/dist/admin/allocation.model.js +3 -5
- package/dist/admin/api.d.ts +10 -4
- package/dist/admin/api.js +11 -7
- package/dist/admin/armSelect/ArmSelectField.js +14 -22
- package/dist/admin/arms/arms.model.d.ts +1 -21
- package/dist/admin/arms/arms.model.js +1 -153
- package/dist/admin/copy/actions.d.ts +39 -3
- package/dist/admin/copy/actions.js +40 -4
- package/dist/admin/copy/armSelect.js +5 -5
- package/dist/admin/copy/arms.d.ts +1 -6
- package/dist/admin/copy/arms.js +1 -16
- package/dist/admin/copy/audience.d.ts +2 -1
- package/dist/admin/copy/audience.js +3 -2
- package/dist/admin/copy/common.d.ts +13 -6
- package/dist/admin/copy/common.js +14 -8
- package/dist/admin/copy/goals.d.ts +21 -6
- package/dist/admin/copy/goals.js +27 -6
- package/dist/admin/copy/readiness.d.ts +22 -0
- package/dist/admin/copy/readiness.js +21 -0
- package/dist/admin/copy/recommendation.d.ts +0 -3
- package/dist/admin/copy/recommendation.js +0 -3
- package/dist/admin/copy/results.d.ts +43 -2
- package/dist/admin/copy/results.js +44 -3
- package/dist/admin/copy/review.d.ts +2 -2
- package/dist/admin/copy/review.js +2 -2
- package/dist/admin/copy/setting.d.ts +2 -0
- package/dist/admin/copy/setting.js +2 -0
- package/dist/admin/copy/surface.d.ts +7 -5
- package/dist/admin/copy/surface.js +9 -5
- package/dist/admin/copy/testPage.d.ts +10 -0
- package/dist/admin/copy/testPage.js +10 -0
- package/dist/admin/format/date.d.ts +2 -0
- package/dist/admin/format/date.js +10 -0
- package/dist/admin/paths.d.ts +2 -1
- package/dist/admin/paths.js +2 -1
- package/dist/admin/previewLinks.model.d.ts +4 -2
- package/dist/admin/previewLinks.model.js +8 -7
- package/dist/admin/results/CiChart.js +11 -21
- package/dist/admin/results/DataQualityPanel.d.ts +4 -2
- package/dist/admin/results/DataQualityPanel.js +30 -17
- package/dist/admin/results/DataTable.d.ts +16 -0
- package/dist/admin/results/DataTable.js +152 -0
- package/dist/admin/results/StatTile.d.ts +7 -0
- package/dist/admin/results/StatTile.js +22 -0
- package/dist/admin/results/VerdictHero.d.ts +13 -0
- package/dist/admin/results/{VerdictCard.js → VerdictHero.js} +15 -12
- package/dist/admin/results/legacy.d.ts +16 -0
- package/dist/admin/results/legacy.js +27 -0
- package/dist/admin/results/releasePages.model.d.ts +17 -0
- package/dist/admin/results/releasePages.model.js +10 -0
- package/dist/admin/results/results.css +64 -0
- package/dist/admin/results/results.model.d.ts +11 -7
- package/dist/admin/results/results.model.js +24 -7
- package/dist/admin/results/snapshot.d.ts +14 -0
- package/dist/admin/results/snapshot.js +3 -0
- package/dist/admin/surface/ResultsRoute.d.ts +1 -1
- package/dist/admin/surface/ResultsRoute.js +59 -34
- package/dist/admin/surface/SurfaceShell.d.ts +11 -2
- package/dist/admin/surface/SurfaceShell.js +49 -14
- package/dist/admin/surface/TestsHome.d.ts +6 -1
- package/dist/admin/surface/TestsHome.js +182 -114
- package/dist/admin/surface/TestsSurface.js +56 -16
- package/dist/admin/surface/goals/GoalForm.js +341 -308
- package/dist/admin/surface/goals/GoalsList.js +123 -89
- package/dist/admin/surface/goals/data.d.ts +8 -2
- package/dist/admin/surface/goals/data.js +24 -16
- package/dist/admin/surface/goals/goals.css +10 -13
- package/dist/admin/surface/home.model.d.ts +8 -8
- package/dist/admin/surface/home.model.js +23 -12
- package/dist/admin/surface/newTest/NewTest.js +9 -9
- package/dist/admin/surface/pageBlock/parts.js +31 -26
- package/dist/admin/surface/readiness/ActionDialogs.js +47 -19
- package/dist/admin/surface/readiness/Banner.d.ts +3 -0
- package/dist/admin/surface/readiness/Banner.js +58 -33
- package/dist/admin/surface/readiness/SaveStatus.d.ts +1 -0
- package/dist/admin/surface/readiness/SaveStatus.js +6 -3
- package/dist/admin/surface/readiness/readiness.model.d.ts +2 -0
- package/dist/admin/surface/readiness/readiness.model.js +25 -9
- package/dist/admin/surface/readiness/useTransition.d.ts +1 -0
- package/dist/admin/surface/readiness/useTransition.js +2 -1
- package/dist/admin/surface/results/ReleasePages.d.ts +14 -0
- package/dist/admin/surface/results/ReleasePages.js +116 -0
- package/dist/admin/surface/results/ResultsBody.d.ts +11 -1
- package/dist/admin/surface/results/ResultsBody.js +277 -105
- package/dist/admin/surface/results/ResultsHeader.d.ts +10 -0
- package/dist/admin/surface/results/ResultsHeader.js +177 -0
- package/dist/admin/surface/results/recommendation.model.d.ts +3 -2
- package/dist/admin/surface/results/recommendation.model.js +12 -9
- package/dist/admin/surface/results/useResults.d.ts +21 -3
- package/dist/admin/surface/results/useResults.js +2 -1
- package/dist/admin/surface/setting/parts.js +75 -88
- package/dist/admin/surface/setting/setting.css +3 -15
- package/dist/admin/surface/surface.css +61 -47
- package/dist/admin/surface/test/AdvancedSection.js +55 -48
- package/dist/admin/surface/test/GoalSection.js +46 -47
- package/dist/admin/surface/test/LengthSection.js +46 -34
- package/dist/admin/surface/test/Rail.d.ts +4 -0
- package/dist/admin/surface/test/Rail.js +83 -0
- package/dist/admin/surface/test/ReadinessBar.d.ts +5 -0
- package/dist/admin/surface/test/ReadinessBar.js +33 -0
- package/dist/admin/surface/test/Section.d.ts +1 -1
- package/dist/admin/surface/test/Section.js +36 -16
- package/dist/admin/surface/test/TargetingSection.js +168 -142
- package/dist/admin/surface/test/TestPage.d.ts +2 -1
- package/dist/admin/surface/test/TestPage.js +146 -68
- package/dist/admin/surface/test/VersionsSection.js +151 -120
- package/dist/admin/surface/test/fields.d.ts +11 -4
- package/dist/admin/surface/test/fields.js +84 -85
- package/dist/admin/surface/test/jumpToSection.d.ts +3 -0
- package/dist/admin/surface/test/jumpToSection.js +10 -0
- package/dist/admin/surface/test/pageParts.js +9 -3
- package/dist/admin/surface/test/testPage.css +81 -14
- package/dist/admin/surface/test/testPage.model.d.ts +2 -3
- package/dist/admin/surface/test/testPage.model.js +1 -21
- package/dist/admin/surface/useTestDocument.js +12 -5
- package/dist/admin/targeting/targeting.model.d.ts +1 -3
- package/dist/admin/targeting/targeting.model.js +3 -8
- package/dist/admin/theme.css +115 -36
- package/dist/admin/theme.d.ts +38 -0
- package/dist/admin/theme.js +38 -1
- package/dist/admin/useApi.d.ts +17 -2
- package/dist/admin/useLayerUsage.js +14 -23
- package/dist/admin/views/ExperimentsRootView.js +12 -2
- package/dist/admin/views/TestsNavLinkServer.d.ts +2 -0
- package/dist/admin/views/TestsNavLinkServer.js +43 -0
- package/dist/arms/copy.d.ts +6 -0
- package/dist/arms/copy.js +16 -0
- package/dist/arms/model.d.ts +23 -0
- package/dist/arms/model.js +173 -0
- package/dist/collections/experiments/config.js +25 -0
- package/dist/collections/experiments/lockHook.js +3 -1
- package/dist/collections/identities.js +6 -1
- package/dist/collections/metrics.js +7 -0
- package/dist/collections/metricsGuards.d.ts +9 -0
- package/dist/collections/metricsGuards.js +33 -0
- package/dist/contentDone.d.ts +4 -0
- package/dist/contentDone.js +5 -0
- package/dist/db/appends.d.ts +6 -2
- package/dist/db/appends.js +10 -4
- package/dist/db/identities.d.ts +8 -7
- package/dist/db/identities.js +9 -6
- package/dist/db/locks.d.ts +6 -0
- package/dist/db/locks.js +13 -1
- package/dist/db/metricUsage.d.ts +19 -0
- package/dist/db/metricUsage.js +54 -0
- package/dist/db/names.d.ts +5 -0
- package/dist/db/names.js +5 -0
- package/dist/db/pageOccupancy.d.ts +12 -0
- package/dist/db/pageOccupancy.js +45 -0
- package/dist/db/publishedContent.d.ts +10 -0
- package/dist/db/publishedContent.js +43 -0
- package/dist/db/results.d.ts +2 -0
- package/dist/db/results.js +6 -2
- package/dist/db/snapshots.d.ts +18 -6
- package/dist/db/snapshots.js +25 -8
- package/dist/db/sweep.js +11 -6
- package/dist/endpoints/index.js +19 -1
- package/dist/endpoints/results.js +5 -0
- package/dist/endpoints/sanitizeReadinessDetail.d.ts +1 -0
- package/dist/endpoints/sanitizeReadinessDetail.js +2 -0
- package/dist/endpoints/summary.js +20 -2
- package/dist/endpoints/transitions.d.ts +15 -0
- package/dist/endpoints/transitions.js +36 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +28 -0
- package/dist/exports/adminRsc.d.ts +1 -0
- package/dist/exports/adminRsc.js +1 -0
- package/dist/exports/index.d.ts +2 -1
- package/dist/exports/index.js +1 -0
- package/dist/exports/puck.d.ts +2 -1
- package/dist/exports/puck.js +1 -1
- package/dist/exports/puckEditor.d.ts +4 -1
- package/dist/exports/puckEditor.js +3 -1
- package/dist/exports/rsc.d.ts +6 -1
- package/dist/exports/rsc.js +2 -0
- package/dist/hooks/pageGuard.d.ts +5 -0
- package/dist/hooks/pageGuard.js +139 -0
- package/dist/lib/fetchAllDocs.d.ts +16 -0
- package/dist/lib/fetchAllDocs.js +56 -0
- package/dist/log.js +6 -3
- package/dist/manifest/compile.d.ts +5 -0
- package/dist/manifest/compile.js +5 -1
- package/dist/options.js +16 -0
- package/dist/plugin.js +30 -2
- package/dist/puck/ExperimentPicker.js +47 -16
- package/dist/puck/ExperimentSlot.d.ts +51 -1
- package/dist/puck/ExperimentSlot.js +23 -12
- package/dist/puck/copy.d.ts +6 -5
- package/dist/puck/copy.js +6 -5
- package/dist/puck/editor/CreateExperimentDrawer.js +46 -56
- package/dist/puck/editor/ExperimentSlotEditor.d.ts +52 -1
- package/dist/puck/editor/ExperimentSlotEditor.js +292 -137
- package/dist/puck/editor/ResultsReadout.d.ts +6 -0
- package/dist/puck/editor/ResultsReadout.js +99 -0
- package/dist/puck/editor/VersionBuilder.d.ts +34 -0
- package/dist/puck/editor/VersionBuilder.js +317 -0
- package/dist/puck/editor/armWrites.d.ts +7 -0
- package/dist/puck/editor/armWrites.js +71 -0
- package/dist/puck/editor/armsCache.d.ts +14 -0
- package/dist/puck/editor/armsCache.js +78 -0
- package/dist/puck/editor/copy.d.ts +33 -1
- package/dist/puck/editor/copy.js +45 -8
- package/dist/puck/editor/createTest.d.ts +1 -1
- package/dist/puck/editor/createTest.js +25 -14
- package/dist/puck/editor/editor.chrome.css +121 -0
- package/dist/puck/editor/editor.css +288 -204
- package/dist/puck/editor/editor.model.d.ts +4 -1
- package/dist/puck/editor/editor.model.js +10 -7
- package/dist/puck/editor/experimentOutlinePlugin.d.ts +3 -0
- package/dist/puck/editor/experimentOutlinePlugin.js +89 -0
- package/dist/puck/experimentSlotField.d.ts +2 -0
- package/dist/puck/experimentSlotField.js +11 -3
- package/dist/puck/hostContext.d.ts +10 -0
- package/dist/puck/hostContext.js +12 -0
- package/dist/puck/routes.d.ts +12 -0
- package/dist/puck/routes.js +11 -0
- package/dist/puck/slotConfig.d.ts +1 -1
- package/dist/puck/slotConfig.js +2 -2
- package/dist/results/aggregate.js +4 -3
- package/dist/results/analyse.d.ts +4 -1
- package/dist/results/analyse.js +27 -11
- package/dist/results/cuped.js +3 -2
- package/dist/results/identityCredit.d.ts +15 -7
- package/dist/results/identityCredit.js +32 -33
- package/dist/results/rolloutUnits.js +2 -1
- package/dist/results/snapshot.js +8 -2
- package/dist/results/srm.js +2 -1
- package/dist/sanitizeReadinessDetail.d.ts +7 -0
- package/dist/sanitizeReadinessDetail.js +63 -0
- package/dist/server/linkIdentity.d.ts +10 -1
- package/dist/server/linkIdentity.js +17 -15
- package/dist/server/metadata.d.ts +5 -0
- package/dist/server/metadata.js +18 -3
- package/dist/server/normaliseContentRelationships.d.ts +7 -0
- package/dist/server/normaliseContentRelationships.js +62 -0
- package/dist/server/recordContentChange.d.ts +1 -1
- package/dist/server/recordContentChange.js +17 -8
- package/dist/server/runResults.d.ts +3 -2
- package/dist/server/runResults.js +157 -35
- package/dist/targeting/values.d.ts +4 -0
- package/dist/targeting/values.js +10 -0
- package/dist/transitions/addMetric.js +8 -0
- package/dist/transitions/archive.js +2 -3
- package/dist/transitions/checkStart.d.ts +10 -3
- package/dist/transitions/checkStart.js +25 -0
- package/dist/transitions/contentFields.d.ts +7 -0
- package/dist/transitions/contentFields.js +20 -0
- package/dist/transitions/decide.js +22 -2
- package/dist/transitions/dismissContentEntry.d.ts +8 -0
- package/dist/transitions/dismissContentEntry.js +45 -0
- package/dist/transitions/makeContent.d.ts +5 -2
- package/dist/transitions/makeContent.js +217 -118
- package/dist/transitions/phaseRecord.d.ts +8 -2
- package/dist/transitions/placements.d.ts +3 -6
- package/dist/transitions/placements.js +12 -1
- package/dist/transitions/preflightData.d.ts +9 -1
- package/dist/transitions/preflightData.js +138 -20
- package/dist/transitions/releasePages.d.ts +8 -0
- package/dist/transitions/releasePages.js +26 -0
- package/dist/transitions/restoreContentEntry.d.ts +8 -0
- package/dist/transitions/restoreContentEntry.js +47 -0
- package/dist/transitions/start.js +62 -7
- package/dist/types/contracts.d.ts +24 -4
- package/dist/types/endpoints.d.ts +34 -3
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/reasons.d.ts +3 -0
- package/dist/types/reasons.js +1 -0
- package/dist/types/snapshot.d.ts +6 -1
- package/dist/ui/Banner.d.ts +2 -0
- package/dist/ui/Banner.js +2 -0
- package/dist/ui/Breadcrumb.d.ts +7 -0
- package/dist/ui/Breadcrumb.js +25 -0
- package/dist/ui/Button.d.ts +15 -0
- package/dist/ui/Button.js +32 -0
- package/dist/ui/Card.d.ts +12 -0
- package/dist/ui/Card.js +40 -0
- package/dist/ui/Checkbox.d.ts +2 -0
- package/dist/ui/Checkbox.js +2 -0
- package/dist/{admin/components → ui}/ChipsInput.d.ts +4 -3
- package/dist/{admin/components → ui}/ChipsInput.js +20 -12
- package/dist/ui/Dialog.d.ts +13 -0
- package/dist/ui/Dialog.js +85 -0
- package/dist/ui/EmptyState.d.ts +2 -0
- package/dist/ui/EmptyState.js +2 -0
- package/dist/ui/Feedback.d.ts +24 -0
- package/dist/ui/Feedback.js +40 -0
- package/dist/ui/Inputs.d.ts +34 -0
- package/dist/ui/Inputs.js +225 -0
- package/dist/ui/NumberInput.d.ts +2 -0
- package/dist/ui/NumberInput.js +2 -0
- package/dist/ui/Range.d.ts +2 -0
- package/dist/ui/Range.js +2 -0
- package/dist/ui/SearchInput.d.ts +2 -0
- package/dist/ui/SearchInput.js +2 -0
- package/dist/ui/SectionCard.d.ts +2 -0
- package/dist/ui/SectionCard.js +2 -0
- package/dist/ui/Select.d.ts +2 -0
- package/dist/ui/Select.js +2 -0
- package/dist/ui/StatusChip.d.ts +2 -0
- package/dist/ui/StatusChip.js +2 -0
- package/dist/ui/Tabs.d.ts +16 -0
- package/dist/ui/Tabs.js +61 -0
- package/dist/ui/TextInput.d.ts +2 -0
- package/dist/ui/TextInput.js +2 -0
- package/dist/ui/Textarea.d.ts +2 -0
- package/dist/ui/Textarea.js +2 -0
- package/dist/ui/Toggle.d.ts +2 -0
- package/dist/ui/Toggle.js +2 -0
- package/dist/ui/accent.d.ts +40 -0
- package/dist/ui/accent.js +139 -0
- package/dist/ui/index.d.ts +9 -0
- package/dist/ui/index.js +9 -0
- package/dist/ui/ui.css +92 -0
- package/package.json +5 -2
- package/dist/admin/components/chipsInput.css +0 -11
- package/dist/admin/results/ArmTable.d.ts +0 -8
- package/dist/admin/results/ArmTable.js +0 -113
- package/dist/admin/results/VerdictCard.d.ts +0 -9
- package/dist/admin/surface/goals/GoalsBreadcrumb.d.ts +0 -5
- package/dist/admin/surface/goals/GoalsBreadcrumb.js +0 -47
- package/dist/admin/surface/test/TestHeader.d.ts +0 -4
- package/dist/admin/surface/test/TestHeader.js +0 -70
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Arm, DocId, ExperimentKind, ExposurePolicy, FlagPoint, MetricSource, ResolvedOptions, Status, TargetingRule, UnitType } from '../types/contracts.js';
|
|
2
2
|
import type { ArmIssueCode, PreflightResult } from '../types/endpoints.js';
|
|
3
3
|
import type { BlockPlacement, FlagPlacement } from './placements.js';
|
|
4
|
-
export type ReadinessCode = ArmIssueCode | 'invalid-status' | 'no-planned-duration' | 'no-primary-metric' | 'no-reach-per-day' | 'page-no-targets' | 'page-user-unit' | 'flag-no-point' | 'flag-params-invalid' | 'flag-point-busy' | 'flag-placement-missing' | 'no-placements' | 'empty-slot' | 'layer-unit-mismatch' | 'layer-full' | 'unknown-metric-source' | 'unit-source-mismatch' | 'undeclared-targeting-key' | 'invalid-targeting-rule';
|
|
4
|
+
export type ReadinessCode = ArmIssueCode | 'invalid-status' | 'no-planned-duration' | 'no-primary-metric' | 'no-reach-per-day' | 'page-no-targets' | 'page-in-use' | 'page-target-unpublished' | 'page-targets-unchecked' | 'version-page-unavailable' | 'version-page-unchecked' | 'page-user-unit' | 'flag-no-point' | 'flag-params-invalid' | 'flag-point-busy' | 'flag-placement-missing' | 'no-placements' | 'empty-slot' | 'make-content-unsupported' | 'layer-unit-mismatch' | 'layer-full' | 'unknown-metric-source' | 'unit-source-mismatch' | 'block-path-rule' | 'undeclared-targeting-key' | 'invalid-targeting-rule';
|
|
5
5
|
export interface StartExperiment {
|
|
6
6
|
id: DocId;
|
|
7
7
|
slug: string;
|
|
@@ -38,6 +38,7 @@ export interface StartMetric {
|
|
|
38
38
|
id: DocId;
|
|
39
39
|
source: string;
|
|
40
40
|
}
|
|
41
|
+
export type PlacementFailure = 'placements-failed' | 'forbidden' | 'not-found';
|
|
41
42
|
export interface CheckStartInput {
|
|
42
43
|
experiment: StartExperiment;
|
|
43
44
|
metrics: StartMetric[];
|
|
@@ -45,9 +46,12 @@ export interface CheckStartInput {
|
|
|
45
46
|
flagPoints?: FlagPoint[];
|
|
46
47
|
placements?: BlockPlacement[];
|
|
47
48
|
flagPlacements?: FlagPlacement[];
|
|
48
|
-
placementError?:
|
|
49
|
-
flagPlacementError?:
|
|
49
|
+
placementError?: PlacementFailure;
|
|
50
|
+
flagPlacementError?: PlacementFailure;
|
|
50
51
|
flagPointBusy?: boolean;
|
|
52
|
+
unpublishedPageTargets?: string[];
|
|
53
|
+
pageTargetsUnchecked?: boolean;
|
|
54
|
+
pageTestsInUse?: Pick<StartExperiment, 'id' | 'arms'>[];
|
|
51
55
|
layer?: {
|
|
52
56
|
unitType: UnitType;
|
|
53
57
|
usedSlices: number[];
|
|
@@ -58,5 +62,8 @@ export declare const ARM_CHECK_ORDER: readonly ArmIssueCode[];
|
|
|
58
62
|
export declare const relationshipId: (value: DocId | {
|
|
59
63
|
id: DocId;
|
|
60
64
|
} | null | undefined) => DocId | undefined;
|
|
65
|
+
/** Every path the runtime would match for these arms (pageRewrite, experimentMetadata): any string
|
|
66
|
+
* pageTarget, holdout included, normalised the same way ('' is '/'). Occupancy and lock keys use this. */
|
|
67
|
+
export declare const pageTargetPaths: (arms: readonly Arm[]) => string[];
|
|
61
68
|
/** Pure Review/Start predicates in refusal order. Inapplicable checks pass. */
|
|
62
69
|
export declare function checkStart(input: CheckStartInput): PreflightResult['checks'];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { checkArms } from '../collections/experiments/armInvariants.js';
|
|
2
|
+
import { normalisePath } from '../runtime/normalisePath.js';
|
|
2
3
|
import { isOpValid } from '../targeting/ops.js';
|
|
3
4
|
export const ARM_CHECK_ORDER = [
|
|
4
5
|
'weights-total',
|
|
@@ -13,6 +14,10 @@ export const ARM_CHECK_ORDER = [
|
|
|
13
14
|
'duplicate-slot'
|
|
14
15
|
];
|
|
15
16
|
export const relationshipId = (value)=>value == null ? undefined : typeof value === 'object' ? value.id : value;
|
|
17
|
+
/** Every path the runtime would match for these arms (pageRewrite, experimentMetadata): any string
|
|
18
|
+
* pageTarget, holdout included, normalised the same way ('' is '/'). Occupancy and lock keys use this. */ export const pageTargetPaths = (arms)=>[
|
|
19
|
+
...new Set(arms.filter((arm)=>typeof arm.pageTarget === 'string').map((arm)=>normalisePath(arm.pageTarget)))
|
|
20
|
+
].sort();
|
|
16
21
|
const positive = (value)=>typeof value === 'number' && Number.isFinite(value) && value > 0;
|
|
17
22
|
function invalidParams(arms, point) {
|
|
18
23
|
return arms.flatMap((arm)=>{
|
|
@@ -56,6 +61,14 @@ function invalidParams(arms, point) {
|
|
|
56
61
|
'viewport'
|
|
57
62
|
].includes(e.exposurePolicy) || positive(e.analysis?.reachPerDay));
|
|
58
63
|
add('page-no-targets', e.kind !== 'page' || e.arms.filter((arm)=>arm.kind !== 'holdout').every((arm)=>typeof arm.pageTarget === 'string' && arm.pageTarget.trim().length > 0));
|
|
64
|
+
const occupiedPaths = new Set((input.pageTestsInUse ?? []).filter((other)=>String(other.id) !== String(e.id)).flatMap((other)=>pageTargetPaths(other.arms)));
|
|
65
|
+
const sharedPaths = e.kind === 'page' ? pageTargetPaths(e.arms).filter((path)=>occupiedPaths.has(path)) : [];
|
|
66
|
+
add('page-in-use', sharedPaths.length === 0, {
|
|
67
|
+
paths: sharedPaths
|
|
68
|
+
});
|
|
69
|
+
add('page-target-unpublished', !input.unpublishedPageTargets?.length, input.unpublishedPageTargets?.[0]);
|
|
70
|
+
add('page-targets-unchecked', true);
|
|
71
|
+
if (input.pageTargetsUnchecked) checks[checks.length - 1].warning = true;
|
|
59
72
|
add('page-user-unit', e.kind !== 'page' || e.unitType === 'visitor');
|
|
60
73
|
const point = input.flagPoints?.find((point)=>point.id === e.flagPoint);
|
|
61
74
|
add('flag-no-point', e.kind !== 'flag' || !!point);
|
|
@@ -72,6 +85,12 @@ function invalidParams(arms, point) {
|
|
|
72
85
|
add('no-placements', e.kind !== 'block' || !input.placementError && placements.length > 0, input.placementError);
|
|
73
86
|
const empty = placements.filter((placement)=>e.arms.some((arm)=>arm.kind !== 'holdout' && (!arm.slot || !(placement.slots[arm.slot] > 0))));
|
|
74
87
|
add('empty-slot', e.kind !== 'block' || empty.length === 0, empty);
|
|
88
|
+
add('make-content-unsupported', true);
|
|
89
|
+
const localized = placements.find((placement)=>placement.localized);
|
|
90
|
+
if (localized) Object.assign(checks[checks.length - 1], {
|
|
91
|
+
warning: true,
|
|
92
|
+
detail: localized.title
|
|
93
|
+
});
|
|
75
94
|
add('layer-unit-mismatch', e.layer == null || input.layer?.unitType === e.unitType);
|
|
76
95
|
const free = 100 - new Set(input.layer?.usedSlices).size;
|
|
77
96
|
add('layer-full', e.layer == null || !!input.layer && free >= e.allocation, {
|
|
@@ -82,6 +101,12 @@ function invalidParams(arms, point) {
|
|
|
82
101
|
add('unknown-metric-source', unknown.length === 0, unknown);
|
|
83
102
|
const mismatches = input.metrics.filter((metric)=>!metric.source.startsWith('events:') && e.unitType === 'user' && input.metricSources?.some((source)=>source.id === metric.source && source.keyedBy === 'visitor'));
|
|
84
103
|
add('unit-source-mismatch', mismatches.length === 0, mismatches);
|
|
104
|
+
const pathRules = (e.targeting ?? []).flatMap((rule, index)=>rule.attribute === 'path' ? [
|
|
105
|
+
index
|
|
106
|
+
] : []);
|
|
107
|
+
add('block-path-rule', e.kind !== 'block' || pathRules.length === 0, {
|
|
108
|
+
rules: pathRules
|
|
109
|
+
});
|
|
85
110
|
const undeclared = (e.targeting ?? []).filter((rule)=>!Object.hasOwn(input.attributeKeys ?? {}, rule.attribute));
|
|
86
111
|
add('undeclared-targeting-key', undeclared.length === 0, {
|
|
87
112
|
message: 'Targeting uses an undeclared attribute.',
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Block, Field, SanitizedConfig } from 'payload';
|
|
2
|
+
/** Payload keeps both inline definitions and slug references in blockReferences. */
|
|
3
|
+
export declare function contentBlocks(field: Extract<Field, {
|
|
4
|
+
type: 'blocks';
|
|
5
|
+
}>, blocks?: Block[]): Block[];
|
|
6
|
+
/** Any localized data makes a full-document publish unsafe in Payload 3.90. */
|
|
7
|
+
export declare function hasLocalizedFields(fields: Field[], config: Pick<SanitizedConfig, 'localization' | 'blocks'>, seen?: Set<Field>): boolean;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Payload keeps both inline definitions and slug references in blockReferences. */ export function contentBlocks(field, blocks = []) {
|
|
2
|
+
return [
|
|
3
|
+
...field.blocks,
|
|
4
|
+
...(field.blockReferences ?? []).flatMap((reference)=>typeof reference === 'string' ? blocks.filter((block)=>block.slug === reference) : [
|
|
5
|
+
reference
|
|
6
|
+
])
|
|
7
|
+
];
|
|
8
|
+
}
|
|
9
|
+
/** Any localized data makes a full-document publish unsafe in Payload 3.90. */ export function hasLocalizedFields(fields, config, seen = new Set()) {
|
|
10
|
+
if (!config.localization) return false;
|
|
11
|
+
const blocks = config.blocks ?? [];
|
|
12
|
+
return fields.some((field)=>{
|
|
13
|
+
if (seen.has(field)) return false;
|
|
14
|
+
const branch = new Set(seen).add(field);
|
|
15
|
+
if ('localized' in field && field.localized) return true;
|
|
16
|
+
if ('tabs' in field && field.tabs.some((tab)=>tab.localized || hasLocalizedFields(tab.fields, config, branch))) return true;
|
|
17
|
+
if ('fields' in field && hasLocalizedFields(field.fields, config, branch)) return true;
|
|
18
|
+
return field.type === 'blocks' && contentBlocks(field, blocks).some((block)=>hasLocalizedFields(block.fields, config, branch));
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { DEFAULT_SEQUENTIAL_TUNING_PARAMETER } from '../constants.js';
|
|
1
2
|
import { reclaimSlices } from '../db/slices.js';
|
|
2
3
|
import { decideGuard } from '../results/decideGuard.js';
|
|
3
|
-
import {
|
|
4
|
+
import { lockPagePaths } from '../db/locks.js';
|
|
5
|
+
import { routedPageTests } from '../db/pageOccupancy.js';
|
|
6
|
+
import { pageTargetPaths, relationshipId } from './checkStart.js';
|
|
4
7
|
import { expectedConfirm, matchesConfirm, storedWinnerLabel } from './confirmPhrases.js';
|
|
5
8
|
import { withTransition } from './kernel.js';
|
|
6
9
|
import { readPhaseRecord, setPhaseEndedAt, writePhaseRecord } from './phaseRecord.js';
|
|
@@ -44,6 +47,19 @@ export async function decide(req, { experimentId, winnerArm, note, confirmation
|
|
|
44
47
|
}
|
|
45
48
|
const test = await readPhaseRecord(req, experimentId, current.phase);
|
|
46
49
|
if (test.phaseKind !== 'test') refuse('invalid-status');
|
|
50
|
+
if (current.kind === 'page' && current.status === 'stopped') {
|
|
51
|
+
// Deciding a stopped page test restores its routing: recheck page occupancy under the
|
|
52
|
+
// sorted path locks Start takes, so another test that started on these pages meanwhile wins.
|
|
53
|
+
// The frozen test-round arms are what decide restores; the stopped document's arms may have been edited.
|
|
54
|
+
const mine = pageTargetPaths(test.arms);
|
|
55
|
+
await lockPagePaths(exec, mine);
|
|
56
|
+
// Other tests' routed (frozen-round) arms, as the runtime matches them.
|
|
57
|
+
const occupied = new Set((await routedPageTests(exec)).filter((other)=>String(other.id) !== String(experimentId)).flatMap((other)=>pageTargetPaths(other.arms)));
|
|
58
|
+
const shared = mine.filter((path)=>occupied.has(path));
|
|
59
|
+
if (shared.length) refuse('page-in-use', {
|
|
60
|
+
paths: shared
|
|
61
|
+
});
|
|
62
|
+
}
|
|
47
63
|
const winner = test.arms.find((arm)=>arm.key === winnerArm && arm.kind !== 'holdout');
|
|
48
64
|
if (!winner) refuse('invalid-winner');
|
|
49
65
|
// §7.8: decision snapshot lookup serves the authorised transition.
|
|
@@ -112,7 +128,11 @@ export async function decide(req, { experimentId, winnerArm, note, confirmation
|
|
|
112
128
|
allocationChanges: [],
|
|
113
129
|
targeting: test.targeting,
|
|
114
130
|
metricDefinitions: test.metricDefinitions,
|
|
115
|
-
analysis:
|
|
131
|
+
analysis: {
|
|
132
|
+
...test.analysis,
|
|
133
|
+
// Older test phases may predate freezing the effective tuning value.
|
|
134
|
+
sequentialTuningParameter: test.analysis?.sequentialTuningParameter ?? DEFAULT_SEQUENTIAL_TUNING_PARAMETER
|
|
135
|
+
},
|
|
116
136
|
contentChanges: []
|
|
117
137
|
});
|
|
118
138
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PayloadRequest } from 'payload';
|
|
2
|
+
import type { DocId } from '../types/contracts.js';
|
|
3
|
+
import type { DismissContentEntryBody } from '../types/endpoints.js';
|
|
4
|
+
export declare function dismissContentEntry(req: PayloadRequest, { experimentId, collection, id }: DismissContentEntryBody & {
|
|
5
|
+
experimentId: DocId;
|
|
6
|
+
}): Promise<{
|
|
7
|
+
doc: import("payload").JsonObject & import("payload").TypeWithID;
|
|
8
|
+
}>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { relationshipId } from './checkStart.js';
|
|
2
|
+
import { withTransition } from './kernel.js';
|
|
3
|
+
import { readExperiment, refuse, updateExperiment } from './start.js';
|
|
4
|
+
export async function dismissContentEntry(req, { experimentId, collection, id }) {
|
|
5
|
+
const before = await readExperiment(req, experimentId);
|
|
6
|
+
// Only the audit report changes; assignment and the manifest stay the same.
|
|
7
|
+
const { result: doc } = await withTransition(req, {
|
|
8
|
+
experimentId,
|
|
9
|
+
layerId: relationshipId(before.layer),
|
|
10
|
+
flagPointId: before.kind === 'flag' ? before.flagPoint : undefined,
|
|
11
|
+
bump: false
|
|
12
|
+
}, async ({ req })=>{
|
|
13
|
+
const current = await readExperiment(req, experimentId);
|
|
14
|
+
const decision = current.decision;
|
|
15
|
+
const report = decision?.contentReport;
|
|
16
|
+
const index = Array.isArray(report?.docs) ? report.docs.findIndex((entry)=>entry.collection === collection && String(entry.id) === String(id)) : -1;
|
|
17
|
+
const entry = report?.docs?.[index];
|
|
18
|
+
if (current.status !== 'decided' || !decision?.winnerArm || decision.discardedAt) refuse('not-decided');
|
|
19
|
+
if (!entry) refuse('entry-not-found');
|
|
20
|
+
if (entry.ok !== false) refuse('entry-ok');
|
|
21
|
+
if (entry.dismissedAt) refuse('entry-dismissed');
|
|
22
|
+
const docs = report.docs.map((entry, i)=>i === index ? {
|
|
23
|
+
...entry,
|
|
24
|
+
dismissedAt: new Date().toISOString(),
|
|
25
|
+
dismissedBy: req.user?.id == null ? null : String(req.user.id)
|
|
26
|
+
} : entry);
|
|
27
|
+
return updateExperiment(req, experimentId, {
|
|
28
|
+
decision: {
|
|
29
|
+
...decision,
|
|
30
|
+
contentReport: {
|
|
31
|
+
...report,
|
|
32
|
+
docs
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
req.payload.logger.info({
|
|
38
|
+
experimentId,
|
|
39
|
+
collection,
|
|
40
|
+
id
|
|
41
|
+
}, 'Experiment content entry dismissed');
|
|
42
|
+
return {
|
|
43
|
+
doc
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { type PayloadRequest } from 'payload';
|
|
2
|
+
import type { ContentReportReason } from '../types/reasons.js';
|
|
2
3
|
import type { DocId, ResolvedOptions } from '../types/contracts.js';
|
|
3
4
|
type ContentDoc = {
|
|
4
5
|
collection: string;
|
|
5
6
|
id: DocId;
|
|
6
7
|
ok: boolean;
|
|
7
|
-
detail?:
|
|
8
|
-
error?:
|
|
8
|
+
detail?: ContentReportReason;
|
|
9
|
+
error?: ContentReportReason;
|
|
10
|
+
dismissedAt?: string;
|
|
11
|
+
dismissedBy?: string | null;
|
|
9
12
|
};
|
|
10
13
|
export declare function makeContent<TUser extends {
|
|
11
14
|
id: DocId;
|