@delmaredigital/payload-experiments 0.3.0-rc.1 → 0.4.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +172 -21
- 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 +47 -26
- package/dist/admin/TestsNavLink.d.ts +3 -1
- package/dist/admin/TestsNavLink.js +2 -2
- 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 +11 -2
- package/dist/admin/api.js +20 -2
- 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/blocks/BlockPanel.d.ts +4 -0
- package/dist/admin/blocks/BlockPanel.js +153 -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 +43 -0
- package/dist/admin/blocks/formApi.js +153 -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 +7 -0
- package/dist/admin/blocks/statusCache.js +83 -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/arms.d.ts +1 -6
- package/dist/admin/copy/arms.js +1 -16
- package/dist/admin/copy/blocks.d.ts +27 -0
- package/dist/admin/copy/blocks.js +38 -0
- 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 +26 -0
- package/dist/admin/copy/pages.js +26 -0
- package/dist/admin/copy/readiness.d.ts +25 -3
- package/dist/admin/copy/readiness.js +25 -3
- package/dist/admin/copy/results.d.ts +3 -0
- package/dist/admin/copy/results.js +4 -1
- package/dist/admin/copy/review.d.ts +1 -1
- package/dist/admin/copy/review.js +1 -1
- package/dist/admin/copy/surface.d.ts +0 -1
- package/dist/admin/copy/surface.js +0 -1
- package/dist/admin/copy/testPage.d.ts +4 -0
- package/dist/admin/copy/testPage.js +4 -0
- package/dist/admin/goals/XpGoalField.d.ts +2 -0
- package/dist/admin/goals/XpGoalField.js +152 -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 +8 -1
- package/dist/admin/paths.js +8 -1
- 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/previewLinks.model.d.ts +2 -0
- package/dist/admin/previewLinks.model.js +3 -0
- package/dist/admin/results/DataQualityPanel.js +4 -1
- package/dist/admin/results/legacy.d.ts +16 -0
- package/dist/admin/results/legacy.js +27 -0
- package/dist/admin/results/results.model.d.ts +9 -6
- package/dist/admin/results/results.model.js +22 -9
- package/dist/admin/results/snapshot.d.ts +14 -0
- package/dist/admin/results/snapshot.js +3 -0
- package/dist/admin/surface/TestsHome.js +43 -15
- package/dist/admin/surface/TestsSurface.js +5 -1
- package/dist/admin/surface/contracts.d.ts +6 -5
- package/dist/admin/surface/goals/GoalForm.js +67 -34
- package/dist/admin/surface/goals/GoalsList.js +3 -3
- package/dist/admin/surface/goals/data.js +19 -16
- 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 +2 -1
- package/dist/admin/surface/home.model.js +8 -4
- package/dist/admin/surface/newTest/NewTest.js +58 -17
- package/dist/admin/surface/newTest/PageWizard.d.ts +7 -0
- package/dist/admin/surface/newTest/PageWizard.js +209 -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/PagePicker.d.ts +10 -0
- package/dist/admin/surface/pagePicker/PagePicker.js +172 -0
- package/dist/admin/surface/pagePicker/pagePicker.model.d.ts +25 -0
- package/dist/admin/surface/pagePicker/pagePicker.model.js +25 -0
- package/dist/admin/surface/readiness/ActionDialogs.js +103 -69
- 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 +95 -6
- 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 +23 -3
- package/dist/admin/surface/results/ResultsHeader.js +2 -5
- package/dist/admin/surface/results/recommendation.model.d.ts +3 -2
- package/dist/admin/surface/results/recommendation.model.js +10 -3
- package/dist/admin/surface/results/useResults.d.ts +10 -1
- package/dist/admin/surface/surface.css +0 -1
- package/dist/admin/surface/test/AdvancedSection.js +111 -104
- package/dist/admin/surface/test/GoalSection.js +97 -22
- 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 +2 -1
- 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 +79 -2
- package/dist/admin/surface/test/testPage.css +8 -1
- 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/testDocument.model.d.ts +1 -0
- package/dist/admin/surface/testDocument.model.js +2 -1
- package/dist/admin/surface/useTestDocument.d.ts +11 -2
- package/dist/admin/surface/useTestDocument.js +55 -16
- package/dist/admin/useApi.d.ts +11 -2
- package/dist/admin/useApi.js +3 -5
- package/dist/admin/useLayerUsage.js +14 -23
- package/dist/admin/views/ExperimentsRootView.js +58 -16
- 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/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 +12 -2
- 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 +12 -2
- package/dist/collections/phases.js +1 -0
- package/dist/collections/serialize.js +7 -0
- package/dist/db/appends.d.ts +6 -2
- package/dist/db/appends.js +10 -4
- 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 +14 -0
- package/dist/db/locks.js +36 -1
- 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/pageOccupancy.d.ts +12 -0
- package/dist/db/pageOccupancy.js +45 -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 +9 -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 +1 -0
- package/dist/exports/adminRsc.js +1 -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 +5 -2
- package/dist/exports/puck.js +3 -1
- package/dist/exports/puckEditor.d.ts +6 -1
- package/dist/exports/puckEditor.js +4 -1
- package/dist/exports/rsc.d.ts +4 -0
- package/dist/exports/rsc.js +2 -0
- package/dist/fields/xpGoal.d.ts +3 -0
- package/dist/fields/xpGoal.js +28 -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/hooks/pageGuard.d.ts +5 -1
- package/dist/hooks/pageGuard.js +160 -80
- package/dist/lib/fetchAllDocs.d.ts +16 -0
- package/dist/lib/fetchAllDocs.js +56 -0
- package/dist/onInit.js +29 -0
- package/dist/options.d.ts +2 -0
- package/dist/options.js +43 -10
- package/dist/plugin.js +45 -28
- 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 -4
- package/dist/puck/copy.js +6 -4
- 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 +48 -161
- package/dist/puck/editor/editor.css +48 -161
- package/dist/puck/editor/editor.model.d.ts +8 -3
- package/dist/puck/editor/editor.model.js +16 -10
- package/dist/puck/editor/experimentOutlinePlugin.d.ts +3 -0
- package/dist/puck/editor/experimentOutlinePlugin.js +89 -0
- 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 +134 -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/integration.d.ts +6 -0
- package/dist/puck/integration.js +21 -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/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 +51 -1
- package/dist/server/metadata.js +9 -2
- package/dist/server/normaliseContentRelationships.d.ts +7 -0
- package/dist/server/normaliseContentRelationships.js +62 -0
- package/dist/server/pageKindSeoContract.d.ts +27 -0
- package/dist/server/pageKindSeoContract.js +80 -0
- package/dist/server/recordContentChange.d.ts +1 -1
- package/dist/server/recordContentChange.js +72 -13
- package/dist/server/resolveExperimentBlocks.d.ts +5 -0
- package/dist/server/resolveExperimentBlocks.js +54 -0
- package/dist/server/runResults.js +92 -19
- 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 +17 -1
- package/dist/transitions/checkStart.js +99 -7
- package/dist/transitions/decide.js +40 -1
- 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 +10 -6
- package/dist/transitions/placements.js +156 -2
- package/dist/transitions/preflightData.d.ts +8 -0
- package/dist/transitions/preflightData.js +248 -36
- package/dist/transitions/resolveCaps.d.ts +1 -0
- package/dist/transitions/resolveCaps.js +6 -2
- package/dist/transitions/start.js +77 -3
- package/dist/types/admin.d.ts +1 -1
- package/dist/types/assignments.d.ts +1 -0
- package/dist/types/contracts.d.ts +31 -10
- 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/Tabs.d.ts +4 -2
- package/dist/ui/Tabs.js +33 -26
- package/dist/ui/accent.d.ts +22 -3
- package/dist/ui/accent.js +74 -16
- package/package.json +12 -2
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { createUsePuck, walkTree } from '@puckeditor/core';
|
|
4
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
5
|
+
import { EXPERIMENT_SLOT_TYPE } from '../../types/puck.js';
|
|
6
|
+
import { useArms } from './armsCache.js';
|
|
7
|
+
const usePuckSelector = createUsePuck();
|
|
8
|
+
const slots = [
|
|
9
|
+
'control',
|
|
10
|
+
'variantA',
|
|
11
|
+
'variantB',
|
|
12
|
+
'variantC'
|
|
13
|
+
];
|
|
14
|
+
// Puck 0.24.0 dist/chunk-DQXMVPVM.mjs:8040 (item attribute), 8126–8135
|
|
15
|
+
// (direct zone containers). Zones have no stable attribute; avoid the CSS-module hash.
|
|
16
|
+
const zone = 'div[class*="Layer-zones"]';
|
|
17
|
+
const chain = (count)=>Array(count).fill(zone).join(' ~ ');
|
|
18
|
+
function SlotRules({ item: [id, testId, emptySlots] }) {
|
|
19
|
+
const known = useArms(testId ?? undefined);
|
|
20
|
+
const [mounted, setMounted] = useState(false);
|
|
21
|
+
useEffect(()=>{
|
|
22
|
+
setMounted(true);
|
|
23
|
+
}, []);
|
|
24
|
+
// CSS.escape is a browser API: SSR and the first hydration render emit no rules.
|
|
25
|
+
if (!mounted) return null;
|
|
26
|
+
const rules = slots.flatMap((slot, index)=>{
|
|
27
|
+
// Original always remains available. Unknown bound tests must reveal every zone.
|
|
28
|
+
if (slot === 'control' || testId !== null && (!known || known.arms.includes(slot))) return [];
|
|
29
|
+
if (!emptySlots[index]) return [];
|
|
30
|
+
// Exactly the nth zone sibling: the positive chain matches n onward and the
|
|
31
|
+
// negated chain excludes n+1 onward. Unlike nth-child, DropLine siblings do not count.
|
|
32
|
+
const selector = `li[data-puck-layer-tree-id="${CSS.escape(id)}"] > ${chain(index + 1)}:not(${chain(index + 2)})`;
|
|
33
|
+
return [
|
|
34
|
+
`${selector} { display: none; }`
|
|
35
|
+
];
|
|
36
|
+
}).join('\n');
|
|
37
|
+
return /*#__PURE__*/ _jsx("style", {
|
|
38
|
+
"data-xp-outline": id,
|
|
39
|
+
children: rules
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function ExperimentOutline({ children }) {
|
|
43
|
+
// Public selector API in Puck 0.24, rather than the deprecated usePuck hook.
|
|
44
|
+
const key = usePuckSelector((state)=>{
|
|
45
|
+
const { data } = state.appState;
|
|
46
|
+
const items = new Map();
|
|
47
|
+
const collect = (content)=>{
|
|
48
|
+
for (const item of content)if (item.type === EXPERIMENT_SLOT_TYPE) {
|
|
49
|
+
const testId = item.props.experiment?.id;
|
|
50
|
+
items.set(item.props.id, [
|
|
51
|
+
item.props.id,
|
|
52
|
+
testId === undefined ? null : String(testId),
|
|
53
|
+
slots.map((slot)=>{
|
|
54
|
+
const content = item.props[slot];
|
|
55
|
+
return !Array.isArray(content) || content.length === 0;
|
|
56
|
+
})
|
|
57
|
+
]);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
walkTree(data, state.config, (content)=>{
|
|
61
|
+
collect(content);
|
|
62
|
+
});
|
|
63
|
+
// walkTree visits slots inside legacy zones, but does not call back on the zone itself.
|
|
64
|
+
for (const content of Object.values(data.zones ?? {}))collect(content);
|
|
65
|
+
return JSON.stringify([
|
|
66
|
+
...items.values()
|
|
67
|
+
]);
|
|
68
|
+
});
|
|
69
|
+
// Only binding/emptiness changes rebuild the rules, not edits to other page fields.
|
|
70
|
+
const rules = useMemo(()=>JSON.parse(key).map((item)=>/*#__PURE__*/ _jsx(SlotRules, {
|
|
71
|
+
item: item
|
|
72
|
+
}, item[0])), [
|
|
73
|
+
key
|
|
74
|
+
]);
|
|
75
|
+
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
76
|
+
children: [
|
|
77
|
+
children,
|
|
78
|
+
rules
|
|
79
|
+
]
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/** Add to Puck's plugins so its outline wrapper composes with the host's overrides. */ export function experimentOutlinePlugin() {
|
|
83
|
+
return {
|
|
84
|
+
name: 'experiment-outline',
|
|
85
|
+
overrides: {
|
|
86
|
+
outline: ExperimentOutline
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ExperimentsPuckIntegration } from '../../types/puck.js';
|
|
2
|
+
import type { PuckRoutes } from '../routes.js';
|
|
3
|
+
/**
|
|
4
|
+
* Call once at module scope in a 'use client' module. A new object per render
|
|
5
|
+
* rebuilds Puck's config and plugin list.
|
|
6
|
+
*/
|
|
7
|
+
export declare function experimentsPuckEditor({ routes, accent }?: {
|
|
8
|
+
routes?: PuckRoutes;
|
|
9
|
+
accent?: string;
|
|
10
|
+
}): ExperimentsPuckIntegration;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { EXPERIMENT_SLOT_TYPE } from '../../types/puck.js';
|
|
3
|
+
import { categoryTitle } from '../copy.js';
|
|
4
|
+
import { createExperimentSlotEditor } from './ExperimentSlotEditor.js';
|
|
5
|
+
import { experimentOutlinePlugin } from './experimentOutlinePlugin.js';
|
|
6
|
+
/**
|
|
7
|
+
* Call once at module scope in a 'use client' module. A new object per render
|
|
8
|
+
* rebuilds Puck's config and plugin list.
|
|
9
|
+
*/ export function experimentsPuckEditor({ routes, accent } = {}) {
|
|
10
|
+
return {
|
|
11
|
+
name: 'experiments',
|
|
12
|
+
components: {
|
|
13
|
+
[EXPERIMENT_SLOT_TYPE]: createExperimentSlotEditor({
|
|
14
|
+
routes,
|
|
15
|
+
accent
|
|
16
|
+
})
|
|
17
|
+
},
|
|
18
|
+
categories: {
|
|
19
|
+
experiments: {
|
|
20
|
+
title: categoryTitle,
|
|
21
|
+
components: [
|
|
22
|
+
EXPERIMENT_SLOT_TYPE
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
plugins: [
|
|
27
|
+
experimentOutlinePlugin()
|
|
28
|
+
]
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CustomField } from '@puckeditor/core';
|
|
2
|
+
import { type PuckRoutes } from '../routes.js';
|
|
3
|
+
import './editor.css';
|
|
4
|
+
/** Editor config only. The render config keeps xpGoalPuckField() from /puck. */
|
|
5
|
+
export declare function xpGoalPuckEditorField(options?: {
|
|
6
|
+
routes?: PuckRoutes;
|
|
7
|
+
}): CustomField<string>;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useId } from 'react';
|
|
4
|
+
import { useClickGoalPicker } from '../../goals/picker/useClickGoalPicker.js';
|
|
5
|
+
import { CLICK_GOAL_COPY as C } from '../../goals/picker/copy.js';
|
|
6
|
+
import { TextInput, Select } from '../../ui/Inputs.js';
|
|
7
|
+
import { Button } from '../../ui/Button.js';
|
|
8
|
+
import { resolvePuckRoutes } from '../routes.js';
|
|
9
|
+
import './editor.css';
|
|
10
|
+
function XpGoalPuckEditor({ value, onChange, api, readOnly }) {
|
|
11
|
+
const picker = useClickGoalPicker(api, {
|
|
12
|
+
onSelect: onChange
|
|
13
|
+
});
|
|
14
|
+
const id = useId();
|
|
15
|
+
const stopEnter = (event)=>{
|
|
16
|
+
if (event.key === 'Enter') {
|
|
17
|
+
event.preventDefault();
|
|
18
|
+
event.stopPropagation();
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
22
|
+
className: "xp-slot-root xp-puck-field",
|
|
23
|
+
children: [
|
|
24
|
+
/*#__PURE__*/ _jsx(TextInput, {
|
|
25
|
+
id: `${id}-value`,
|
|
26
|
+
label: C.label,
|
|
27
|
+
value: value ?? '',
|
|
28
|
+
readOnly: readOnly,
|
|
29
|
+
onChange: (event)=>onChange(event.currentTarget.value)
|
|
30
|
+
}),
|
|
31
|
+
picker.canManage && !readOnly ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
32
|
+
children: [
|
|
33
|
+
/*#__PURE__*/ _jsxs(Select, {
|
|
34
|
+
id: `${id}-picker`,
|
|
35
|
+
label: C.choose,
|
|
36
|
+
value: picker.goals.some((goal)=>goal.slug === value) ? value : '',
|
|
37
|
+
onChange: (event)=>{
|
|
38
|
+
if (event.currentTarget.value) onChange(event.currentTarget.value);
|
|
39
|
+
},
|
|
40
|
+
children: [
|
|
41
|
+
/*#__PURE__*/ _jsx("option", {
|
|
42
|
+
value: "",
|
|
43
|
+
children: C.choose
|
|
44
|
+
}),
|
|
45
|
+
picker.goals.map((goal)=>/*#__PURE__*/ _jsx("option", {
|
|
46
|
+
value: goal.slug,
|
|
47
|
+
children: goal.name
|
|
48
|
+
}, goal.id))
|
|
49
|
+
]
|
|
50
|
+
}),
|
|
51
|
+
picker.undefinedKey(value ?? '') ? /*#__PURE__*/ _jsx("p", {
|
|
52
|
+
className: "xp-ui-help",
|
|
53
|
+
role: "status",
|
|
54
|
+
children: C.undefinedKey
|
|
55
|
+
}) : null,
|
|
56
|
+
!picker.panel.open ? /*#__PURE__*/ _jsx(Button, {
|
|
57
|
+
type: "button",
|
|
58
|
+
onClick: picker.open,
|
|
59
|
+
children: C.newGoal
|
|
60
|
+
}) : /*#__PURE__*/ _jsxs("section", {
|
|
61
|
+
"aria-labelledby": `${id}-title`,
|
|
62
|
+
children: [
|
|
63
|
+
/*#__PURE__*/ _jsx("h3", {
|
|
64
|
+
id: `${id}-title`,
|
|
65
|
+
children: C.title
|
|
66
|
+
}),
|
|
67
|
+
/*#__PURE__*/ _jsx(TextInput, {
|
|
68
|
+
id: `${id}-name`,
|
|
69
|
+
label: C.name,
|
|
70
|
+
value: picker.panel.name,
|
|
71
|
+
disabled: picker.panel.pending,
|
|
72
|
+
onKeyDown: stopEnter,
|
|
73
|
+
onChange: (event)=>picker.setName(event.currentTarget.value)
|
|
74
|
+
}),
|
|
75
|
+
/*#__PURE__*/ _jsxs("details", {
|
|
76
|
+
open: picker.panel.advanced,
|
|
77
|
+
onToggle: (event)=>picker.setAdvanced(event.currentTarget.open),
|
|
78
|
+
children: [
|
|
79
|
+
/*#__PURE__*/ _jsx("summary", {
|
|
80
|
+
children: C.advanced
|
|
81
|
+
}),
|
|
82
|
+
/*#__PURE__*/ _jsx(TextInput, {
|
|
83
|
+
id: `${id}-slug`,
|
|
84
|
+
label: C.slug,
|
|
85
|
+
value: picker.panel.slug,
|
|
86
|
+
disabled: picker.panel.pending,
|
|
87
|
+
onKeyDown: stopEnter,
|
|
88
|
+
error: picker.panel.errors.slug,
|
|
89
|
+
onChange: (event)=>picker.setSlug(event.currentTarget.value)
|
|
90
|
+
})
|
|
91
|
+
]
|
|
92
|
+
}),
|
|
93
|
+
picker.panel.errors.general ? /*#__PURE__*/ _jsx("p", {
|
|
94
|
+
className: "xp-ui-error",
|
|
95
|
+
role: "alert",
|
|
96
|
+
children: picker.panel.errors.general
|
|
97
|
+
}) : null,
|
|
98
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
99
|
+
className: "xp-ui-button-group",
|
|
100
|
+
children: [
|
|
101
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
102
|
+
type: "button",
|
|
103
|
+
disabled: picker.panel.pending || !picker.panel.name.trim(),
|
|
104
|
+
onClick: ()=>void picker.create(),
|
|
105
|
+
children: C.createGoal
|
|
106
|
+
}),
|
|
107
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
108
|
+
type: "button",
|
|
109
|
+
disabled: picker.panel.pending,
|
|
110
|
+
onClick: picker.cancel,
|
|
111
|
+
children: C.cancel
|
|
112
|
+
})
|
|
113
|
+
]
|
|
114
|
+
})
|
|
115
|
+
]
|
|
116
|
+
})
|
|
117
|
+
]
|
|
118
|
+
}) : null
|
|
119
|
+
]
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/** Editor config only. The render config keeps xpGoalPuckField() from /puck. */ export function xpGoalPuckEditorField(options) {
|
|
123
|
+
const routes = resolvePuckRoutes(options?.routes);
|
|
124
|
+
return {
|
|
125
|
+
type: 'custom',
|
|
126
|
+
label: C.label,
|
|
127
|
+
render: ({ value, onChange, readOnly })=>/*#__PURE__*/ _jsx(XpGoalPuckEditor, {
|
|
128
|
+
value: value,
|
|
129
|
+
onChange: onChange,
|
|
130
|
+
api: routes.api,
|
|
131
|
+
readOnly: readOnly
|
|
132
|
+
})
|
|
133
|
+
};
|
|
134
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { CustomField } from '@puckeditor/core';
|
|
2
2
|
import type { ExperimentRef } from '../types/puck.js';
|
|
3
|
+
import { type PuckRoutes } from './routes.js';
|
|
3
4
|
export declare function createExperimentSlotField(opts?: {
|
|
4
5
|
label?: string;
|
|
6
|
+
routes?: PuckRoutes;
|
|
5
7
|
}): CustomField<ExperimentRef | null>;
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { createElement } from 'react';
|
|
2
2
|
import { ExperimentPicker } from './ExperimentPicker.js';
|
|
3
|
+
import { resolvePuckRoutes } from './routes.js';
|
|
4
|
+
import { PuckHostProvider } from './hostContext.js';
|
|
3
5
|
import { pickerCopy } from './copy.js';
|
|
4
6
|
export function createExperimentSlotField(opts) {
|
|
7
|
+
const routes = resolvePuckRoutes(opts?.routes);
|
|
5
8
|
return {
|
|
6
9
|
type: 'custom',
|
|
7
10
|
label: opts?.label ?? pickerCopy.label,
|
|
8
|
-
render: ({ value, onChange })=>createElement(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
render: ({ value, onChange })=>createElement(PuckHostProvider, {
|
|
12
|
+
routes,
|
|
13
|
+
children: createElement('div', {
|
|
14
|
+
className: 'xp-puck-field'
|
|
15
|
+
}, createElement(ExperimentPicker, {
|
|
16
|
+
value,
|
|
17
|
+
onChange
|
|
18
|
+
}))
|
|
11
19
|
})
|
|
12
20
|
};
|
|
13
21
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { resolvePuckRoutes } from './routes.js';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
export declare const usePuckRoutes: () => {
|
|
4
|
+
admin: string;
|
|
5
|
+
api: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function PuckHostProvider({ routes, children }: {
|
|
8
|
+
routes: ReturnType<typeof resolvePuckRoutes>;
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext } from 'react';
|
|
4
|
+
import { resolvePuckRoutes } from './routes.js';
|
|
5
|
+
const PuckHostContext = /*#__PURE__*/ createContext(resolvePuckRoutes());
|
|
6
|
+
export const usePuckRoutes = ()=>useContext(PuckHostContext);
|
|
7
|
+
export function PuckHostProvider({ routes, children }) {
|
|
8
|
+
return /*#__PURE__*/ _jsx(PuckHostContext.Provider, {
|
|
9
|
+
value: routes,
|
|
10
|
+
children: children
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type ExperimentsPuckIntegration } from '../types/puck.js';
|
|
2
|
+
import type { PuckRoutes } from './routes.js';
|
|
3
|
+
/** Bundle the test block and its sidebar category for a host render config. */
|
|
4
|
+
export declare function experimentsPuck({ routes }?: {
|
|
5
|
+
routes?: PuckRoutes;
|
|
6
|
+
}): ExperimentsPuckIntegration;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { EXPERIMENT_SLOT_TYPE } from '../types/puck.js';
|
|
2
|
+
import { createExperimentSlot } from './ExperimentSlot.js';
|
|
3
|
+
import { categoryTitle } from './copy.js';
|
|
4
|
+
/** Bundle the test block and its sidebar category for a host render config. */ export function experimentsPuck({ routes } = {}) {
|
|
5
|
+
return {
|
|
6
|
+
name: 'experiments',
|
|
7
|
+
components: {
|
|
8
|
+
[EXPERIMENT_SLOT_TYPE]: createExperimentSlot({
|
|
9
|
+
routes
|
|
10
|
+
})
|
|
11
|
+
},
|
|
12
|
+
categories: {
|
|
13
|
+
experiments: {
|
|
14
|
+
title: categoryTitle,
|
|
15
|
+
components: [
|
|
16
|
+
EXPERIMENT_SLOT_TYPE
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface PuckRoutes {
|
|
2
|
+
admin?: string;
|
|
3
|
+
api?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare const DEFAULT_PUCK_ROUTES: {
|
|
6
|
+
readonly admin: "/admin";
|
|
7
|
+
readonly api: "/api";
|
|
8
|
+
};
|
|
9
|
+
export declare function resolvePuckRoutes(routes?: PuckRoutes): {
|
|
10
|
+
admin: string;
|
|
11
|
+
api: string;
|
|
12
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Payload's documented route defaults; hosts pass config.routes when customized.
|
|
2
|
+
export const DEFAULT_PUCK_ROUTES = {
|
|
3
|
+
admin: '/admin',
|
|
4
|
+
api: '/api'
|
|
5
|
+
};
|
|
6
|
+
export function resolvePuckRoutes(routes) {
|
|
7
|
+
return {
|
|
8
|
+
admin: (routes?.admin ?? DEFAULT_PUCK_ROUTES.admin).replace(/\/$/, ''),
|
|
9
|
+
api: (routes?.api ?? DEFAULT_PUCK_ROUTES.api).replace(/\/$/, '')
|
|
10
|
+
};
|
|
11
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ComponentConfig } from '@puckeditor/core';
|
|
2
2
|
import type { ExperimentSlotProps } from '../types/puck.js';
|
|
3
|
-
export declare const experimentSlotLabel
|
|
3
|
+
export declare const experimentSlotLabel = "Experiment block";
|
|
4
4
|
export declare const experimentSlotFields: NonNullable<ComponentConfig<ExperimentSlotProps>['fields']>;
|
|
5
5
|
export declare const experimentSlotDefaultProps: NonNullable<ComponentConfig<ExperimentSlotProps>['defaultProps']>;
|
package/dist/puck/slotConfig.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createExperimentSlotField } from './experimentSlotField.js';
|
|
2
|
-
import {
|
|
3
|
-
export const experimentSlotLabel =
|
|
2
|
+
import { slotLabels } from './copy.js';
|
|
3
|
+
export const experimentSlotLabel = 'Experiment block';
|
|
4
4
|
export const experimentSlotFields = {
|
|
5
5
|
experiment: createExperimentSlotField(),
|
|
6
6
|
// Puck 0.24's defaultSlots prepends each slot, reversing declaration order in the outline.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Snapshot, SnapshotHealth } from '../types/snapshot.js';
|
|
2
|
+
/** Refresh page health while retaining health produced by other checks. */
|
|
3
|
+
export declare function mergePageHealth(previous: readonly SnapshotHealth[], pages: readonly SnapshotHealth[]): SnapshotHealth[];
|
|
4
|
+
export declare function canonicalHealth(snapshot: Pick<Snapshot, 'health' | 'goalFairnessNotice'>, ignoreUncheckedPlacements?: boolean): string;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const healthKey = (row)=>JSON.stringify(row.code === 'placement-unchecked' ? [
|
|
2
|
+
row.code,
|
|
3
|
+
row.detail.collection ?? '',
|
|
4
|
+
row.detail.id ?? '',
|
|
5
|
+
row.detail.path ?? ''
|
|
6
|
+
] : [
|
|
7
|
+
row.code,
|
|
8
|
+
'path' in row ? row.path : '',
|
|
9
|
+
'metricId' in row ? row.metricId : ''
|
|
10
|
+
]);
|
|
11
|
+
/** Refresh page health while retaining health produced by other checks. */ export function mergePageHealth(previous, pages) {
|
|
12
|
+
return [
|
|
13
|
+
...new Map([
|
|
14
|
+
...previous.filter((row)=>row.code === 'goal-in-some-versions' || row.code === 'placement-unchecked'),
|
|
15
|
+
...pages
|
|
16
|
+
].map((row)=>[
|
|
17
|
+
healthKey(row),
|
|
18
|
+
row
|
|
19
|
+
])).values()
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
export function canonicalHealth(snapshot, ignoreUncheckedPlacements = false) {
|
|
23
|
+
return JSON.stringify([
|
|
24
|
+
snapshot.goalFairnessNotice ?? '',
|
|
25
|
+
...(snapshot.health ?? []).filter((row)=>!ignoreUncheckedPlacements || row.code !== 'placement-unchecked').map(healthKey).sort()
|
|
26
|
+
]);
|
|
27
|
+
}
|
package/dist/results/snapshot.js
CHANGED
|
@@ -7,6 +7,7 @@ const count = (value)=>number(value) && Number.isInteger(value) && value >= 0;
|
|
|
7
7
|
const string = (value)=>typeof value === 'string';
|
|
8
8
|
const boolean = (value)=>typeof value === 'boolean';
|
|
9
9
|
const nullable = (check)=>(value)=>value === null || check(value);
|
|
10
|
+
const union = (...checks)=>(value)=>checks.some((check)=>check(value));
|
|
10
11
|
const list = (check)=>(value)=>Array.isArray(value) && value.every(check);
|
|
11
12
|
const oneOf = (...values)=>(value)=>values.includes(value);
|
|
12
13
|
const isObject = (value)=>value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
@@ -140,10 +141,22 @@ const shape = object({
|
|
|
140
141
|
by: nullable(id)
|
|
141
142
|
}))
|
|
142
143
|
}, {
|
|
143
|
-
|
|
144
|
+
goalFairnessNotice: string,
|
|
145
|
+
health: list(union(object({
|
|
144
146
|
code: oneOf('version-page-unavailable', 'version-page-unchecked'),
|
|
145
147
|
path: string
|
|
146
|
-
})
|
|
148
|
+
}), object({
|
|
149
|
+
code: oneOf('goal-in-some-versions'),
|
|
150
|
+
metricId: id,
|
|
151
|
+
name: string
|
|
152
|
+
}), object({
|
|
153
|
+
code: oneOf('placement-unchecked'),
|
|
154
|
+
detail: object({}, {
|
|
155
|
+
collection: string,
|
|
156
|
+
id,
|
|
157
|
+
path: string
|
|
158
|
+
})
|
|
159
|
+
})))
|
|
147
160
|
});
|
|
148
161
|
/** Validate the frozen JSON shape and headline/rollout invariants before storage.
|
|
149
162
|
* This also accepts the shared CORE-13/admin fixtures without any I/O.
|
|
@@ -8,6 +8,7 @@ const arm = (value)=>typeof value === 'string' && [
|
|
|
8
8
|
'variantC',
|
|
9
9
|
'holdout'
|
|
10
10
|
].includes(value);
|
|
11
|
+
const path = (value)=>typeof value === 'string' && value.length <= 128 && value.startsWith('/') && !value.includes('//') && !/[^A-Za-z0-9_/-]/.test(value);
|
|
11
12
|
const failure = (value)=>value === 'placements-failed' || value === 'forbidden' || value === 'not-found';
|
|
12
13
|
/** Summary details contain identifiers only. Project known shapes rather than
|
|
13
14
|
* recursively copying host objects: titles, messages and placement detail are text.
|
|
@@ -15,15 +16,64 @@ const failure = (value)=>value === 'placements-failed' || value === 'forbidden'
|
|
|
15
16
|
* counts and layer capacity, which are not identifiers).
|
|
16
17
|
*/ export function sanitizeReadinessDetail(code, detail) {
|
|
17
18
|
switch(code){
|
|
19
|
+
case 'goal-in-some-versions':
|
|
20
|
+
{
|
|
21
|
+
const rows = Array.isArray(detail) ? detail : [
|
|
22
|
+
detail
|
|
23
|
+
];
|
|
24
|
+
return rows.filter(object).filter((row)=>key(row.metricId) || typeof row.metricId === 'number' && Number.isSafeInteger(row.metricId)).map((row)=>({
|
|
25
|
+
metricId: row.metricId
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
case 'placement-unchecked':
|
|
29
|
+
return object(detail) ? Object.fromEntries(Object.entries(detail).filter(([k, v])=>k === 'path' ? path(v) : [
|
|
30
|
+
'collection',
|
|
31
|
+
'id'
|
|
32
|
+
].includes(k) && (key(v) || typeof v === 'number' && Number.isSafeInteger(v)))) : undefined;
|
|
33
|
+
case 'placement-unpublished':
|
|
34
|
+
case 'duplicate-version-row':
|
|
35
|
+
case 'interacting-tests':
|
|
36
|
+
return Array.isArray(detail) ? detail.filter(object).map((row)=>Object.fromEntries(Object.entries(row).filter(([k, v])=>[
|
|
37
|
+
'collection',
|
|
38
|
+
'id',
|
|
39
|
+
'experimentId',
|
|
40
|
+
'slug'
|
|
41
|
+
].includes(k) ? key(v) || typeof v === 'number' && Number.isSafeInteger(v) : k === 'path' ? path(v) : k === 'status' ? [
|
|
42
|
+
'running',
|
|
43
|
+
'decided'
|
|
44
|
+
].includes(String(v)) : k === 'version' && typeof v === 'boolean'))) : undefined;
|
|
45
|
+
case 'version-same-address':
|
|
46
|
+
return object(detail) && arm(detail.arm) && arm(detail.otherArm) && path(detail.path) ? {
|
|
47
|
+
arm: detail.arm,
|
|
48
|
+
otherArm: detail.otherArm,
|
|
49
|
+
path: detail.path
|
|
50
|
+
} : undefined;
|
|
51
|
+
case 'version-identical':
|
|
52
|
+
return object(detail) && arm(detail.arm) && key(detail.collection) && (key(detail.id) || typeof detail.id === 'number' && Number.isSafeInteger(detail.id)) ? {
|
|
53
|
+
arm: detail.arm,
|
|
54
|
+
collection: detail.collection,
|
|
55
|
+
id: detail.id,
|
|
56
|
+
...path(detail.path) ? {
|
|
57
|
+
path: detail.path
|
|
58
|
+
} : {}
|
|
59
|
+
} : undefined;
|
|
18
60
|
case 'block-path-rule':
|
|
19
61
|
return object(detail) && Array.isArray(detail.rules) && detail.rules.length && detail.rules.every((index)=>Number.isSafeInteger(index) && index >= 0) ? {
|
|
20
62
|
rules: detail.rules
|
|
21
63
|
} : undefined;
|
|
22
64
|
case 'no-placements':
|
|
23
65
|
return failure(detail) ? detail : undefined;
|
|
66
|
+
case 'page-in-use':
|
|
67
|
+
{
|
|
68
|
+
if (!object(detail) || !Array.isArray(detail.paths)) return undefined;
|
|
69
|
+
const paths = detail.paths.filter(path);
|
|
70
|
+
return paths.length ? {
|
|
71
|
+
paths
|
|
72
|
+
} : undefined;
|
|
73
|
+
}
|
|
24
74
|
case 'page-target-unpublished':
|
|
25
75
|
// Paths only: no query, fragment, whitespace, URL authority or encoded text.
|
|
26
|
-
return
|
|
76
|
+
return path(detail) ? detail : undefined;
|
|
27
77
|
case 'flag-params-invalid':
|
|
28
78
|
{
|
|
29
79
|
if (!Array.isArray(detail) || !detail.length || !detail.every((item)=>object(item) && arm(item.arm) && key(item.field))) return undefined;
|
package/dist/server/metadata.js
CHANGED
|
@@ -13,14 +13,21 @@ import { serverManifest } from './context.js';
|
|
|
13
13
|
const live = manifest?.experiments.find((e)=>e.kind === 'page' && e.arms.some((a)=>a.pageTarget !== undefined && normalisePath(a.pageTarget) === normalisePath(path)));
|
|
14
14
|
if (live) {
|
|
15
15
|
const controlPath = live.arms.find((a)=>a.kind === 'control')?.pageTarget;
|
|
16
|
-
|
|
16
|
+
if (!controlPath) return {};
|
|
17
|
+
// The Original's URL may be served with a variant's route: its canonical must stay on the Original,
|
|
18
|
+
// never the (noindexed) variant route the host would otherwise derive.
|
|
19
|
+
return normalisePath(path) !== normalisePath(controlPath) ? {
|
|
17
20
|
robots: {
|
|
18
21
|
index: false
|
|
19
22
|
},
|
|
20
23
|
alternates: {
|
|
21
24
|
canonical: controlPath
|
|
22
25
|
}
|
|
23
|
-
} : {
|
|
26
|
+
} : {
|
|
27
|
+
alternates: {
|
|
28
|
+
canonical: controlPath
|
|
29
|
+
}
|
|
30
|
+
};
|
|
24
31
|
}
|
|
25
32
|
const archivedPath = manifest ? findNormalisedPathKey(manifest.archivedPages, path) : undefined;
|
|
26
33
|
const archived = archivedPath === undefined ? undefined : manifest?.archivedPages[archivedPath];
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Block, Field } from 'payload';
|
|
2
|
+
/** Undo depth expansion only where the collection schema declares relationships.
|
|
3
|
+
* Hidden fields and field-level afterRead hooks are unaffected by depth, so
|
|
4
|
+
* relationship expansion is the only depth-dependent transformation to undo.
|
|
5
|
+
* Other objects (especially JSON content with its own ids) must stay intact.
|
|
6
|
+
*/
|
|
7
|
+
export declare function normaliseContentRelationships(data: unknown, fields: Field[], blocks?: Block[], locales?: readonly string[]): unknown;
|