@delmaredigital/payload-experiments 0.0.0-stage → 0.1.0-rc.6
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 +590 -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 +107 -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 +141 -0
- package/dist/types/contracts.js +1 -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 +138 -3
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { COLUMNS, TABLES } from './names.js';
|
|
2
|
+
import { XpRefusal } from '../errors.js';
|
|
3
|
+
import { qualifiedTable, quoteIdentifier as quote } from './sql.js';
|
|
4
|
+
/** Read the main collection row BEFORE the host write. Draft version retention
|
|
5
|
+
* and unpublishing cannot remove this baseline. Adapter reads avoid Local API
|
|
6
|
+
* error handling killing the host transaction before savepoint recovery.
|
|
7
|
+
*/ export async function previousPublishedContent(req, collection, id) {
|
|
8
|
+
const doc = await req.payload.db.findOne({
|
|
9
|
+
collection,
|
|
10
|
+
req,
|
|
11
|
+
where: {
|
|
12
|
+
id: {
|
|
13
|
+
equals: id
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
// Adapter reads have no access layer; document the audit's access posture.
|
|
17
|
+
overrideAccess: false
|
|
18
|
+
});
|
|
19
|
+
if (!doc) return undefined;
|
|
20
|
+
// A draft created with content is already in the main table. Only the
|
|
21
|
+
// existence of a published version matters here; never use its content as
|
|
22
|
+
// the baseline (published main rows remain reliable after version pruning).
|
|
23
|
+
if (doc._status === 'draft') {
|
|
24
|
+
// §7.8 contentChanges exception: existence only, on the capture savepoint's req.
|
|
25
|
+
const published = await req.payload.db.findVersions({
|
|
26
|
+
collection,
|
|
27
|
+
req,
|
|
28
|
+
limit: 1,
|
|
29
|
+
pagination: false,
|
|
30
|
+
where: {
|
|
31
|
+
and: [
|
|
32
|
+
{
|
|
33
|
+
parent: {
|
|
34
|
+
equals: id
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
'version._status': {
|
|
39
|
+
equals: 'published'
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
overrideAccess: false
|
|
45
|
+
});
|
|
46
|
+
if (!published.docs.length) return undefined;
|
|
47
|
+
}
|
|
48
|
+
return doc;
|
|
49
|
+
}
|
|
50
|
+
/** Serialize flagDefaults read/modify/write before the manifest builder bumps. */ export async function lockContentDefaults(exec) {
|
|
51
|
+
await exec.query("SELECT pg_advisory_xact_lock(hashtext('xp:state'), 0)");
|
|
52
|
+
}
|
|
53
|
+
/** §7.8 contentChanges exception: internal lookup on the host transaction.
|
|
54
|
+
* Use the executor directly: Payload Local API read errors kill the caller's
|
|
55
|
+
* transaction before a never-throw hook can recover its audit savepoint.
|
|
56
|
+
*/ export async function runningContentExperiment(exec, binding) {
|
|
57
|
+
const table = qualifiedTable(exec, TABLES.experiments);
|
|
58
|
+
const c = COLUMNS.experiments;
|
|
59
|
+
const { rows } = await exec.query(`SELECT ${quote(c.id)} AS id, ${quote(c.phase)} AS phase FROM ${table} WHERE ${quote(c[binding.field])} = $1 AND ${quote(c.status)} = 'running' LIMIT 1`, [
|
|
60
|
+
binding.value
|
|
61
|
+
]);
|
|
62
|
+
return rows[0];
|
|
63
|
+
}
|
|
64
|
+
async function append(exec, column, { experimentId, phase, entry }) {
|
|
65
|
+
const table = qualifiedTable(exec, TABLES.phases);
|
|
66
|
+
const c = COLUMNS.phases;
|
|
67
|
+
const { rowCount } = await exec.query(`UPDATE ${table} SET ${quote(column)} = coalesce(${quote(column)}, '[]'::jsonb) || $1::jsonb WHERE ${quote(c.experimentId)} = $2 AND ${quote(c.phase)} = $3`, [
|
|
68
|
+
JSON.stringify([
|
|
69
|
+
entry
|
|
70
|
+
]),
|
|
71
|
+
experimentId,
|
|
72
|
+
phase
|
|
73
|
+
]);
|
|
74
|
+
if (rowCount === 0) throw new XpRefusal('not-found', 'The phase record was not found.', {
|
|
75
|
+
experimentId,
|
|
76
|
+
phase
|
|
77
|
+
}, 404);
|
|
78
|
+
}
|
|
79
|
+
/** Requires the experiment lock on the transaction executor (withTransition).
|
|
80
|
+
* The generated schema stores allocationChanges as one JSONB column, not rows.
|
|
81
|
+
*/ export function appendAllocationChange(exec, args) {
|
|
82
|
+
return append(exec, COLUMNS.phases.allocationChanges, args);
|
|
83
|
+
}
|
|
84
|
+
/** Requires the experiment lock on the transaction executor (withTransition).
|
|
85
|
+
* The generated schema stores contentChanges as one JSONB column, not rows.
|
|
86
|
+
*/ export function appendContentChange(exec, args) {
|
|
87
|
+
return append(exec, COLUMNS.phases.contentChanges, args);
|
|
88
|
+
}
|
|
89
|
+
/** A best-effort audit must not abort the host write sharing its transaction.
|
|
90
|
+
* Recover SQL errors before returning them: catching alone leaves Postgres aborted.
|
|
91
|
+
*/ export async function contentChangeSavepoint(exec, fn) {
|
|
92
|
+
await exec.query('SAVEPOINT xp_content_change');
|
|
93
|
+
try {
|
|
94
|
+
const { rows } = await exec.query("SELECT current_setting('lock_timeout') AS timeout");
|
|
95
|
+
const previousTimeout = rows[0].timeout;
|
|
96
|
+
await exec.query("SELECT set_config('lock_timeout', '2s', true)");
|
|
97
|
+
const result = await fn();
|
|
98
|
+
// RELEASE retains SET LOCAL changes; restore the caller's value explicitly.
|
|
99
|
+
await exec.query("SELECT set_config('lock_timeout', $1, true)", [
|
|
100
|
+
previousTimeout
|
|
101
|
+
]);
|
|
102
|
+
await exec.query('RELEASE SAVEPOINT xp_content_change');
|
|
103
|
+
return {
|
|
104
|
+
ok: true,
|
|
105
|
+
result
|
|
106
|
+
};
|
|
107
|
+
} catch (error) {
|
|
108
|
+
// ROLLBACK TO restores SET LOCAL and releases locks acquired in this savepoint.
|
|
109
|
+
await exec.query('ROLLBACK TO SAVEPOINT xp_content_change');
|
|
110
|
+
await exec.query('RELEASE SAVEPOINT xp_content_change');
|
|
111
|
+
return {
|
|
112
|
+
ok: false,
|
|
113
|
+
error
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PayloadRequest } from 'payload';
|
|
2
|
+
import type { DocId } from '../types/contracts.js';
|
|
3
|
+
import type { Executor } from './executor.js';
|
|
4
|
+
interface DeleteState {
|
|
5
|
+
tail: Promise<void>;
|
|
6
|
+
excludedIds: Map<string, DocId>;
|
|
7
|
+
pruneFlagDefaultIds: Map<string, DocId>;
|
|
8
|
+
}
|
|
9
|
+
/** Payload bulk delete runs hooks concurrently on a shared transaction. Queue
|
|
10
|
+
* each hook body, including the full manifest read/modify/write, on that session.
|
|
11
|
+
* Successful beforeDelete bodies retain exclusions until the transaction ends:
|
|
12
|
+
* another row can be between hooks with its children gone but its row present.
|
|
13
|
+
*/
|
|
14
|
+
export declare function serializeDelete<T>(req: PayloadRequest, fn: (state: DeleteState) => Promise<T>): Promise<T>;
|
|
15
|
+
/** Select only snapshot ids and delete the entire set in one SQL statement. */
|
|
16
|
+
export declare function deleteExperimentSnapshots(exec: Executor, experimentId: DocId): Promise<void>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { XpNoTransactionError } from '../errors.js';
|
|
2
|
+
import { COLUMNS, TABLES } from './names.js';
|
|
3
|
+
import { qualifiedTable, quoteIdentifier as quote } from './sql.js';
|
|
4
|
+
// Key by the actual session: borrowed requests in one transaction share a queue,
|
|
5
|
+
// different Payload instances do not, and commit/rollback permits GC of state.
|
|
6
|
+
const transactions = new WeakMap();
|
|
7
|
+
/** Payload bulk delete runs hooks concurrently on a shared transaction. Queue
|
|
8
|
+
* each hook body, including the full manifest read/modify/write, on that session.
|
|
9
|
+
* Successful beforeDelete bodies retain exclusions until the transaction ends:
|
|
10
|
+
* another row can be between hooks with its children gone but its row present.
|
|
11
|
+
*/ export async function serializeDelete(req, fn) {
|
|
12
|
+
const id = await req.transactionID;
|
|
13
|
+
const session = id == null ? undefined : req.payload.db.sessions?.[id];
|
|
14
|
+
if (!session) throw new XpNoTransactionError();
|
|
15
|
+
let state = transactions.get(session);
|
|
16
|
+
if (!state) {
|
|
17
|
+
state = {
|
|
18
|
+
tail: Promise.resolve(),
|
|
19
|
+
excludedIds: new Map(),
|
|
20
|
+
pruneFlagDefaultIds: new Map()
|
|
21
|
+
};
|
|
22
|
+
transactions.set(session, state);
|
|
23
|
+
}
|
|
24
|
+
const current = state;
|
|
25
|
+
const work = current.tail.then(()=>fn(current));
|
|
26
|
+
current.tail = work.then(()=>undefined, ()=>undefined);
|
|
27
|
+
return work;
|
|
28
|
+
}
|
|
29
|
+
/** Select only snapshot ids and delete the entire set in one SQL statement. */ export async function deleteExperimentSnapshots(exec, experimentId) {
|
|
30
|
+
const table = qualifiedTable(exec, TABLES.resultsSnapshots);
|
|
31
|
+
const c = COLUMNS.resultsSnapshots;
|
|
32
|
+
await exec.query(`DELETE FROM ${table} WHERE ${quote(c.id)} IN (SELECT ${quote(c.id)} FROM ${table} WHERE ${quote(c.experimentId)} = $1)`, [
|
|
33
|
+
experimentId
|
|
34
|
+
]);
|
|
35
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Payload } from 'payload';
|
|
2
|
+
import { type Executor } from './executor.js';
|
|
3
|
+
/** The caller owns the transaction spanning the read and both deletes. */
|
|
4
|
+
export declare function eraseUserRows(exec: Executor, userId: string): Promise<{
|
|
5
|
+
identities: number;
|
|
6
|
+
events: number;
|
|
7
|
+
}>;
|
|
8
|
+
/** Own a Payload transaction, then verify on the pool: Payload can swallow COMMIT errors. */
|
|
9
|
+
export declare function eraseUserInTransaction(payload: Payload, userId: string): Promise<{
|
|
10
|
+
identities: number;
|
|
11
|
+
events: number;
|
|
12
|
+
}>;
|
package/dist/db/erase.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { pooledExecutor, txExecutor } from './executor.js';
|
|
2
|
+
import { COLUMNS, TABLES } from './names.js';
|
|
3
|
+
const quote = (name)=>`"${name.replaceAll('"', '""')}"`;
|
|
4
|
+
const i = COLUMNS.identities;
|
|
5
|
+
const e = COLUMNS.events;
|
|
6
|
+
function table(exec, name) {
|
|
7
|
+
const schema = exec.schemaName;
|
|
8
|
+
return `${schema ? `${quote(schema)}.` : ''}${quote(name)}`;
|
|
9
|
+
}
|
|
10
|
+
const eventPredicate = `${quote(e.userId)} = $1 OR (${quote(e.unitType)} = 'user' AND ${quote(e.unitId)} = $1) OR ${quote(e.unitId)} = ANY($2::text[])`;
|
|
11
|
+
async function erase(exec, userId) {
|
|
12
|
+
const linked = await exec.query(`SELECT ${quote(i.anonymousId)} AS "anonymousId" FROM ${table(exec, TABLES.identities)} WHERE ${quote(i.userId)} = $1`, [
|
|
13
|
+
userId
|
|
14
|
+
]);
|
|
15
|
+
const anonymousIds = linked.rows.map((row)=>row.anonymousId);
|
|
16
|
+
const events = await exec.query(`DELETE FROM ${table(exec, TABLES.events)} WHERE ${eventPredicate} RETURNING ${quote(e.id)}`, [
|
|
17
|
+
userId,
|
|
18
|
+
anonymousIds
|
|
19
|
+
]);
|
|
20
|
+
const identities = await exec.query(`DELETE FROM ${table(exec, TABLES.identities)} WHERE ${quote(i.userId)} = $1 RETURNING ${quote(i.id)}`, [
|
|
21
|
+
userId
|
|
22
|
+
]);
|
|
23
|
+
return {
|
|
24
|
+
counts: {
|
|
25
|
+
identities: identities.rows.length,
|
|
26
|
+
events: events.rows.length
|
|
27
|
+
},
|
|
28
|
+
anonymousIds
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** The caller owns the transaction spanning the read and both deletes. */ export async function eraseUserRows(exec, userId) {
|
|
32
|
+
return (await erase(exec, userId)).counts;
|
|
33
|
+
}
|
|
34
|
+
/** Own a Payload transaction, then verify on the pool: Payload can swallow COMMIT errors. */ export async function eraseUserInTransaction(payload, userId) {
|
|
35
|
+
const transactionID = await payload.db.beginTransaction();
|
|
36
|
+
if (transactionID == null) throw new Error('Experiments: eraseUser requires a transaction');
|
|
37
|
+
let result;
|
|
38
|
+
try {
|
|
39
|
+
result = await erase(txExecutor({
|
|
40
|
+
payload,
|
|
41
|
+
transactionID
|
|
42
|
+
}), userId);
|
|
43
|
+
await payload.db.commitTransaction(transactionID);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
await payload.db.rollbackTransaction(transactionID);
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
const exec = pooledExecutor(payload);
|
|
49
|
+
const verified = await exec.query(`SELECT EXISTS (SELECT 1 FROM ${table(exec, TABLES.events)} WHERE ${eventPredicate}) OR EXISTS (SELECT 1 FROM ${table(exec, TABLES.identities)} WHERE ${quote(i.userId)} = $1) AS remaining`, [
|
|
50
|
+
userId,
|
|
51
|
+
result.anonymousIds
|
|
52
|
+
]);
|
|
53
|
+
if (verified.rows[0]?.remaining !== false) throw new Error('Experiments: eraseUser commit unverified');
|
|
54
|
+
return result.counts;
|
|
55
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { DocId, UnitType } from '../types/contracts.js';
|
|
2
|
+
import type { Executor } from './executor.js';
|
|
3
|
+
/** Delete cascade: caller holds the experiment lock in the delete transaction. */
|
|
4
|
+
export declare function deleteExperimentEvents(exec: Executor, experimentId: DocId): Promise<void>;
|
|
5
|
+
export interface EventRow {
|
|
6
|
+
type: 'exposure' | 'conversion' | 'arm-mismatch';
|
|
7
|
+
experimentId?: DocId;
|
|
8
|
+
phase?: number;
|
|
9
|
+
arm?: string;
|
|
10
|
+
unitId: string;
|
|
11
|
+
unitType: UnitType;
|
|
12
|
+
userId?: string;
|
|
13
|
+
eventName?: string;
|
|
14
|
+
value?: number;
|
|
15
|
+
ts: string;
|
|
16
|
+
dimensions?: Record<string, string | number | boolean> | null;
|
|
17
|
+
attributes?: Record<string, unknown>;
|
|
18
|
+
dedupeKey: string;
|
|
19
|
+
}
|
|
20
|
+
/** Pooled/autocommit executor required: an FK failure must not poison a caller's transaction. */
|
|
21
|
+
export declare function insertEvents(exec: Executor, rows: EventRow[], log?: (message: string) => void): Promise<number>;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { exposureDimensions } from '../events/dimensions.js';
|
|
2
|
+
import { COLUMNS, TABLES } from './names.js';
|
|
3
|
+
import { qualifiedTable, quoteIdentifier } from './sql.js';
|
|
4
|
+
/** Delete cascade: caller holds the experiment lock in the delete transaction. */ export async function deleteExperimentEvents(exec, experimentId) {
|
|
5
|
+
await exec.query(`DELETE FROM ${qualifiedTable(exec, TABLES.events)} WHERE ${quoteIdentifier(COLUMNS.events.experimentId)} = $1`, [
|
|
6
|
+
experimentId
|
|
7
|
+
]);
|
|
8
|
+
}
|
|
9
|
+
const quote = (name)=>`"${name.replaceAll('"', '""')}"`;
|
|
10
|
+
const c = COLUMNS.events;
|
|
11
|
+
function foreignKeyViolation(error) {
|
|
12
|
+
const seen = new Set();
|
|
13
|
+
while(error && typeof error === 'object' && !seen.has(error)){
|
|
14
|
+
seen.add(error);
|
|
15
|
+
const failure = error;
|
|
16
|
+
if (failure.code === '23503') return true;
|
|
17
|
+
error = failure.cause;
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
/** Dimensions are optional: malformed host values (including custom toJSON or
|
|
22
|
+
* cyclic objects from JavaScript callers) must not abort any row in the batch. */ function serialiseDimensions(row) {
|
|
23
|
+
if (row.type !== 'exposure' || !row.dimensions) return null;
|
|
24
|
+
try {
|
|
25
|
+
const dimensions = exposureDimensions(row.dimensions, Object.keys(row.dimensions));
|
|
26
|
+
return dimensions ? JSON.stringify(dimensions) : null;
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Pooled/autocommit executor required: an FK failure must not poison a caller's transaction. */ export async function insertEvents(exec, rows, log = console.error) {
|
|
32
|
+
const valid = rows.filter((row)=>{
|
|
33
|
+
if (row.value === undefined || Number.isFinite(row.value)) return true;
|
|
34
|
+
log('Experiments: dropped event with non-finite value');
|
|
35
|
+
return false;
|
|
36
|
+
});
|
|
37
|
+
if (!valid.length) return 0;
|
|
38
|
+
const schema = exec.schemaName;
|
|
39
|
+
const table = `${schema ? `${quote(schema)}.` : ''}${quote(TABLES.events)}`;
|
|
40
|
+
async function insert(batch) {
|
|
41
|
+
const params = [];
|
|
42
|
+
const values = batch.map((row)=>{
|
|
43
|
+
const fields = [
|
|
44
|
+
row.type,
|
|
45
|
+
row.experimentId ?? null,
|
|
46
|
+
row.phase ?? null,
|
|
47
|
+
row.arm ?? null,
|
|
48
|
+
row.unitId,
|
|
49
|
+
row.unitType,
|
|
50
|
+
row.userId ?? null,
|
|
51
|
+
row.eventName ?? null,
|
|
52
|
+
row.value ?? null,
|
|
53
|
+
row.ts,
|
|
54
|
+
serialiseDimensions(row),
|
|
55
|
+
row.attributes === undefined ? null : JSON.stringify(row.attributes),
|
|
56
|
+
row.dedupeKey
|
|
57
|
+
];
|
|
58
|
+
return `(${fields.map((value)=>{
|
|
59
|
+
params.push(value);
|
|
60
|
+
return `$${params.length}`;
|
|
61
|
+
}).join(', ')}, now(), now())`;
|
|
62
|
+
});
|
|
63
|
+
const result = await exec.query(`INSERT INTO ${table} (${[
|
|
64
|
+
c.type,
|
|
65
|
+
c.experimentId,
|
|
66
|
+
c.phase,
|
|
67
|
+
c.arm,
|
|
68
|
+
c.unitId,
|
|
69
|
+
c.unitType,
|
|
70
|
+
c.userId,
|
|
71
|
+
c.eventName,
|
|
72
|
+
c.value,
|
|
73
|
+
c.ts,
|
|
74
|
+
c.dimensions,
|
|
75
|
+
c.attributes,
|
|
76
|
+
c.dedupeKey,
|
|
77
|
+
c.createdAt,
|
|
78
|
+
c.updatedAt
|
|
79
|
+
].map(quote).join(', ')}) VALUES ${values.join(', ')} ON CONFLICT (${quote(c.dedupeKey)}) DO NOTHING RETURNING ${quote(c.id)}`, params);
|
|
80
|
+
return result.rows.length;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
return await insert(valid);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
if (!foreignKeyViolation(error)) throw error;
|
|
86
|
+
// The failed statement is atomic. Retry separately to retain unaffected rows.
|
|
87
|
+
let count = 0;
|
|
88
|
+
for (const row of valid){
|
|
89
|
+
try {
|
|
90
|
+
count += await insert([
|
|
91
|
+
row
|
|
92
|
+
]);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (!foreignKeyViolation(error)) throw error;
|
|
95
|
+
log('Experiments: dropped event after concurrent experiment deletion');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return count;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Payload, PayloadRequest } from 'payload';
|
|
2
|
+
import type { Executor } from './pgExecutor.js';
|
|
3
|
+
export type { Executor } from './pgExecutor.js';
|
|
4
|
+
export { pgPoolExecutor, ownConnection, withStatementTimeout, raceDeadline, ResultsBudgetError, RESULTS_TIMEOUT_MARGIN_MS } from './pgExecutor.js';
|
|
5
|
+
export declare function txExecutor(req: PayloadRequest): Executor & {
|
|
6
|
+
schemaName?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function pooledExecutor(payload: Payload): Executor & {
|
|
9
|
+
schemaName?: string;
|
|
10
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { sql } from '@payloadcms/db-postgres/drizzle';
|
|
2
|
+
import { XpNoTransactionError } from '../errors.js';
|
|
3
|
+
import { normalise, scanParameters } from './pgExecutor.js';
|
|
4
|
+
export { pgPoolExecutor, ownConnection, withStatementTimeout, raceDeadline, ResultsBudgetError, RESULTS_TIMEOUT_MARGIN_MS } from './pgExecutor.js';
|
|
5
|
+
/** Convert code-only $n placeholders to sql.raw chunks and sql.param values.
|
|
6
|
+
* Repeated/out-of-order placeholders rebind; arrays are always one parameter.
|
|
7
|
+
*/ function bind(text, params) {
|
|
8
|
+
const chunks = [];
|
|
9
|
+
let start = 0;
|
|
10
|
+
for (const position of scanParameters(text, params)){
|
|
11
|
+
chunks.push(sql.raw(text.slice(start, position.start)), sql`${sql.param(params[position.index])}`);
|
|
12
|
+
start = position.end;
|
|
13
|
+
}
|
|
14
|
+
chunks.push(sql.raw(text.slice(start)));
|
|
15
|
+
return sql.join(chunks, sql.raw(''));
|
|
16
|
+
}
|
|
17
|
+
export function txExecutor(req) {
|
|
18
|
+
const db = req.payload.db;
|
|
19
|
+
return {
|
|
20
|
+
schemaName: db.schemaName,
|
|
21
|
+
async query (text, params = []) {
|
|
22
|
+
const id = await req.transactionID;
|
|
23
|
+
const session = id == null ? undefined : db.sessions?.[id];
|
|
24
|
+
if (!session) throw new XpNoTransactionError();
|
|
25
|
+
return normalise(await session.db.execute(bind(text, params)));
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function pooledExecutor(payload) {
|
|
30
|
+
const db = payload.db;
|
|
31
|
+
return {
|
|
32
|
+
schemaName: db.schemaName,
|
|
33
|
+
async query (text, params = []) {
|
|
34
|
+
return normalise(await db.drizzle.execute(bind(text, params)));
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Payload } from 'payload';
|
|
2
|
+
import { type Executor } from './executor.js';
|
|
3
|
+
export declare function insertIdentity(exec: Executor, { anonymousId, userId }: {
|
|
4
|
+
anonymousId: string;
|
|
5
|
+
userId: string;
|
|
6
|
+
}): Promise<void>;
|
|
7
|
+
/** §5.6 / §7.8: privileged insert, isolated from the caller's sign-in transaction. */
|
|
8
|
+
export declare function insertIdentityOnOwnConnection(payload: Payload, identity: {
|
|
9
|
+
anonymousId: string;
|
|
10
|
+
userId: string;
|
|
11
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ownConnection } from './executor.js';
|
|
2
|
+
import { COLUMNS, TABLES } from './names.js';
|
|
3
|
+
const quote = (name)=>`"${name.replaceAll('"', '""')}"`;
|
|
4
|
+
export async function insertIdentity(exec, { anonymousId, userId }) {
|
|
5
|
+
const schema = exec.schemaName;
|
|
6
|
+
const table = `${schema ? `${quote(schema)}.` : ''}${quote(TABLES.identities)}`;
|
|
7
|
+
const c = COLUMNS.identities;
|
|
8
|
+
await exec.query(`INSERT INTO ${table} (${[
|
|
9
|
+
c.anonymousId,
|
|
10
|
+
c.userId,
|
|
11
|
+
c.linkedAt,
|
|
12
|
+
c.createdAt,
|
|
13
|
+
c.updatedAt
|
|
14
|
+
].map(quote).join(', ')}) VALUES ($1, $2, now(), now(), now()) ON CONFLICT (${quote(c.anonymousId)}) DO NOTHING`, [
|
|
15
|
+
anonymousId,
|
|
16
|
+
userId
|
|
17
|
+
]);
|
|
18
|
+
}
|
|
19
|
+
/** §5.6 / §7.8: privileged insert, isolated from the caller's sign-in transaction. */ export async function insertIdentityOnOwnConnection(payload, identity) {
|
|
20
|
+
const db = payload.db;
|
|
21
|
+
const exec = await ownConnection(db.pool);
|
|
22
|
+
try {
|
|
23
|
+
await insertIdentity(Object.assign(exec, {
|
|
24
|
+
schemaName: db.schemaName
|
|
25
|
+
}), identity);
|
|
26
|
+
} finally{
|
|
27
|
+
exec.release();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Executor } from './executor.js';
|
|
2
|
+
import type { DocId } from '../types/contracts.js';
|
|
3
|
+
export declare const LOCK_NAMESPACES: {
|
|
4
|
+
readonly layer: "xp:layer";
|
|
5
|
+
readonly flagPoint: "xp:flagpoint";
|
|
6
|
+
readonly experiment: "xp:experiment";
|
|
7
|
+
};
|
|
8
|
+
/** Transaction executor only. Call in layer → flag point → experiment order;
|
|
9
|
+
* the manifest builder takes the state lock last, after transition writes.
|
|
10
|
+
*/
|
|
11
|
+
export declare function lockTransitionScope(exec: Executor, scope: {
|
|
12
|
+
experimentId: DocId;
|
|
13
|
+
layerId?: DocId | null;
|
|
14
|
+
flagPointId?: string | null;
|
|
15
|
+
}): Promise<void>;
|
package/dist/db/locks.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const LOCK_NAMESPACES = {
|
|
2
|
+
layer: 'xp:layer',
|
|
3
|
+
flagPoint: 'xp:flagpoint',
|
|
4
|
+
experiment: 'xp:experiment'
|
|
5
|
+
};
|
|
6
|
+
const LOCK_STATEMENTS = {
|
|
7
|
+
layer: `SELECT pg_advisory_xact_lock(hashtext('${LOCK_NAMESPACES.layer}'), hashtext($1))`,
|
|
8
|
+
flagPoint: `SELECT pg_advisory_xact_lock(hashtext('${LOCK_NAMESPACES.flagPoint}'), hashtext($1))`,
|
|
9
|
+
experiment: `SELECT pg_advisory_xact_lock(hashtext('${LOCK_NAMESPACES.experiment}'), hashtext($1))`
|
|
10
|
+
};
|
|
11
|
+
/** Transaction executor only. Call in layer → flag point → experiment order;
|
|
12
|
+
* the manifest builder takes the state lock last, after transition writes.
|
|
13
|
+
*/ export async function lockTransitionScope(exec, scope) {
|
|
14
|
+
if (scope.layerId != null) await exec.query(LOCK_STATEMENTS.layer, [
|
|
15
|
+
String(scope.layerId)
|
|
16
|
+
]);
|
|
17
|
+
if (scope.flagPointId) await exec.query(LOCK_STATEMENTS.flagPoint, [
|
|
18
|
+
scope.flagPointId
|
|
19
|
+
]);
|
|
20
|
+
await exec.query(LOCK_STATEMENTS.experiment, [
|
|
21
|
+
String(scope.experimentId)
|
|
22
|
+
]);
|
|
23
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Executor } from './pgExecutor.js';
|
|
2
|
+
import type { Manifest } from '../types/manifest.js';
|
|
3
|
+
export declare function readManifestRow(exec: Executor, options?: {
|
|
4
|
+
schemaName?: string;
|
|
5
|
+
}): Promise<{
|
|
6
|
+
revision: number;
|
|
7
|
+
manifest: Manifest;
|
|
8
|
+
} | null>;
|