@delmaredigital/payload-experiments 0.0.0-stage → 0.1.0-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (466) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +582 -2
  3. package/dist/access.d.ts +1 -0
  4. package/dist/access.js +2 -0
  5. package/dist/admin/AllocationField.d.ts +2 -0
  6. package/dist/admin/AllocationField.js +80 -0
  7. package/dist/admin/FlagPointSelect.d.ts +2 -0
  8. package/dist/admin/FlagPointSelect.js +54 -0
  9. package/dist/admin/LayerUsage.d.ts +2 -0
  10. package/dist/admin/LayerUsage.js +24 -0
  11. package/dist/admin/LockedFieldNotice.d.ts +2 -0
  12. package/dist/admin/LockedFieldNotice.js +26 -0
  13. package/dist/admin/MetricSourceSelect.d.ts +2 -0
  14. package/dist/admin/MetricSourceSelect.js +32 -0
  15. package/dist/admin/PhaseHistory.d.ts +2 -0
  16. package/dist/admin/PhaseHistory.js +76 -0
  17. package/dist/admin/PreviewLinks.d.ts +11 -0
  18. package/dist/admin/PreviewLinks.js +69 -0
  19. package/dist/admin/StatusCell.d.ts +3 -0
  20. package/dist/admin/StatusCell.js +19 -0
  21. package/dist/admin/SummaryCell.d.ts +2 -0
  22. package/dist/admin/SummaryCell.js +32 -0
  23. package/dist/admin/actions/AddMetricDialog.d.ts +6 -0
  24. package/dist/admin/actions/AddMetricDialog.js +138 -0
  25. package/dist/admin/actions/AllocateDialog.d.ts +6 -0
  26. package/dist/admin/actions/AllocateDialog.js +81 -0
  27. package/dist/admin/actions/ConfirmDialog.d.ts +14 -0
  28. package/dist/admin/actions/ConfirmDialog.js +192 -0
  29. package/dist/admin/actions/DecideDialog.d.ts +7 -0
  30. package/dist/admin/actions/DecideDialog.js +173 -0
  31. package/dist/admin/actions/ExperimentActions.d.ts +3 -0
  32. package/dist/admin/actions/ExperimentActions.js +110 -0
  33. package/dist/admin/actions/MakeContentResult.d.ts +6 -0
  34. package/dist/admin/actions/MakeContentResult.js +50 -0
  35. package/dist/admin/actions/actions.model.d.ts +37 -0
  36. package/dist/admin/actions/actions.model.js +71 -0
  37. package/dist/admin/actions/decide.model.d.ts +21 -0
  38. package/dist/admin/actions/decide.model.js +38 -0
  39. package/dist/admin/allocation.model.d.ts +20 -0
  40. package/dist/admin/allocation.model.js +38 -0
  41. package/dist/admin/api.d.ts +32 -0
  42. package/dist/admin/api.js +95 -0
  43. package/dist/admin/armSelect/ArmSelectField.d.ts +2 -0
  44. package/dist/admin/armSelect/ArmSelectField.js +185 -0
  45. package/dist/admin/armSelect/armSelect.model.d.ts +30 -0
  46. package/dist/admin/armSelect/armSelect.model.js +20 -0
  47. package/dist/admin/arms/ArmsField.d.ts +2 -0
  48. package/dist/admin/arms/ArmsField.js +310 -0
  49. package/dist/admin/arms/ParamsEditor.d.ts +16 -0
  50. package/dist/admin/arms/ParamsEditor.js +79 -0
  51. package/dist/admin/arms/arms.model.d.ts +21 -0
  52. package/dist/admin/arms/arms.model.js +153 -0
  53. package/dist/admin/arms/pageTarget.model.d.ts +4 -0
  54. package/dist/admin/arms/pageTarget.model.js +19 -0
  55. package/dist/admin/arms/params.model.d.ts +7 -0
  56. package/dist/admin/arms/params.model.js +35 -0
  57. package/dist/admin/copy/actions.d.ts +38 -0
  58. package/dist/admin/copy/actions.js +38 -0
  59. package/dist/admin/copy/armSelect.d.ts +11 -0
  60. package/dist/admin/copy/armSelect.js +11 -0
  61. package/dist/admin/copy/arms.d.ts +37 -0
  62. package/dist/admin/copy/arms.js +47 -0
  63. package/dist/admin/copy/common.d.ts +20 -0
  64. package/dist/admin/copy/common.js +27 -0
  65. package/dist/admin/copy/history.d.ts +9 -0
  66. package/dist/admin/copy/history.js +9 -0
  67. package/dist/admin/copy/metrics.d.ts +9 -0
  68. package/dist/admin/copy/metrics.js +9 -0
  69. package/dist/admin/copy/results.d.ts +78 -0
  70. package/dist/admin/copy/results.js +81 -0
  71. package/dist/admin/copy/review.d.ts +33 -0
  72. package/dist/admin/copy/review.js +127 -0
  73. package/dist/admin/copy/status.d.ts +8 -0
  74. package/dist/admin/copy/status.js +8 -0
  75. package/dist/admin/copy/tabs.d.ts +7 -0
  76. package/dist/admin/copy/tabs.js +7 -0
  77. package/dist/admin/copy/who.d.ts +36 -0
  78. package/dist/admin/copy/who.js +36 -0
  79. package/dist/admin/labels.d.ts +45 -0
  80. package/dist/admin/labels.js +53 -0
  81. package/dist/admin/layerUsage.model.d.ts +15 -0
  82. package/dist/admin/layerUsage.model.js +18 -0
  83. package/dist/admin/lockedNotice.model.d.ts +3 -0
  84. package/dist/admin/lockedNotice.model.js +18 -0
  85. package/dist/admin/metricSource.model.d.ts +7 -0
  86. package/dist/admin/metricSource.model.js +11 -0
  87. package/dist/admin/paths.d.ts +16 -0
  88. package/dist/admin/paths.js +16 -0
  89. package/dist/admin/permissions.model.d.ts +10 -0
  90. package/dist/admin/permissions.model.js +4 -0
  91. package/dist/admin/phaseHistory.model.d.ts +19 -0
  92. package/dist/admin/phaseHistory.model.js +19 -0
  93. package/dist/admin/reload.d.ts +1 -0
  94. package/dist/admin/reload.js +1 -0
  95. package/dist/admin/results/ArmTable.d.ts +8 -0
  96. package/dist/admin/results/ArmTable.js +112 -0
  97. package/dist/admin/results/CappedCell.d.ts +4 -0
  98. package/dist/admin/results/CappedCell.js +24 -0
  99. package/dist/admin/results/CiChart.d.ts +10 -0
  100. package/dist/admin/results/CiChart.js +178 -0
  101. package/dist/admin/results/DataQualityPanel.d.ts +4 -0
  102. package/dist/admin/results/DataQualityPanel.js +40 -0
  103. package/dist/admin/results/ResultsView.d.ts +3 -0
  104. package/dist/admin/results/ResultsView.js +312 -0
  105. package/dist/admin/results/VerdictCard.d.ts +9 -0
  106. package/dist/admin/results/VerdictCard.js +49 -0
  107. package/dist/admin/results/chart.model.d.ts +32 -0
  108. package/dist/admin/results/chart.model.js +52 -0
  109. package/dist/admin/results/format.d.ts +10 -0
  110. package/dist/admin/results/format.js +55 -0
  111. package/dist/admin/results/results.model.d.ts +105 -0
  112. package/dist/admin/results/results.model.js +175 -0
  113. package/dist/admin/review/ReviewPanel.d.ts +2 -0
  114. package/dist/admin/review/ReviewPanel.js +268 -0
  115. package/dist/admin/review/review.model.d.ts +31 -0
  116. package/dist/admin/review/review.model.js +40 -0
  117. package/dist/admin/status.model.d.ts +16 -0
  118. package/dist/admin/status.model.js +17 -0
  119. package/dist/admin/styles.css +10 -0
  120. package/dist/admin/styles.d.ts +1 -0
  121. package/dist/admin/styles.js +2 -0
  122. package/dist/admin/summaryBatcher.d.ts +8 -0
  123. package/dist/admin/summaryBatcher.js +33 -0
  124. package/dist/admin/targeting/TargetingAttributeField.d.ts +2 -0
  125. package/dist/admin/targeting/TargetingAttributeField.js +31 -0
  126. package/dist/admin/targeting/TargetingOpField.d.ts +2 -0
  127. package/dist/admin/targeting/TargetingOpField.js +45 -0
  128. package/dist/admin/targeting/TargetingValueField.d.ts +2 -0
  129. package/dist/admin/targeting/TargetingValueField.js +142 -0
  130. package/dist/admin/targeting/targeting.model.d.ts +18 -0
  131. package/dist/admin/targeting/targeting.model.js +48 -0
  132. package/dist/admin/useApi.d.ts +20 -0
  133. package/dist/admin/useApi.js +10 -0
  134. package/dist/admin/useLayerUsage.d.ts +15 -0
  135. package/dist/admin/useLayerUsage.js +92 -0
  136. package/dist/client/ExperimentPageview.d.ts +1 -0
  137. package/dist/client/ExperimentPageview.js +27 -0
  138. package/dist/client/ExperimentsProvider.d.ts +6 -0
  139. package/dist/client/ExperimentsProvider.js +22 -0
  140. package/dist/client/ViewportExposure.d.ts +9 -0
  141. package/dist/client/ViewportExposure.js +69 -0
  142. package/dist/client/beacon.d.ts +17 -0
  143. package/dist/client/beacon.js +71 -0
  144. package/dist/client/context.d.ts +2 -0
  145. package/dist/client/context.js +2 -0
  146. package/dist/client/idempotency.d.ts +2 -0
  147. package/dist/client/idempotency.js +13 -0
  148. package/dist/client/trackConversionBeacon.d.ts +3 -0
  149. package/dist/client/trackConversionBeacon.js +16 -0
  150. package/dist/client/useExperiment.d.ts +5 -0
  151. package/dist/client/useExperiment.js +27 -0
  152. package/dist/client/useFlagPoint.d.ts +9 -0
  153. package/dist/client/useFlagPoint.js +32 -0
  154. package/dist/collections/access.d.ts +21 -0
  155. package/dist/collections/access.js +25 -0
  156. package/dist/collections/events.d.ts +3 -0
  157. package/dist/collections/events.js +117 -0
  158. package/dist/collections/experiments/armInvariants.d.ts +12 -0
  159. package/dist/collections/experiments/armInvariants.js +89 -0
  160. package/dist/collections/experiments/config.d.ts +3 -0
  161. package/dist/collections/experiments/config.js +574 -0
  162. package/dist/collections/experiments/createDefaults.d.ts +2 -0
  163. package/dist/collections/experiments/createDefaults.js +7 -0
  164. package/dist/collections/experiments/deleteGuard.d.ts +3 -0
  165. package/dist/collections/experiments/deleteGuard.js +8 -0
  166. package/dist/collections/experiments/editability.d.ts +4 -0
  167. package/dist/collections/experiments/editability.js +35 -0
  168. package/dist/collections/experiments/flagControlParams.d.ts +2 -0
  169. package/dist/collections/experiments/flagControlParams.js +32 -0
  170. package/dist/collections/experiments/lockHook.d.ts +2 -0
  171. package/dist/collections/experiments/lockHook.js +134 -0
  172. package/dist/collections/identities.d.ts +3 -0
  173. package/dist/collections/identities.js +30 -0
  174. package/dist/collections/index.d.ts +6 -0
  175. package/dist/collections/index.js +24 -0
  176. package/dist/collections/layers.d.ts +3 -0
  177. package/dist/collections/layers.js +77 -0
  178. package/dist/collections/metrics.d.ts +3 -0
  179. package/dist/collections/metrics.js +115 -0
  180. package/dist/collections/phases.d.ts +3 -0
  181. package/dist/collections/phases.js +59 -0
  182. package/dist/collections/serialize.d.ts +5 -0
  183. package/dist/collections/serialize.js +40 -0
  184. package/dist/collections/snapshots.d.ts +3 -0
  185. package/dist/collections/snapshots.js +47 -0
  186. package/dist/constants.d.ts +7 -0
  187. package/dist/constants.js +7 -0
  188. package/dist/db/adapter.d.ts +3 -0
  189. package/dist/db/adapter.js +9 -0
  190. package/dist/db/appends.d.ts +58 -0
  191. package/dist/db/appends.js +116 -0
  192. package/dist/db/delete.d.ts +17 -0
  193. package/dist/db/delete.js +35 -0
  194. package/dist/db/erase.d.ts +12 -0
  195. package/dist/db/erase.js +55 -0
  196. package/dist/db/events.d.ts +21 -0
  197. package/dist/db/events.js +100 -0
  198. package/dist/db/executor.d.ts +10 -0
  199. package/dist/db/executor.js +37 -0
  200. package/dist/db/identities.d.ts +11 -0
  201. package/dist/db/identities.js +29 -0
  202. package/dist/db/locks.d.ts +15 -0
  203. package/dist/db/locks.js +23 -0
  204. package/dist/db/manifest.d.ts +8 -0
  205. package/dist/db/manifest.js +8 -0
  206. package/dist/db/names.d.ts +271 -0
  207. package/dist/db/names.js +270 -0
  208. package/dist/db/pgExecutor.d.ts +41 -0
  209. package/dist/db/pgExecutor.js +250 -0
  210. package/dist/db/results.d.ts +53 -0
  211. package/dist/db/results.js +103 -0
  212. package/dist/db/slices.d.ts +18 -0
  213. package/dist/db/slices.js +51 -0
  214. package/dist/db/snapshots.d.ts +47 -0
  215. package/dist/db/snapshots.js +89 -0
  216. package/dist/db/sql.d.ts +5 -0
  217. package/dist/db/sql.js +5 -0
  218. package/dist/db/state.d.ts +12 -0
  219. package/dist/db/state.js +27 -0
  220. package/dist/db/sweep.d.ts +13 -0
  221. package/dist/db/sweep.js +90 -0
  222. package/dist/db/txid.d.ts +7 -0
  223. package/dist/db/txid.js +12 -0
  224. package/dist/define.d.ts +3 -0
  225. package/dist/define.js +6 -0
  226. package/dist/endpoints/events.d.ts +7 -0
  227. package/dist/endpoints/events.js +98 -0
  228. package/dist/endpoints/index.d.ts +18 -0
  229. package/dist/endpoints/index.js +160 -0
  230. package/dist/endpoints/manifest.d.ts +7 -0
  231. package/dist/endpoints/manifest.js +30 -0
  232. package/dist/endpoints/preflight.d.ts +8 -0
  233. package/dist/endpoints/preflight.js +25 -0
  234. package/dist/endpoints/previewToken.d.ts +7 -0
  235. package/dist/endpoints/previewToken.js +54 -0
  236. package/dist/endpoints/recompute.d.ts +7 -0
  237. package/dist/endpoints/recompute.js +41 -0
  238. package/dist/endpoints/results.d.ts +24 -0
  239. package/dist/endpoints/results.js +136 -0
  240. package/dist/endpoints/summary.d.ts +7 -0
  241. package/dist/endpoints/summary.js +40 -0
  242. package/dist/endpoints/transitions.d.ts +42 -0
  243. package/dist/endpoints/transitions.js +154 -0
  244. package/dist/errors.d.ts +27 -0
  245. package/dist/errors.js +33 -0
  246. package/dist/events/body.d.ts +7 -0
  247. package/dist/events/body.js +43 -0
  248. package/dist/events/dimensions.d.ts +4 -0
  249. package/dist/events/dimensions.js +51 -0
  250. package/dist/events/preview.d.ts +2 -0
  251. package/dist/events/preview.js +9 -0
  252. package/dist/events/request.d.ts +3 -0
  253. package/dist/events/request.js +14 -0
  254. package/dist/events/rows.d.ts +7 -0
  255. package/dist/events/rows.js +65 -0
  256. package/dist/exports/adminClient.d.ts +16 -0
  257. package/dist/exports/adminClient.js +17 -0
  258. package/dist/exports/client.d.ts +7 -0
  259. package/dist/exports/client.js +7 -0
  260. package/dist/exports/index.d.ts +4 -0
  261. package/dist/exports/index.js +3 -0
  262. package/dist/exports/proxy.d.ts +4 -0
  263. package/dist/exports/proxy.js +3 -0
  264. package/dist/exports/puck.d.ts +4 -0
  265. package/dist/exports/puck.js +3 -0
  266. package/dist/exports/puckEditor.d.ts +1 -0
  267. package/dist/exports/puckEditor.js +2 -0
  268. package/dist/exports/rsc.d.ts +13 -0
  269. package/dist/exports/rsc.js +13 -0
  270. package/dist/exports/runtimeProxy.d.ts +4 -0
  271. package/dist/exports/runtimeProxy.js +2 -0
  272. package/dist/exports/runtimeServer.d.ts +2 -0
  273. package/dist/exports/runtimeServer.js +1 -0
  274. package/dist/fields/armSelect.d.ts +7 -0
  275. package/dist/fields/armSelect.js +33 -0
  276. package/dist/globals/state.d.ts +3 -0
  277. package/dist/globals/state.js +31 -0
  278. package/dist/hooks/contentChanges.d.ts +3 -0
  279. package/dist/hooks/contentChanges.js +10 -0
  280. package/dist/hooks/hostHook.d.ts +5 -0
  281. package/dist/hooks/hostHook.js +20 -0
  282. package/dist/index.d.ts +1 -0
  283. package/dist/index.js +4 -0
  284. package/dist/manifest/cache.d.ts +10 -0
  285. package/dist/manifest/cache.js +51 -0
  286. package/dist/manifest/compile.d.ts +63 -0
  287. package/dist/manifest/compile.js +237 -0
  288. package/dist/onInit.d.ts +5 -0
  289. package/dist/onInit.js +13 -0
  290. package/dist/options/fromReq.d.ts +3 -0
  291. package/dist/options/fromReq.js +5 -0
  292. package/dist/options.d.ts +9 -0
  293. package/dist/options.js +99 -0
  294. package/dist/plugin.d.ts +7 -0
  295. package/dist/plugin.js +62 -0
  296. package/dist/proxy/apply.d.ts +3 -0
  297. package/dist/proxy/apply.js +5 -0
  298. package/dist/proxy/consentFallback.d.ts +5 -0
  299. package/dist/proxy/consentFallback.js +27 -0
  300. package/dist/proxy/control.d.ts +6 -0
  301. package/dist/proxy/control.js +36 -0
  302. package/dist/proxy/createProxy.d.ts +3 -0
  303. package/dist/proxy/createProxy.js +15 -0
  304. package/dist/proxy/pageRewrite.d.ts +4 -0
  305. package/dist/proxy/pageRewrite.js +41 -0
  306. package/dist/proxy/resolve.d.ts +13 -0
  307. package/dist/proxy/resolve.js +163 -0
  308. package/dist/proxy/skip.d.ts +1 -0
  309. package/dist/proxy/skip.js +9 -0
  310. package/dist/puck/ExperimentPicker.d.ts +5 -0
  311. package/dist/puck/ExperimentPicker.js +88 -0
  312. package/dist/puck/ExperimentSlot.d.ts +3 -0
  313. package/dist/puck/ExperimentSlot.js +16 -0
  314. package/dist/puck/copy.d.ts +14 -0
  315. package/dist/puck/copy.js +14 -0
  316. package/dist/puck/editor/CreateExperimentDrawer.d.ts +5 -0
  317. package/dist/puck/editor/CreateExperimentDrawer.js +120 -0
  318. package/dist/puck/editor/ExperimentSlotEditor.d.ts +3 -0
  319. package/dist/puck/editor/ExperimentSlotEditor.js +225 -0
  320. package/dist/puck/editor/cloneSlot.d.ts +3 -0
  321. package/dist/puck/editor/cloneSlot.js +41 -0
  322. package/dist/puck/editor/copy.d.ts +25 -0
  323. package/dist/puck/editor/copy.js +25 -0
  324. package/dist/puck/editor/editor.model.d.ts +16 -0
  325. package/dist/puck/editor/editor.model.js +33 -0
  326. package/dist/puck/experimentSlotField.d.ts +5 -0
  327. package/dist/puck/experimentSlotField.js +13 -0
  328. package/dist/puck/slot.model.d.ts +2 -0
  329. package/dist/puck/slot.model.js +11 -0
  330. package/dist/puck/slotConfig.d.ts +5 -0
  331. package/dist/puck/slotConfig.js +25 -0
  332. package/dist/results/aggregate.d.ts +34 -0
  333. package/dist/results/aggregate.js +66 -0
  334. package/dist/results/analyse.d.ts +79 -0
  335. package/dist/results/analyse.js +378 -0
  336. package/dist/results/cuped.d.ts +16 -0
  337. package/dist/results/cuped.js +31 -0
  338. package/dist/results/decideGuard.d.ts +3 -0
  339. package/dist/results/decideGuard.js +8 -0
  340. package/dist/results/guardrails.d.ts +17 -0
  341. package/dist/results/guardrails.js +10 -0
  342. package/dist/results/history.d.ts +13 -0
  343. package/dist/results/history.js +39 -0
  344. package/dist/results/identityCredit.d.ts +16 -0
  345. package/dist/results/identityCredit.js +46 -0
  346. package/dist/results/power.d.ts +15 -0
  347. package/dist/results/power.js +60 -0
  348. package/dist/results/rolloutUnits.d.ts +17 -0
  349. package/dist/results/rolloutUnits.js +26 -0
  350. package/dist/results/snapshot.d.ts +9 -0
  351. package/dist/results/snapshot.js +154 -0
  352. package/dist/results/srm.d.ts +9 -0
  353. package/dist/results/srm.js +22 -0
  354. package/dist/results/verdict.d.ts +82 -0
  355. package/dist/results/verdict.js +85 -0
  356. package/dist/runtime/assign.d.ts +23 -0
  357. package/dist/runtime/assign.js +104 -0
  358. package/dist/runtime/bot.d.ts +1 -0
  359. package/dist/runtime/bot.js +7 -0
  360. package/dist/runtime/cookie.d.ts +17 -0
  361. package/dist/runtime/cookie.js +35 -0
  362. package/dist/runtime/eligibility.d.ts +22 -0
  363. package/dist/runtime/eligibility.js +20 -0
  364. package/dist/runtime/header.d.ts +17 -0
  365. package/dist/runtime/header.js +97 -0
  366. package/dist/runtime/loaders.d.ts +17 -0
  367. package/dist/runtime/loaders.js +29 -0
  368. package/dist/runtime/normalisePath.d.ts +7 -0
  369. package/dist/runtime/normalisePath.js +15 -0
  370. package/dist/runtime/previewToken.d.ts +9 -0
  371. package/dist/runtime/previewToken.js +23 -0
  372. package/dist/runtime/proxyRuntime.d.ts +2 -0
  373. package/dist/runtime/proxyRuntime.js +22 -0
  374. package/dist/runtime/seed.d.ts +2 -0
  375. package/dist/runtime/seed.js +4 -0
  376. package/dist/runtime/signing.d.ts +3 -0
  377. package/dist/runtime/signing.js +16 -0
  378. package/dist/runtime/targeting.d.ts +6 -0
  379. package/dist/runtime/targeting.js +9 -0
  380. package/dist/server/context.d.ts +10 -0
  381. package/dist/server/context.js +59 -0
  382. package/dist/server/eraseUser.d.ts +7 -0
  383. package/dist/server/eraseUser.js +4 -0
  384. package/dist/server/getAssignments.d.ts +9 -0
  385. package/dist/server/getAssignments.js +227 -0
  386. package/dist/server/getFlagPoint.d.ts +11 -0
  387. package/dist/server/getFlagPoint.js +36 -0
  388. package/dist/server/headerRevision.d.ts +6 -0
  389. package/dist/server/headerRevision.js +10 -0
  390. package/dist/server/linkIdentity.d.ts +9 -0
  391. package/dist/server/linkIdentity.js +34 -0
  392. package/dist/server/metadata.d.ts +7 -0
  393. package/dist/server/metadata.js +24 -0
  394. package/dist/server/previewDraft.d.ts +7 -0
  395. package/dist/server/previewDraft.js +51 -0
  396. package/dist/server/recordContentChange.d.ts +7 -0
  397. package/dist/server/recordContentChange.js +150 -0
  398. package/dist/server/recordExposures.d.ts +7 -0
  399. package/dist/server/recordExposures.js +56 -0
  400. package/dist/server/request.d.ts +7 -0
  401. package/dist/server/request.js +39 -0
  402. package/dist/server/resolveExperimentData.d.ts +6 -0
  403. package/dist/server/resolveExperimentData.js +64 -0
  404. package/dist/server/runResults.d.ts +22 -0
  405. package/dist/server/runResults.js +307 -0
  406. package/dist/server/serverRuntime.d.ts +7 -0
  407. package/dist/server/serverRuntime.js +20 -0
  408. package/dist/server/sitemap.d.ts +3 -0
  409. package/dist/server/sitemap.js +6 -0
  410. package/dist/server/toClientAssignments.d.ts +7 -0
  411. package/dist/server/toClientAssignments.js +56 -0
  412. package/dist/server/trackConversion.d.ts +12 -0
  413. package/dist/server/trackConversion.js +69 -0
  414. package/dist/targeting/ops.d.ts +8 -0
  415. package/dist/targeting/ops.js +58 -0
  416. package/dist/transitions/addMetric.d.ts +16 -0
  417. package/dist/transitions/addMetric.js +126 -0
  418. package/dist/transitions/allocate.d.ts +9 -0
  419. package/dist/transitions/allocate.js +84 -0
  420. package/dist/transitions/archive.d.ts +9 -0
  421. package/dist/transitions/archive.js +46 -0
  422. package/dist/transitions/checkStart.d.ts +61 -0
  423. package/dist/transitions/checkStart.js +104 -0
  424. package/dist/transitions/confirmPhrases.d.ts +5 -0
  425. package/dist/transitions/confirmPhrases.js +18 -0
  426. package/dist/transitions/decide.d.ts +9 -0
  427. package/dist/transitions/decide.js +138 -0
  428. package/dist/transitions/deleteExperiment.d.ts +5 -0
  429. package/dist/transitions/deleteExperiment.js +66 -0
  430. package/dist/transitions/kernel.d.ts +31 -0
  431. package/dist/transitions/kernel.js +69 -0
  432. package/dist/transitions/makeContent.d.ts +20 -0
  433. package/dist/transitions/makeContent.js +411 -0
  434. package/dist/transitions/phaseRecord.d.ts +27 -0
  435. package/dist/transitions/phaseRecord.js +81 -0
  436. package/dist/transitions/placements.d.ts +26 -0
  437. package/dist/transitions/placements.js +89 -0
  438. package/dist/transitions/preflightData.d.ts +21 -0
  439. package/dist/transitions/preflightData.js +317 -0
  440. package/dist/transitions/releaseHoldout.d.ts +8 -0
  441. package/dist/transitions/releaseHoldout.js +30 -0
  442. package/dist/transitions/resolveCaps.d.ts +28 -0
  443. package/dist/transitions/resolveCaps.js +68 -0
  444. package/dist/transitions/start.d.ts +26 -0
  445. package/dist/transitions/start.js +220 -0
  446. package/dist/transitions/stop.d.ts +8 -0
  447. package/dist/transitions/stop.js +25 -0
  448. package/dist/types/admin.d.ts +20 -0
  449. package/dist/types/admin.js +1 -0
  450. package/dist/types/assignments.d.ts +57 -0
  451. package/dist/types/assignments.js +1 -0
  452. package/dist/types/contracts.d.ts +133 -0
  453. package/dist/types/contracts.js +2 -0
  454. package/dist/types/endpoints.d.ts +135 -0
  455. package/dist/types/endpoints.js +1 -0
  456. package/dist/types/index.d.ts +8 -0
  457. package/dist/types/index.js +8 -0
  458. package/dist/types/manifest.d.ts +42 -0
  459. package/dist/types/manifest.js +1 -0
  460. package/dist/types/puck.d.ts +23 -0
  461. package/dist/types/puck.js +1 -0
  462. package/dist/types/runtime.d.ts +59 -0
  463. package/dist/types/runtime.js +1 -0
  464. package/dist/types/snapshot.d.ts +117 -0
  465. package/dist/types/snapshot.js +1 -0
  466. package/package.json +137 -3
@@ -0,0 +1,66 @@
1
+ import { txExecutor } from '../db/executor.js';
2
+ import { deleteExperimentSnapshots, serializeDelete } from '../db/delete.js';
3
+ import { deleteExperimentEvents } from '../db/events.js';
4
+ import { lockTransitionScope } from '../db/locks.js';
5
+ import { buildAndStoreManifest } from '../manifest/compile.js';
6
+ import { readExperiment, refuse } from './start.js';
7
+ /** Payload deleteByID owns the request transaction; never commit inside hooks. */ export async function deleteExperiment(req, experimentId) {
8
+ await serializeDelete(req, async (state)=>{
9
+ const exec = txExecutor(req);
10
+ await lockTransitionScope(exec, {
11
+ experimentId
12
+ });
13
+ const current = await readExperiment(req, experimentId);
14
+ if (current.status !== 'draft' && current.status !== 'archived') refuse('invalid-status');
15
+ try {
16
+ const collection = 'xp-phases';
17
+ // §7.8 transition exception: cascade while FK references still exist.
18
+ const { docs } = await req.payload.find({
19
+ collection,
20
+ req,
21
+ overrideAccess: true,
22
+ depth: 0,
23
+ limit: 0,
24
+ where: {
25
+ experiment: {
26
+ equals: experimentId
27
+ }
28
+ }
29
+ });
30
+ // deleteByID throws on a hook failure; bulk delete swallows per-doc errors.
31
+ for (const doc of docs){
32
+ // §7.8: privileged plugin cascade shares the access-checked delete's req.
33
+ await req.payload.delete({
34
+ collection,
35
+ id: doc.id,
36
+ req,
37
+ overrideAccess: true
38
+ });
39
+ }
40
+ await deleteExperimentSnapshots(exec, experimentId);
41
+ await deleteExperimentEvents(exec, experimentId);
42
+ state.excludedIds.set(String(experimentId), experimentId);
43
+ const decision = current.decision;
44
+ if (current.status !== 'archived' || decision?.discardedAt) state.pruneFlagDefaultIds.set(String(experimentId), experimentId);
45
+ } catch (error) {
46
+ req.payload.logger.error({
47
+ experimentId,
48
+ err: error
49
+ }, 'Experiment cascade failed; aborting delete');
50
+ throw error;
51
+ }
52
+ });
53
+ }
54
+ export async function deleteExperimentAfter(req, experimentId) {
55
+ await serializeDelete(req, async (state)=>{
56
+ await buildAndStoreManifest(req, {
57
+ excludeIds: [
58
+ ...state.excludedIds.values(),
59
+ experimentId
60
+ ],
61
+ pruneFlagDefaultIds: [
62
+ ...state.pruneFlagDefaultIds.values()
63
+ ]
64
+ });
65
+ });
66
+ }
@@ -0,0 +1,31 @@
1
+ import { type PayloadRequest } from 'payload';
2
+ import { type Executor } from '../db/executor.js';
3
+ import type { DocId } from '../types/contracts.js';
4
+ export interface TransitionScope {
5
+ experimentId: DocId;
6
+ layerId?: DocId | null;
7
+ flagPointId?: string | null;
8
+ /** Defaults to true; content-change audit appends opt out. */
9
+ bump?: boolean;
10
+ /** Best-effort content audit only; a borrowed transaction must never be killed here. */
11
+ audit?: boolean;
12
+ excludeIds?: DocId[];
13
+ }
14
+ /** Runs transition writes on the request's transaction under ordered locks.
15
+ * Returns the callback result and, unless bump is false, the stored manifest's
16
+ * revision. Only the transaction owner commits and verifies that transaction's
17
+ * status on the pool (even without a bump); a caller-owned transaction still
18
+ * needs its caller to commit. The explicit borrowed audit path takes only lock E
19
+ * inside a savepoint. ROLLBACK TO SAVEPOINT releases that transaction advisory
20
+ * lock (Postgres §13.3); after RELEASE it remains held until the host commits.
21
+ * Audit failures never kill the host transaction, even if savepoint recovery
22
+ * itself fails: the host must observe its own failed commit. Other failures kill
23
+ * even a borrowed transaction. Owned audits follow the ordinary owned path.
24
+ */
25
+ export declare function withTransition<T>(req: PayloadRequest, scope: TransitionScope, fn: (ctx: {
26
+ exec: Executor;
27
+ req: PayloadRequest;
28
+ }) => Promise<T>): Promise<{
29
+ result: T;
30
+ revision?: number;
31
+ }>;
@@ -0,0 +1,69 @@
1
+ import { commitTransaction, initTransaction, killTransaction } from 'payload';
2
+ import { pooledExecutor, txExecutor } from '../db/executor.js';
3
+ import { lockTransitionScope } from '../db/locks.js';
4
+ import { currentTxid, txidStatus } from '../db/txid.js';
5
+ import { contentChangeSavepoint } from '../db/appends.js';
6
+ import { XpRefusal } from '../errors.js';
7
+ import { buildAndStoreManifest } from '../manifest/compile.js';
8
+ /** Runs transition writes on the request's transaction under ordered locks.
9
+ * Returns the callback result and, unless bump is false, the stored manifest's
10
+ * revision. Only the transaction owner commits and verifies that transaction's
11
+ * status on the pool (even without a bump); a caller-owned transaction still
12
+ * needs its caller to commit. The explicit borrowed audit path takes only lock E
13
+ * inside a savepoint. ROLLBACK TO SAVEPOINT releases that transaction advisory
14
+ * lock (Postgres §13.3); after RELEASE it remains held until the host commits.
15
+ * Audit failures never kill the host transaction, even if savepoint recovery
16
+ * itself fails: the host must observe its own failed commit. Other failures kill
17
+ * even a borrowed transaction. Owned audits follow the ordinary owned path.
18
+ */ export async function withTransition(req, scope, fn) {
19
+ const hadTransition = Object.prototype.hasOwnProperty.call(req.context, 'xpTransition');
20
+ const previousTransition = req.context.xpTransition;
21
+ let borrowedAudit = scope.audit === true && req.transactionID != null;
22
+ try {
23
+ const owns = await initTransaction(req);
24
+ borrowedAudit = scope.audit === true && !owns;
25
+ const exec = txExecutor(req);
26
+ req.context.xpTransition = true;
27
+ const work = async ()=>{
28
+ await lockTransitionScope(exec, scope);
29
+ return fn({
30
+ exec,
31
+ req
32
+ });
33
+ };
34
+ let result;
35
+ if (borrowedAudit) {
36
+ const audit = await contentChangeSavepoint(exec, work);
37
+ if (!audit.ok) {
38
+ throw audit.error;
39
+ }
40
+ result = audit.result;
41
+ } else result = await work();
42
+ const revision = scope.bump === false ? undefined : (await buildAndStoreManifest(req, {
43
+ excludeIds: scope.excludeIds
44
+ })).revision;
45
+ if (owns) {
46
+ const xid = await currentTxid(exec);
47
+ await commitTransaction(req);
48
+ const status = await txidStatus(pooledExecutor(req.payload), xid);
49
+ if (status !== 'committed') {
50
+ throw new XpRefusal('commit-unverified', 'The transition commit could not be verified.', {
51
+ xid,
52
+ status
53
+ }, 500);
54
+ }
55
+ }
56
+ return revision === undefined ? {
57
+ result
58
+ } : {
59
+ result,
60
+ revision
61
+ };
62
+ } catch (error) {
63
+ if (!borrowedAudit) await killTransaction(req);
64
+ throw error;
65
+ } finally{
66
+ if (hadTransition) req.context.xpTransition = previousTransition;
67
+ else delete req.context.xpTransition;
68
+ }
69
+ }
@@ -0,0 +1,20 @@
1
+ import { type PayloadRequest } from 'payload';
2
+ import type { DocId, ResolvedOptions } from '../types/contracts.js';
3
+ type ContentDoc = {
4
+ collection: string;
5
+ id: DocId;
6
+ ok: boolean;
7
+ detail?: string;
8
+ error?: string;
9
+ };
10
+ export declare function makeContent<TUser extends {
11
+ id: DocId;
12
+ }>(req: PayloadRequest, { experimentId, options }: {
13
+ experimentId: DocId;
14
+ options: ResolvedOptions<TUser>;
15
+ }): Promise<{
16
+ docs: ContentDoc[];
17
+ doc: import("payload").JsonObject & import("payload").TypeWithID;
18
+ revision: number;
19
+ }>;
20
+ export {};
@@ -0,0 +1,411 @@
1
+ import { commitTransaction, initTransaction, killTransaction } from 'payload';
2
+ import { lockContentDefaults } from '../db/appends.js';
3
+ import { pooledExecutor, txExecutor } from '../db/executor.js';
4
+ import { currentTxid, txidStatus } from '../db/txid.js';
5
+ import { buildAndStoreManifest } from '../manifest/compile.js';
6
+ import { puckCollections } from '../options.js';
7
+ import { archive } from './archive.js';
8
+ import { relationshipId } from './checkStart.js';
9
+ import { withTransition } from './kernel.js';
10
+ import { scanBlockPlacements } from './placements.js';
11
+ import { readExperiment, refuse, updateExperiment } from './start.js';
12
+ const object = (value)=>value !== null && typeof value === 'object' && !Array.isArray(value);
13
+ /** Flatten selected slots in every content array, including nested selected slots. */ function unwrap(value, slug, slot) {
14
+ if (Array.isArray(value)) return value.flatMap((node)=>{
15
+ if (object(node) && node.type === 'ExperimentSlot' && object(node.props)) {
16
+ const ref = node.props.experiment;
17
+ if ((typeof ref === 'string' ? ref : object(ref) ? ref.slug : undefined) === slug) {
18
+ const content = node.props[slot];
19
+ if (!Array.isArray(content)) throw new Error('The winning content slot is missing.');
20
+ return unwrap(content, slug, slot);
21
+ }
22
+ }
23
+ return [
24
+ unwrap(node, slug, slot)
25
+ ];
26
+ });
27
+ if (object(value)) return Object.fromEntries(Object.entries(value).map(([key, child])=>[
28
+ key,
29
+ unwrap(child, slug, slot)
30
+ ]));
31
+ return value;
32
+ }
33
+ /** Payload update merges omissions from the latest version even on publish.
34
+ * Restore each captured document's fields, omitting API/adapter metadata.
35
+ */ function contentData(doc, field, content) {
36
+ const metadata = new Set([
37
+ 'id',
38
+ 'createdAt',
39
+ 'updatedAt',
40
+ '_isLocked',
41
+ '_userEditing'
42
+ ]);
43
+ return {
44
+ ...Object.fromEntries(Object.entries(doc).filter(([key])=>!metadata.has(key))),
45
+ [field]: content
46
+ };
47
+ }
48
+ /** Refuse unsafe collection shapes before rebuilding complete host documents. */ async function fieldRefusal(fields, req, doc, operation = 'read', data = doc, siblingData = data, blockData, seen = new Set()) {
49
+ for (const field of fields){
50
+ if (seen.has(field)) continue;
51
+ const branch = new Set(seen).add(field);
52
+ if ('localized' in field && field.localized) return 'This page is localized; update it by hand.';
53
+ if ('hidden' in field && field.hidden === true) return 'This page has hidden fields; update it by hand.';
54
+ if ('access' in field && field.access?.[operation] && !await field.access[operation]({
55
+ id: doc.id,
56
+ req,
57
+ doc,
58
+ data,
59
+ siblingData,
60
+ blockData
61
+ })) {
62
+ return operation === 'read' ? 'This page has fields you cannot read; update it by hand.' : 'This page has fields you cannot update; update it by hand.';
63
+ }
64
+ const value = 'name' in field ? siblingData[field.name] : siblingData;
65
+ if ('tabs' in field) for (const tab of field.tabs){
66
+ if ('localized' in tab && tab.localized) return 'This page is localized; update it by hand.';
67
+ if ('hidden' in tab && tab.hidden === true) return 'This page has hidden fields; update it by hand.';
68
+ if ('access' in tab && tab.access?.[operation] && !await tab.access[operation]({
69
+ id: doc.id,
70
+ req,
71
+ doc,
72
+ data,
73
+ siblingData,
74
+ blockData
75
+ })) {
76
+ return operation === 'read' ? 'This page has fields you cannot read; update it by hand.' : 'This page has fields you cannot update; update it by hand.';
77
+ }
78
+ const tabData = 'name' in tab && tab.name ? siblingData[tab.name] : siblingData;
79
+ const refusal = await fieldRefusal(tab.fields, req, doc, operation, data, object(tabData) ? tabData : {}, blockData, branch);
80
+ if (refusal) return refusal;
81
+ }
82
+ if ('fields' in field) for (const row of Array.isArray(value) && value.length ? value : [
83
+ value
84
+ ]){
85
+ const refusal = await fieldRefusal(field.fields, req, doc, operation, data, object(row) ? row : {}, blockData, branch);
86
+ if (refusal) return refusal;
87
+ }
88
+ if (field.type === 'blocks') {
89
+ const blocks = [
90
+ ...field.blocks,
91
+ ...(field.blockReferences ?? []).map((slug)=>req.payload.config.blocks?.find((block)=>block.slug === slug)).filter((block)=>block !== undefined)
92
+ ];
93
+ for (const block of blocks){
94
+ const rows = Array.isArray(value) ? value.filter((row)=>object(row) && row.blockType === block.slug) : [];
95
+ for (const row of rows.length ? rows : [
96
+ {}
97
+ ]){
98
+ const refusal = await fieldRefusal(block.fields, req, doc, operation, data, row, row, branch);
99
+ if (refusal) return refusal;
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
+ /** Each placement commits independently while the outer transition retains its
106
+ * experiment locks. Keep the manager's exact req/user, temporarily lending it a
107
+ * fresh transaction; a denied host write must not kill the outer transaction.
108
+ */ async function hostTransaction(req, fn) {
109
+ const transactionID = req.transactionID;
110
+ const transition = req.context.xpTransition;
111
+ delete req.transactionID;
112
+ delete req.context.xpTransition;
113
+ try {
114
+ if (!await initTransaction(req)) throw new Error('A content transaction could not be started.');
115
+ await fn();
116
+ const xid = await currentTxid(txExecutor(req));
117
+ await commitTransaction(req);
118
+ if (await txidStatus(pooledExecutor(req.payload), xid) !== 'committed') throw new Error('The content commit could not be verified. Retry to check the saved content.');
119
+ } catch (error) {
120
+ await killTransaction(req);
121
+ throw error;
122
+ } finally{
123
+ req.transactionID = transactionID;
124
+ if (transition === undefined) delete req.context.xpTransition;
125
+ else req.context.xpTransition = transition;
126
+ }
127
+ }
128
+ export async function makeContent(req, { experimentId, options }) {
129
+ const before = await readExperiment(req, experimentId);
130
+ const { result } = await withTransition(req, {
131
+ experimentId,
132
+ layerId: relationshipId(before.layer),
133
+ flagPointId: before.kind === 'flag' ? before.flagPoint : undefined,
134
+ bump: false
135
+ }, async ({ exec })=>{
136
+ const current = await readExperiment(req, experimentId);
137
+ if (current.status !== 'decided' || ![
138
+ 'block',
139
+ 'flag',
140
+ 'page'
141
+ ].includes(current.kind)) refuse('invalid-status');
142
+ const decision = current.decision;
143
+ const winner = current.arms.find((arm)=>arm.key === decision?.winnerArm && arm.kind !== 'holdout');
144
+ if (!winner) refuse('invalid-winner');
145
+ const docs = [];
146
+ const attempt = async (collection, id, fn)=>{
147
+ try {
148
+ await hostTransaction(req, fn);
149
+ docs.push({
150
+ collection,
151
+ id,
152
+ ok: true
153
+ });
154
+ } catch (error) {
155
+ const detail = error instanceof Error ? error.message : 'The content could not be updated.';
156
+ docs.push({
157
+ collection,
158
+ id,
159
+ ok: false,
160
+ detail,
161
+ error: detail
162
+ });
163
+ }
164
+ };
165
+ if (current.kind === 'block') {
166
+ if (!winner.slot) refuse('invalid-winner');
167
+ const report = decision?.contentReport;
168
+ const registrations = puckCollections(options);
169
+ const scanned = [];
170
+ for (const registration of registrations){
171
+ const transactionID = req.transactionID;
172
+ delete req.transactionID;
173
+ try {
174
+ scanned.push(...await scanBlockPlacements(req, {
175
+ slug: current.slug,
176
+ puckCollections: [
177
+ registration
178
+ ]
179
+ }));
180
+ } catch (error) {
181
+ // A collection made unreadable since the last attempt still has
182
+ // retained placements to report below. Never sacrifice the outer tx.
183
+ const denied = error && typeof error === 'object' && 'status' in error && error.status === 403;
184
+ if (!denied || !report?.docs?.some((doc)=>doc.collection === registration.slug)) throw error;
185
+ } finally{
186
+ req.transactionID = transactionID;
187
+ }
188
+ }
189
+ if (scanned.some((doc)=>doc.locked)) refuse('locked-placement');
190
+ const placements = new Map();
191
+ for (const doc of [
192
+ ...report?.docs ?? [],
193
+ ...scanned
194
+ ])placements.set(doc.collection + ':' + doc.id, doc);
195
+ const skipped = new Set();
196
+ // Probe with no borrowed transaction: disableErrors handles expected
197
+ // NotFound/Forbidden, and even a host read hook cannot kill the outer tx.
198
+ for (const [key, { collection, id }] of placements){
199
+ const transactionID = req.transactionID;
200
+ delete req.transactionID;
201
+ try {
202
+ const config = req.payload.config.collections.find((config)=>config.slug === collection);
203
+ const access = config?.access?.read ? await config.access.read({
204
+ id,
205
+ req
206
+ }) : true;
207
+ const doc = await req.payload.findByID({
208
+ collection,
209
+ id,
210
+ req,
211
+ overrideAccess: false,
212
+ depth: 0,
213
+ draft: true,
214
+ includeLockStatus: true,
215
+ disableErrors: true
216
+ });
217
+ if (!doc) {
218
+ // Fail closed (exp-orch 16:20): only an unfiltered read proves the page is gone;
219
+ // a Where-filtered miss may be a page this manager cannot see.
220
+ const ok = access === true || typeof access === 'object' && access !== null && Object.keys(access).length === 0;
221
+ const detail = ok ? 'This page no longer exists; nothing to change.' : access === false ? 'You can no longer read this page.' : 'You can no longer read this page. If it was deleted, archive this experiment with discard or hard-coded instead.';
222
+ docs.push({
223
+ collection,
224
+ id,
225
+ ok,
226
+ detail,
227
+ ...!ok ? {
228
+ error: detail
229
+ } : {}
230
+ });
231
+ skipped.add(key);
232
+ } else if (doc._isLocked === true || doc.locked === true) refuse('locked-placement', {
233
+ collection,
234
+ id
235
+ });
236
+ } catch (error) {
237
+ if (error && typeof error === 'object' && 'code' in error && error.code === 'locked-placement') throw error;
238
+ const missing = error && typeof error === 'object' && 'status' in error && error.status === 404;
239
+ const detail = missing ? 'This page no longer exists; nothing to change.' : 'You can no longer read this page.';
240
+ docs.push({
241
+ collection,
242
+ id,
243
+ ok: Boolean(missing),
244
+ detail,
245
+ ...!missing ? {
246
+ error: detail
247
+ } : {}
248
+ });
249
+ skipped.add(key);
250
+ } finally{
251
+ req.transactionID = transactionID;
252
+ }
253
+ }
254
+ for (const [key, { collection, id }] of placements){
255
+ if (skipped.has(key)) continue;
256
+ await attempt(collection, id, async ()=>{
257
+ const field = registrations.find((entry)=>entry.slug === collection)?.field;
258
+ if (!field) throw new Error('This content collection is no longer configured.');
259
+ const draft = await req.payload.findByID({
260
+ collection,
261
+ id,
262
+ req,
263
+ overrideAccess: false,
264
+ depth: 0,
265
+ draft: true
266
+ });
267
+ const config = req.payload.config.collections.find((config)=>config.slug === collection);
268
+ const drafts = Boolean(config?.versions && config.versions.drafts);
269
+ const published = drafts ? await req.payload.findByID({
270
+ collection,
271
+ id,
272
+ req,
273
+ overrideAccess: false,
274
+ depth: 0,
275
+ draft: false
276
+ }) : draft;
277
+ const draftContent = unwrap(draft[field], current.slug, winner.slot);
278
+ const publishedContent = unwrap(published[field], current.slug, winner.slot);
279
+ const changedDraft = JSON.stringify(draftContent) !== JSON.stringify(draft[field]);
280
+ const changedPublished = JSON.stringify(publishedContent) !== JSON.stringify(published[field]);
281
+ // Nothing left to rewrite (e.g. an editor already did it by hand): done, before any refusal —
282
+ // but only when the content field was really read (not stripped as hidden/unreadable, not localized).
283
+ const contentField = topLevelField(config?.fields ?? [], field);
284
+ // Payload strips hidden and read-denied fields from a read, so presence in both proves readability;
285
+ // a localized content field compares only one locale, so it never takes the shortcut.
286
+ const contentReadable = Object.hasOwn(draft, field) && Object.hasOwn(published, field) && !contentField?.localized;
287
+ if (contentReadable && !changedDraft && !changedPublished) return;
288
+ for (const doc of [
289
+ draft,
290
+ published
291
+ ]){
292
+ const refusal = await fieldRefusal(config?.fields ?? [], req, doc);
293
+ if (refusal) throw new Error(refusal);
294
+ }
295
+ const publishedData = contentData(published, field, publishedContent);
296
+ const draftData = {
297
+ ...contentData(draft, field, draftContent),
298
+ _status: 'draft'
299
+ };
300
+ // Payload beforeValidate receives the latest version as doc, and the
301
+ // proposed write as data/siblingData (blockData is the proposed row).
302
+ const publicationRefusal = await fieldRefusal(config?.fields ?? [], req, draft, 'update', publishedData);
303
+ if (publicationRefusal) throw new Error(publicationRefusal);
304
+ if (drafts) {
305
+ const draftOriginal = changedPublished ? {
306
+ ...published,
307
+ ...publishedData
308
+ } : draft;
309
+ const draftRefusal = await fieldRefusal(config?.fields ?? [], req, draftOriginal, 'update', draftData);
310
+ if (draftRefusal) throw new Error(draftRefusal);
311
+ }
312
+ // Host writes are access checked with the manager's req (§7.8).
313
+ if (changedPublished) await req.payload.update({
314
+ collection,
315
+ id,
316
+ req,
317
+ overrideAccess: false,
318
+ overrideLock: false,
319
+ draft: false,
320
+ data: publishedData
321
+ });
322
+ if (drafts) await req.payload.update({
323
+ collection,
324
+ id,
325
+ req,
326
+ overrideAccess: false,
327
+ overrideLock: false,
328
+ draft: true,
329
+ data: draftData
330
+ });
331
+ });
332
+ }
333
+ } else if (current.kind === 'flag') {
334
+ const point = options.flagPoints?.find((point)=>point.id === current.flagPoint);
335
+ if (!point) refuse('flag-no-point');
336
+ if (point.makeContent) await attempt('xp-experiments', experimentId, ()=>point.makeContent({
337
+ experimentId,
338
+ winner: {
339
+ arm: winner.key,
340
+ params: winner.params ?? {}
341
+ },
342
+ req
343
+ }));
344
+ // Lock the shared state before its read/modify/write, after L/F/E.
345
+ await lockContentDefaults(exec);
346
+ // §7.8 transition exception: durable made-content defaults in plugin state.
347
+ const state = await req.payload.findGlobal({
348
+ slug: 'xp-state',
349
+ req,
350
+ overrideAccess: true,
351
+ depth: 0
352
+ });
353
+ // §7.8 transition exception: host documents above always use false.
354
+ await req.payload.updateGlobal({
355
+ slug: 'xp-state',
356
+ req,
357
+ overrideAccess: true,
358
+ data: {
359
+ flagDefaults: {
360
+ ...state.flagDefaults ?? {},
361
+ [point.id]: {
362
+ winnerArm: winner.key,
363
+ params: winner.params ?? {},
364
+ experiment: experimentId,
365
+ decidedAt: current.decidedAt
366
+ }
367
+ }
368
+ }
369
+ });
370
+ }
371
+ const doc = await updateExperiment(req, experimentId, {
372
+ decision: {
373
+ ...decision,
374
+ contentReport: {
375
+ docs
376
+ }
377
+ }
378
+ });
379
+ if (docs.every((doc)=>doc.ok)) return {
380
+ ...await archive(req, {
381
+ experimentId,
382
+ mode: 'after-content'
383
+ }),
384
+ docs
385
+ };
386
+ const manifest = await buildAndStoreManifest(req);
387
+ return {
388
+ doc,
389
+ revision: manifest.revision,
390
+ docs
391
+ };
392
+ });
393
+ return result;
394
+ }
395
+ /** The top-level data field with this name, looking through unnamed rows, collapsibles and tabs. */ function topLevelField(fields, name) {
396
+ for (const raw of fields){
397
+ const field = raw;
398
+ if (field.name === name) return field;
399
+ if (!field.name && (field.type === 'row' || field.type === 'collapsible') && field.fields) {
400
+ const found = topLevelField(field.fields, name);
401
+ if (found) return found;
402
+ }
403
+ if (field.type === 'tabs') {
404
+ for (const tab of field.tabs ?? [])if (!tab.name && tab.fields) {
405
+ const found = topLevelField(tab.fields, name);
406
+ if (found) return found;
407
+ }
408
+ }
409
+ }
410
+ return undefined;
411
+ }
@@ -0,0 +1,27 @@
1
+ import type { PayloadRequest } from 'payload';
2
+ import type { Arm, DocId, TargetingRule } from '../types/contracts.js';
3
+ import type { AllocationChange, ContentChange } from '../db/appends.js';
4
+ export interface PhaseRecord {
5
+ experiment: DocId;
6
+ phase: number;
7
+ phaseKind: 'test' | 'rollout';
8
+ startedAt: string;
9
+ endedAt?: string | null;
10
+ seed: string;
11
+ arms: Arm[];
12
+ allocation: number;
13
+ allocationChanges?: AllocationChange[];
14
+ targeting: TargetingRule[];
15
+ metricDefinitions?: Array<Record<string, unknown>>;
16
+ analysis?: Record<string, unknown>;
17
+ contentChanges?: ContentChange[];
18
+ }
19
+ export declare function readPhaseRecord(req: PayloadRequest, experimentId: DocId, phase: number): Promise<PhaseRecord & {
20
+ id: DocId;
21
+ }>;
22
+ /** Caller holds the experiment lock and passes the kernel's transaction req. */
23
+ export declare function writePhaseRecord(req: PayloadRequest, record: PhaseRecord): Promise<import("payload").JsonObject & import("payload").TypeWithID>;
24
+ /** End only an open phase, preserving the original stop time on a later decide.
25
+ * Caller holds the experiment lock and passes the kernel's transaction req.
26
+ */
27
+ export declare function setPhaseEndedAt(req: PayloadRequest, experimentId: DocId, phase: number, at: string): Promise<import("payload").JsonObject & import("payload").TypeWithID>;