@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.
Files changed (466) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +590 -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 +107 -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 +141 -0
  453. package/dist/types/contracts.js +1 -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 +138 -3
@@ -0,0 +1,22 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useLayoutEffect, useRef } from 'react';
4
+ import { registerApiRoute, unregisterApiRoute } from './beacon.js';
5
+ import { ExperimentsContext } from './context.js';
6
+ export function ExperimentsProvider(props) {
7
+ const token = useRef(Symbol()).current;
8
+ // Register committed providers before descendants can send from passive effects.
9
+ useLayoutEffect(()=>{
10
+ registerApiRoute(token, props.value.apiRoute);
11
+ }, [
12
+ token,
13
+ props.value.apiRoute
14
+ ]);
15
+ useLayoutEffect(()=>()=>unregisterApiRoute(token), [
16
+ token
17
+ ]);
18
+ return /*#__PURE__*/ _jsx(ExperimentsContext.Provider, {
19
+ value: props.value,
20
+ children: props.children
21
+ });
22
+ }
@@ -0,0 +1,9 @@
1
+ import type * as React from 'react';
2
+ export declare function ViewportExposure({ expose, children }: {
3
+ expose?: {
4
+ slug: string;
5
+ arm: string;
6
+ phase: number;
7
+ };
8
+ children: React.ReactNode;
9
+ }): React.JSX.Element;
@@ -0,0 +1,69 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useContext, useEffect, useRef } from 'react';
4
+ import { sendExposure, warnOnce } from './beacon.js';
5
+ import { ExperimentsContext } from './context.js';
6
+ export function ViewportExposure({ expose, children }) {
7
+ const value = useContext(ExperimentsContext);
8
+ const sentinel = useRef(null);
9
+ const apiRoute = value?.apiRoute;
10
+ const slug = expose?.slug, arm = expose?.arm, phase = expose?.phase;
11
+ useEffect(()=>{
12
+ if (slug === undefined || arm === undefined || phase === undefined) return;
13
+ if (apiRoute === undefined) {
14
+ warnOnce('viewport:no-provider', 'ViewportExposure requires ExperimentsProvider to send an exposure.');
15
+ return;
16
+ }
17
+ const target = sentinel.current;
18
+ if (!target || typeof IntersectionObserver === 'undefined') return;
19
+ const observer = new IntersectionObserver((entries)=>{
20
+ if (!entries.some((entry)=>entry.target === target && entry.intersectionRatio >= 0.5)) return;
21
+ sendExposure(apiRoute, {
22
+ type: 'exposure',
23
+ experiment: slug,
24
+ phase,
25
+ arm,
26
+ policy: 'viewport'
27
+ });
28
+ observer.disconnect();
29
+ }, {
30
+ threshold: 0.5
31
+ });
32
+ observer.observe(target);
33
+ return ()=>observer.disconnect();
34
+ }, [
35
+ slug,
36
+ arm,
37
+ phase,
38
+ apiRoute
39
+ ]);
40
+ // Nothing to expose (no running experiment, or a render-policy slot): render exactly
41
+ // what experiments-off renders — no wrapper, no sentinel (owner rule O9). Hooks stay above.
42
+ if (expose === undefined) return /*#__PURE__*/ _jsx(_Fragment, {
43
+ children: children
44
+ });
45
+ return /*#__PURE__*/ _jsxs("div", {
46
+ "data-xp-viewport": true,
47
+ style: {
48
+ position: 'relative',
49
+ display: 'block'
50
+ },
51
+ children: [
52
+ /*#__PURE__*/ _jsx("div", {
53
+ ref: sentinel,
54
+ "data-xp-sentinel": true,
55
+ "aria-hidden": true,
56
+ style: {
57
+ position: 'absolute',
58
+ top: 0,
59
+ left: 0,
60
+ width: '100%',
61
+ height: '100px',
62
+ pointerEvents: 'none',
63
+ visibility: 'hidden'
64
+ }
65
+ }),
66
+ children
67
+ ]
68
+ });
69
+ }
@@ -0,0 +1,17 @@
1
+ import type { EventBody } from '../types/endpoints.js';
2
+ type Event = EventBody['events'][number];
3
+ type Exposure = Extract<Event, {
4
+ type: 'exposure';
5
+ }>;
6
+ /** Register only committed providers; the latest mounted provider supplies conversion routing. */
7
+ export declare function registerApiRoute(token: symbol, apiRoute: string): void;
8
+ export declare function unregisterApiRoute(token: symbol): void;
9
+ /** No mounted provider means experiments are not wired on this page: conversions are not sent (spec §13). */
10
+ export declare function conversionApiRoute(): string | undefined;
11
+ export declare function warnOnce(key: string, message: string): void;
12
+ /** Telemetry must never interrupt rendering or a host interaction. */
13
+ export declare function sendEvent(apiRoute: string, event: Event): void;
14
+ export declare function sendExposure(apiRoute: string, event: Exposure, pathname?: string): void;
15
+ /** Internal test seam; deliberately not exported by /client. */
16
+ export declare function __resetBeaconsForTests(): void;
17
+ export {};
@@ -0,0 +1,71 @@
1
+ 'use client';
2
+ const exposures = new Set();
3
+ const pageviews = new Set();
4
+ const warnings = new Set();
5
+ const providers = new Map();
6
+ /** Register only committed providers; the latest mounted provider supplies conversion routing. */ export function registerApiRoute(token, apiRoute) {
7
+ // Updating an existing token preserves its original mount order.
8
+ providers.set(token, apiRoute);
9
+ }
10
+ export function unregisterApiRoute(token) {
11
+ providers.delete(token);
12
+ }
13
+ /** No mounted provider means experiments are not wired on this page: conversions are not sent (spec §13). */ export function conversionApiRoute() {
14
+ return [
15
+ ...providers.values()
16
+ ].at(-1);
17
+ }
18
+ export function warnOnce(key, message) {
19
+ if (warnings.has(key)) return;
20
+ warnings.add(key);
21
+ console.error(message);
22
+ }
23
+ /** Telemetry must never interrupt rendering or a host interaction. */ export function sendEvent(apiRoute, event) {
24
+ try {
25
+ const body = {
26
+ events: [
27
+ event
28
+ ]
29
+ };
30
+ const json = JSON.stringify(body);
31
+ const url = `${apiRoute}/xp/events`;
32
+ try {
33
+ if (typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function' && navigator.sendBeacon(url, new Blob([
34
+ json
35
+ ], {
36
+ type: 'text/plain;charset=UTF-8'
37
+ }))) return;
38
+ } catch {}
39
+ try {
40
+ void fetch(url, {
41
+ method: 'POST',
42
+ keepalive: true,
43
+ credentials: 'same-origin',
44
+ headers: {
45
+ 'content-type': 'text/plain;charset=UTF-8'
46
+ },
47
+ body: json
48
+ }).catch(()=>{});
49
+ } catch {}
50
+ } catch {}
51
+ }
52
+ export function sendExposure(apiRoute, event, pathname) {
53
+ const seen = pathname === undefined ? exposures : pageviews;
54
+ const key = JSON.stringify([
55
+ event.experiment,
56
+ event.phase,
57
+ event.arm,
58
+ ...pathname === undefined ? [] : [
59
+ pathname
60
+ ]
61
+ ]);
62
+ if (seen.has(key)) return;
63
+ seen.add(key);
64
+ sendEvent(apiRoute, event);
65
+ }
66
+ /** Internal test seam; deliberately not exported by /client. */ export function __resetBeaconsForTests() {
67
+ exposures.clear();
68
+ pageviews.clear();
69
+ warnings.clear();
70
+ providers.clear();
71
+ }
@@ -0,0 +1,2 @@
1
+ import type { ClientAssignments } from '../types/assignments.js';
2
+ export declare const ExperimentsContext: import("react").Context<ClientAssignments | null>;
@@ -0,0 +1,2 @@
1
+ import { createContext } from 'react';
2
+ export const ExperimentsContext = createContext(null);
@@ -0,0 +1,2 @@
1
+ /** Prefer a caller's stable key; restricted browser crypto must not break the caller. */
2
+ export declare function idempotencyKey(provided?: string): string;
@@ -0,0 +1,13 @@
1
+ 'use client';
2
+ /** Prefer a caller's stable key; restricted browser crypto must not break the caller. */ export function idempotencyKey(provided) {
3
+ // An empty key would collapse every conversion of this event for the unit into one dedupe row.
4
+ if (provided) return provided;
5
+ try {
6
+ if (typeof crypto.randomUUID === 'function') return crypto.randomUUID();
7
+ } catch {}
8
+ try {
9
+ const bytes = crypto.getRandomValues(new Uint8Array(16));
10
+ return Array.from(bytes, (byte)=>byte.toString(16).padStart(2, '0')).join('');
11
+ } catch {}
12
+ return Date.now().toString(36) + Math.random().toString(36).slice(2);
13
+ }
@@ -0,0 +1,3 @@
1
+ export declare function trackConversionBeacon(eventName: string, opts?: {
2
+ idempotencyKey?: string;
3
+ }): void;
@@ -0,0 +1,16 @@
1
+ 'use client';
2
+ import { conversionApiRoute, sendEvent, warnOnce } from './beacon.js';
3
+ import { idempotencyKey } from './idempotency.js';
4
+ export function trackConversionBeacon(eventName, opts) {
5
+ const apiRoute = conversionApiRoute();
6
+ if (apiRoute === undefined) {
7
+ // Without an ExperimentsProvider the host has experiments disabled here; never send to a guessed route.
8
+ if (process.env.NODE_ENV !== 'production') warnOnce('conversion:no-provider', `trackConversionBeacon('${eventName}') was called without an ExperimentsProvider; nothing was sent.`);
9
+ return;
10
+ }
11
+ sendEvent(apiRoute, {
12
+ type: 'conversion',
13
+ eventName,
14
+ idempotencyKey: idempotencyKey(opts?.idempotencyKey)
15
+ });
16
+ }
@@ -0,0 +1,5 @@
1
+ export declare function useExperiment<P extends Record<string, unknown> = Record<string, unknown>>(slug: string): {
2
+ arm: string;
3
+ params: P;
4
+ expose(renderedArm: string): void;
5
+ };
@@ -0,0 +1,27 @@
1
+ 'use client';
2
+ import { useContext } from 'react';
3
+ import { sendExposure, warnOnce } from './beacon.js';
4
+ import { ExperimentsContext } from './context.js';
5
+ export function useExperiment(slug) {
6
+ const value = useContext(ExperimentsContext);
7
+ const entry = value && Object.hasOwn(value.experiments, slug) ? value.experiments[slug] : undefined;
8
+ if (!entry) {
9
+ const message = `Experiment "${slug}" must be allow-listed in toClientAssignments and rendered inside ExperimentsProvider.`;
10
+ if (process.env.NODE_ENV !== 'production') throw new Error(message);
11
+ warnOnce(`experiment:${slug}`, message);
12
+ }
13
+ return {
14
+ arm: entry?.arm ?? 'control',
15
+ params: entry?.params ?? {},
16
+ expose (renderedArm) {
17
+ if (!value || !entry?.exposable || entry.policy !== 'manual') return;
18
+ sendExposure(value.apiRoute, {
19
+ type: 'exposure',
20
+ experiment: slug,
21
+ phase: entry.phase,
22
+ arm: renderedArm,
23
+ policy: entry.policy
24
+ });
25
+ }
26
+ };
27
+ }
@@ -0,0 +1,9 @@
1
+ import type { DocId } from '../types/contracts.js';
2
+ export declare function useFlagPoint<P extends Record<string, unknown> = Record<string, unknown>>(id: string): {
3
+ arm: string;
4
+ params: P;
5
+ source: 'experiment' | 'default';
6
+ experiment?: string;
7
+ experimentId?: DocId;
8
+ expose(renderedArm: string): void;
9
+ };
@@ -0,0 +1,32 @@
1
+ 'use client';
2
+ import { useContext } from 'react';
3
+ import { sendExposure, warnOnce } from './beacon.js';
4
+ import { ExperimentsContext } from './context.js';
5
+ export function useFlagPoint(id) {
6
+ const value = useContext(ExperimentsContext);
7
+ const entry = value && Object.hasOwn(value.flagPoints, id) ? value.flagPoints[id] : undefined;
8
+ if (!entry) {
9
+ const message = `Flag point "${id}" must be allow-listed in toClientAssignments and rendered inside ExperimentsProvider.`;
10
+ if (process.env.NODE_ENV !== 'production') throw new Error(message);
11
+ warnOnce(`flagPoint:${id}`, message);
12
+ }
13
+ return {
14
+ arm: entry?.arm ?? 'control',
15
+ params: entry?.params ?? {},
16
+ source: entry?.source ?? 'default',
17
+ ...entry?.source === 'experiment' ? {
18
+ experiment: entry.experiment,
19
+ experimentId: entry.experimentId
20
+ } : {},
21
+ expose (renderedArm) {
22
+ if (!value || !entry?.exposable || entry.source !== 'experiment' || entry.policy !== 'manual' || entry.experiment === undefined || entry.phase === undefined) return;
23
+ sendExposure(value.apiRoute, {
24
+ type: 'exposure',
25
+ experiment: entry.experiment,
26
+ phase: entry.phase,
27
+ arm: renderedArm,
28
+ policy: entry.policy
29
+ });
30
+ }
31
+ };
32
+ }
@@ -0,0 +1,21 @@
1
+ import type { Access } from 'payload';
2
+ import type { ExperimentsAccess, ResolvedOptions } from '../types/contracts.js';
3
+ /** Host access callbacks must return the boolean true, never a truthy filter. */
4
+ export declare const isTrue: (fn: ExperimentsAccess) => Access;
5
+ export declare const deny: Access;
6
+ export declare function managedAccess(options: ResolvedOptions): {
7
+ create: Access;
8
+ read: Access;
9
+ update: Access;
10
+ delete: Access;
11
+ readVersions: Access;
12
+ unlock: Access;
13
+ };
14
+ export declare function readOnlyAccess(read: ExperimentsAccess): {
15
+ create: Access;
16
+ read: Access;
17
+ update: Access;
18
+ delete: Access;
19
+ readVersions: Access;
20
+ unlock: Access;
21
+ };
@@ -0,0 +1,25 @@
1
+ /** Host access callbacks must return the boolean true, never a truthy filter. */ export const isTrue = (fn)=>async ({ req })=>await fn({
2
+ req
3
+ }) === true;
4
+ export const deny = ()=>false;
5
+ export function managedAccess(options) {
6
+ const manage = isTrue(options.access.manage);
7
+ return {
8
+ create: manage,
9
+ read: manage,
10
+ update: manage,
11
+ delete: manage,
12
+ readVersions: manage,
13
+ unlock: manage
14
+ };
15
+ }
16
+ export function readOnlyAccess(read) {
17
+ return {
18
+ create: deny,
19
+ read: isTrue(read),
20
+ update: deny,
21
+ delete: deny,
22
+ readVersions: deny,
23
+ unlock: deny
24
+ };
25
+ }
@@ -0,0 +1,3 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ import type { ResolvedOptions } from '../types/contracts.js';
3
+ export declare function eventsCollection(options: ResolvedOptions): CollectionConfig;
@@ -0,0 +1,117 @@
1
+ import { readOnlyAccess } from './access.js';
2
+ export function eventsCollection(options) {
3
+ return {
4
+ slug: 'xp-events',
5
+ graphQL: false,
6
+ disableDuplicate: true,
7
+ admin: {
8
+ hidden: true
9
+ },
10
+ access: readOnlyAccess(options.access.readIdentities ?? options.access.admin ?? options.access.manage),
11
+ indexes: [
12
+ {
13
+ fields: [
14
+ 'experiment',
15
+ 'phase',
16
+ 'type',
17
+ 'ts'
18
+ ]
19
+ },
20
+ {
21
+ fields: [
22
+ 'unitId',
23
+ 'experiment'
24
+ ]
25
+ },
26
+ {
27
+ fields: [
28
+ 'type',
29
+ 'eventName',
30
+ 'ts'
31
+ ]
32
+ },
33
+ {
34
+ fields: [
35
+ 'experiment',
36
+ 'type',
37
+ 'ts'
38
+ ]
39
+ }
40
+ ],
41
+ fields: [
42
+ {
43
+ name: 'type',
44
+ type: 'select',
45
+ required: true,
46
+ options: [
47
+ 'exposure',
48
+ 'conversion',
49
+ 'arm-mismatch'
50
+ ]
51
+ },
52
+ {
53
+ name: 'experiment',
54
+ type: 'relationship',
55
+ relationTo: 'xp-experiments'
56
+ },
57
+ {
58
+ name: 'phase',
59
+ type: 'number'
60
+ },
61
+ {
62
+ name: 'arm',
63
+ type: 'text'
64
+ },
65
+ {
66
+ name: 'unitId',
67
+ type: 'text',
68
+ required: true
69
+ },
70
+ {
71
+ name: 'unitType',
72
+ type: 'select',
73
+ required: true,
74
+ options: [
75
+ 'visitor',
76
+ 'user'
77
+ ]
78
+ },
79
+ {
80
+ name: 'userId',
81
+ type: 'text'
82
+ },
83
+ {
84
+ name: 'eventName',
85
+ type: 'text'
86
+ },
87
+ {
88
+ name: 'value',
89
+ type: 'number'
90
+ },
91
+ {
92
+ name: 'ts',
93
+ type: 'date',
94
+ required: true
95
+ },
96
+ {
97
+ name: 'dimensions',
98
+ type: 'json',
99
+ admin: {
100
+ hidden: true,
101
+ readOnly: true
102
+ }
103
+ },
104
+ {
105
+ name: 'attributes',
106
+ type: 'json'
107
+ },
108
+ {
109
+ name: 'dedupeKey',
110
+ type: 'text',
111
+ unique: true,
112
+ index: true,
113
+ required: true
114
+ }
115
+ ]
116
+ };
117
+ }
@@ -0,0 +1,12 @@
1
+ import type { Arm, ExperimentKind } from '../../types/contracts.js';
2
+ import type { ArmIssueCode } from '../../types/endpoints.js';
3
+ export interface ArmIssue {
4
+ code: ArmIssueCode;
5
+ armIndex?: number;
6
+ armKey?: string;
7
+ message: string;
8
+ }
9
+ /** The groups a new experiment starts with: Original 50 / Variant A 50, no holdout (spec §5.1, O4). Block slots equal the key. */
10
+ export declare function defaultArms(kind: ExperimentKind | undefined): Arm[];
11
+ export declare function checkArms(arms: Arm[], kind: ExperimentKind): ArmIssue[];
12
+ export declare function addHoldout(arms: Arm[]): Arm[];
@@ -0,0 +1,89 @@
1
+ /** The groups a new experiment starts with: Original 50 / Variant A 50, no holdout (spec §5.1, O4). Block slots equal the key. */ export function defaultArms(kind) {
2
+ const slot = (key)=>kind === 'block' ? {
3
+ slot: key
4
+ } : {};
5
+ return [
6
+ {
7
+ key: 'control',
8
+ kind: 'control',
9
+ label: 'Original',
10
+ weight: 50,
11
+ ...slot('control')
12
+ },
13
+ {
14
+ key: 'variantA',
15
+ kind: 'variant',
16
+ label: 'Variant A',
17
+ weight: 50,
18
+ ...slot('variantA')
19
+ }
20
+ ];
21
+ }
22
+ export function checkArms(arms, kind) {
23
+ const issues = [];
24
+ const issue = (code, message, armIndex)=>{
25
+ issues.push({
26
+ code,
27
+ message,
28
+ ...armIndex === undefined ? {} : {
29
+ armIndex,
30
+ armKey: arms[armIndex].key
31
+ }
32
+ });
33
+ };
34
+ if (arms.some((arm)=>!Number.isInteger(arm.weight) || arm.weight < 0) || arms.reduce((total, arm)=>total + arm.weight, 0) !== 100) {
35
+ issue('weights-total', 'Group shares must be whole numbers that add up to exactly 100.');
36
+ }
37
+ if (arms.filter((arm)=>arm.kind === 'control').length !== 1) issue('control-count', 'Exactly one Original is required.');
38
+ if (arms.some((arm, index)=>arm.kind === 'control' && index !== 0)) issue('control-first', 'The Original must be first.');
39
+ if (arms.filter((arm)=>arm.kind === 'holdout').length > 1) issue('holdout-count', 'Only one Holdout is allowed.');
40
+ if (arms.some((arm, index)=>arm.kind === 'holdout' && index !== arms.length - 1)) issue('holdout-last', 'The Holdout must be the last group.');
41
+ if (arms.filter((arm)=>arm.kind === 'variant').length > 3) issue('variant-count', 'At most three variants are allowed.');
42
+ const keys = new Set(), slots = new Set();
43
+ const kinds = {
44
+ control: 'control',
45
+ variantA: 'variant',
46
+ variantB: 'variant',
47
+ variantC: 'variant',
48
+ holdout: 'holdout'
49
+ };
50
+ arms.forEach((arm, index)=>{
51
+ if (!Object.hasOwn(kinds, arm.key) || kinds[arm.key] !== arm.kind) issue('invalid-key', 'Each group must be the Original, Variant A, B or C, or the Holdout.', index);
52
+ if (keys.has(arm.key)) issue('duplicate-key', 'Each group can appear only once.', index);
53
+ keys.add(arm.key);
54
+ if (kind === 'block') {
55
+ if (arm.kind === 'holdout') {
56
+ if (arm.slot != null) issue('invalid-key', 'The Holdout has no content of its own; it sees the Original.', index);
57
+ } else {
58
+ if (!arm.slot || arm.slot !== arm.key) issue('slot-required', 'Each Original and Variant needs its own content area.', index);
59
+ if (arm.slot && slots.has(arm.slot)) issue('duplicate-slot', 'Two groups cannot share the same content area.', index);
60
+ if (arm.slot) slots.add(arm.slot);
61
+ }
62
+ }
63
+ });
64
+ return issues;
65
+ }
66
+ export function addHoldout(arms) {
67
+ if (arms.some((arm)=>arm.kind === 'holdout' || arm.key === 'holdout')) return arms;
68
+ const total = arms.reduce((sum, arm)=>sum + arm.weight, 0);
69
+ const denominator = total > 0 ? total : arms.length;
70
+ const numerators = arms.map((arm)=>(total > 0 ? arm.weight : 1) * 90);
71
+ const weights = numerators.map((numerator)=>Math.floor(numerator / denominator));
72
+ const remainders = numerators.map((numerator)=>numerator % denominator);
73
+ const order = arms.map((_, index)=>index).sort((a, b)=>remainders[b] - remainders[a] || Number(arms[b].kind === 'control') - Number(arms[a].kind === 'control') || a - b);
74
+ const remainder = 90 - weights.reduce((sum, weight)=>sum + weight, 0);
75
+ for(let i = 0; i < remainder && order.length; i++)weights[order[i % order.length]]++;
76
+ const holdout = {
77
+ key: 'holdout',
78
+ kind: 'holdout',
79
+ label: 'Holdout',
80
+ weight: 10
81
+ };
82
+ return [
83
+ ...arms.map((arm, index)=>({
84
+ ...arm,
85
+ weight: weights[index]
86
+ })),
87
+ holdout
88
+ ];
89
+ }
@@ -0,0 +1,3 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ import type { ResolvedOptions } from '../../types/contracts.js';
3
+ export declare function experimentsCollection(options: ResolvedOptions): CollectionConfig;