@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,27 @@
1
+ import { COLUMNS, TABLES } from './names.js';
2
+ const columns = COLUMNS.state;
3
+ const quote = (name)=>`"${name.replaceAll('"', '""')}"`;
4
+ function table(exec, options = {}) {
5
+ const schemaName = options.schemaName ?? exec.schemaName;
6
+ return `${schemaName ? `${quote(schemaName)}.` : ''}${quote(TABLES.state)}`;
7
+ }
8
+ /** Always take this lock last: layer → flag point → experiment → state. */ export async function bumpRevision(exec) {
9
+ await exec.query("SELECT pg_advisory_xact_lock(hashtext('xp:state'), 0)");
10
+ const updated = await exec.query(`UPDATE ${table(exec)} SET ${quote(columns.revision)} = COALESCE(${quote(columns.revision)}, 0) + 1, ${quote(columns.updatedAt)} = now() RETURNING ${quote(columns.revision)}`);
11
+ if (updated.rows[0]) return updated.rows[0].revision;
12
+ const inserted = await exec.query(`INSERT INTO ${table(exec)} (${quote(columns.revision)}, ${quote(columns.createdAt)}, ${quote(columns.updatedAt)}) VALUES (1, now(), now()) RETURNING ${quote(columns.revision)}`);
13
+ return inserted.rows[0].revision;
14
+ }
15
+ export async function writeManifest(exec, manifest) {
16
+ await exec.query(`UPDATE ${table(exec)} SET ${quote(columns.manifest)} = $1::jsonb, ${quote(columns.updatedAt)} = now()`, [
17
+ JSON.stringify(manifest)
18
+ ]);
19
+ }
20
+ export async function readState(exec, options = {}) {
21
+ const result = await exec.query(`SELECT ${quote(columns.revision)}, ${quote(columns.manifest)}, ${quote(columns.flagDefaults)} AS "flagDefaults" FROM ${table(exec, options)} LIMIT 1`);
22
+ const row = result.rows[0];
23
+ return row ? {
24
+ ...row,
25
+ flagDefaults: row.flagDefaults ?? {}
26
+ } : null;
27
+ }
@@ -0,0 +1,13 @@
1
+ import type { Executor } from './executor.js';
2
+ /** §5.5 / §7.8: privileged retention sweep. Deleted experiments' FKs become
3
+ * NULL; their exposure and arm-mismatch rows are handled by the two NULL branches. Global
4
+ * conversions stay while an open/recent experiment could still need them. */
5
+ export declare function sweepEvents(exec: Executor, { now, retentionDays, batchSize, shouldContinue, logger }: {
6
+ now: Date;
7
+ retentionDays?: number;
8
+ batchSize?: number;
9
+ shouldContinue?: () => boolean;
10
+ logger?: {
11
+ warn(message: string): void;
12
+ };
13
+ }): Promise<number>;
@@ -0,0 +1,90 @@
1
+ import { COLUMNS, TABLES } from './names.js';
2
+ import { qualifiedTable, quoteIdentifier as q } from './sql.js';
3
+ import { ResultsBudgetError } from './pgExecutor.js';
4
+ /** §5.5 / §7.8: privileged retention sweep. Deleted experiments' FKs become
5
+ * NULL; their exposure and arm-mismatch rows are handled by the two NULL branches. Global
6
+ * conversions stay while an open/recent experiment could still need them. */ export async function sweepEvents(exec, { now, retentionDays = 180, batchSize = 1000, shouldContinue = ()=>true, logger }) {
7
+ if (!Number.isSafeInteger(retentionDays) || retentionDays < 1 || !Number.isSafeInteger(batchSize) || batchSize < 1) throw new RangeError('Invalid retention settings');
8
+ const cutoffMs = now.getTime() - retentionDays * 86_400_000;
9
+ const cutoff = new Date(cutoffMs).toISOString();
10
+ const c = COLUMNS.events, p = COLUMNS.phases, s = COLUMNS.state;
11
+ const events = qualifiedTable(exec, TABLES.events), phases = qualifiedTable(exec, TABLES.phases);
12
+ const revision = `(SELECT ${q(s.revision)} FROM ${qualifiedTable(exec, TABLES.state)} LIMIT 1)`;
13
+ let swept = 0;
14
+ // Each statement still uses the caller's budget/race-aware executor.
15
+ async function batches(predicate, order, params = []) {
16
+ while(shouldContinue()){
17
+ const result = await exec.query(`DELETE FROM ${events} WHERE ${q(c.id)} IN (
18
+ SELECT v.${q(c.id)} FROM ${events} v WHERE ${predicate}
19
+ ORDER BY ${order} LIMIT $${params.length + 1})`, [
20
+ ...params,
21
+ batchSize
22
+ ]);
23
+ swept += result.rowCount;
24
+ if (result.rowCount < batchSize) break;
25
+ }
26
+ }
27
+ try {
28
+ // Keep the leading NULL key in ORDER BY too: PostgreSQL does not infer
29
+ // its ordering equivalence from IS NULL when choosing a bounded index scan.
30
+ const byEventType = `v.${q(c.experimentId)}, v.${q(c.type)}, v.${q(c.ts)}`;
31
+ // Drain the two allow-listed diagnostic types with equality probes. An
32
+ // ordered IN scan can otherwise read conversion entries and filter them.
33
+ for (const type of [
34
+ 'exposure',
35
+ 'arm-mismatch'
36
+ ]){
37
+ await batches(`v.${q(c.experimentId)} IS NULL AND v.${q(c.type)} IN ($1)`, byEventType, [
38
+ type
39
+ ]);
40
+ }
41
+ if (!shouldContinue()) return swept;
42
+ // Final-endedAt retention protects every phase while any phase is open.
43
+ const ended = await exec.query(`SELECT ${q(p.experimentId)} AS "experimentId" FROM ${phases}
44
+ WHERE ${q(p.experimentId)} IS NOT NULL GROUP BY ${q(p.experimentId)}
45
+ HAVING NOT bool_or(${q(p.endedAt)} IS NULL) AND max(${q(p.endedAt)}) < $1`, [
46
+ cutoff
47
+ ]);
48
+ if (ended.rows.length) {
49
+ // Recheck in every batch in case a new phase started after the id read.
50
+ await batches(`v.${q(c.experimentId)} = ANY($1) AND NOT EXISTS (
51
+ SELECT 1 FROM ${phases} p WHERE p.${q(p.experimentId)} = v.${q(c.experimentId)}
52
+ AND (p.${q(p.endedAt)} IS NULL OR p.${q(p.endedAt)} >= $2))`, byEventType, [
53
+ ended.rows.map((row)=>row.experimentId),
54
+ cutoff
55
+ ]);
56
+ }
57
+ if (!shouldContinue()) return swept;
58
+ // Every protected window ends at/after cutoff (or is open). For old
59
+ // conversions their union therefore starts at the earliest lookback start;
60
+ // everything before that single bound is safe. Compute it once, in the same
61
+ // snapshot as the state revision. Epoch milliseconds avoid timestamp parsing.
62
+ const protectedRows = await exec.query(`SELECT
63
+ ${revision} AS revision, min(w."from") AS "before" FROM (
64
+ SELECT extract(epoch FROM min(${q(p.startedAt)}) - interval '14 days') * 1000 AS "from"
65
+ FROM ${phases} WHERE ${q(p.experimentId)} IS NOT NULL GROUP BY ${q(p.experimentId)}
66
+ HAVING bool_or(${q(p.endedAt)} IS NULL) OR max(${q(p.endedAt)}) >= $1
67
+ ) w`, [
68
+ cutoff
69
+ ]);
70
+ const frozen = protectedRows.rows[0] ?? {
71
+ revision: null,
72
+ before: null
73
+ };
74
+ const before = new Date(Math.min(cutoffMs, frozen.before ?? cutoffMs)).toISOString();
75
+ // Transitions bump revision in the same transaction as phase writes. This
76
+ // uncorrelated initplan checks it once per statement, not per candidate. If a
77
+ // phase starts mid-sweep, stale windows can delete no further conversions.
78
+ await batches(`v.${q(c.experimentId)} IS NULL AND v.${q(c.type)} = 'conversion' AND v.${q(c.ts)} < $1
79
+ AND ${revision} IS NOT DISTINCT FROM $2`, byEventType, [
80
+ before,
81
+ frozen.revision
82
+ ]);
83
+ return swept;
84
+ } catch (error) {
85
+ if (swept === 0 && error instanceof ResultsBudgetError) {
86
+ logger?.warn('Experiment retention sweep made no progress before its budget expired; eligible rows may remain.');
87
+ }
88
+ throw error;
89
+ }
90
+ }
@@ -0,0 +1,7 @@
1
+ import type { Executor } from './executor.js';
2
+ /** Capture on the transaction executor before committing; keep the bigint as text. */
3
+ export declare function currentTxid(exec: Executor): Promise<string>;
4
+ /** Verify on the pool after commit, so another transaction's revision cannot
5
+ * masquerade as this transaction's success. Null means its status is unavailable.
6
+ */
7
+ export declare function txidStatus(exec: Executor, xid: string): Promise<'committed' | 'aborted' | 'in progress' | null>;
@@ -0,0 +1,12 @@
1
+ /** Capture on the transaction executor before committing; keep the bigint as text. */ export async function currentTxid(exec) {
2
+ const { rows } = await exec.query('SELECT txid_current()::text AS xid');
3
+ return rows[0].xid;
4
+ }
5
+ /** Verify on the pool after commit, so another transaction's revision cannot
6
+ * masquerade as this transaction's success. Null means its status is unavailable.
7
+ */ export async function txidStatus(exec, xid) {
8
+ const { rows } = await exec.query('SELECT txid_status($1::bigint) AS status', [
9
+ xid
10
+ ]);
11
+ return rows[0]?.status ?? null;
12
+ }
@@ -0,0 +1,3 @@
1
+ import type { FlagPoint, MetricSource } from './types/contracts.js';
2
+ export declare function defineFlagPoint<T extends FlagPoint>(p: T): T;
3
+ export declare function defineMetricSource<T extends MetricSource>(s: T): T;
package/dist/define.js ADDED
@@ -0,0 +1,6 @@
1
+ export function defineFlagPoint(p) {
2
+ return p;
3
+ }
4
+ export function defineMetricSource(s) {
5
+ return s;
6
+ }
@@ -0,0 +1,7 @@
1
+ import type { PayloadRequest } from 'payload';
2
+ import type { EndpointContext } from '../types/endpoints.js';
3
+ export declare function handleEvents<TUser extends {
4
+ id: string | number;
5
+ } = {
6
+ id: string | number;
7
+ }>(req: PayloadRequest, { options }: EndpointContext<TUser>): Promise<Response>;
@@ -0,0 +1,98 @@
1
+ import { NextRequest } from 'next/server.js';
2
+ import { DEFAULT_MAX_BODY_BYTES } from '../constants.js';
3
+ import { pooledExecutor } from '../db/executor.js';
4
+ import { insertEvents } from '../db/events.js';
5
+ import { EventsBodyError, readEvents } from '../events/body.js';
6
+ import { isPreview } from '../events/preview.js';
7
+ import { sourceRequest } from '../events/request.js';
8
+ import { eventRows } from '../events/rows.js';
9
+ import { assign } from '../runtime/assign.js';
10
+ import { isBot } from '../runtime/bot.js';
11
+ import { readUnitCookie } from '../runtime/cookie.js';
12
+ import { requestAttributes } from '../runtime/normalisePath.js';
13
+ import { serverManifest } from '../server/context.js';
14
+ const accepted = ()=>Response.json({}, {
15
+ status: 202
16
+ });
17
+ export async function handleEvents(req, { options }) {
18
+ const runtime = options.runtime, proxy = runtime.proxyRuntime;
19
+ const visitorId = readUnitCookie(req.headers, proxy);
20
+ if (!visitorId) return Response.json({
21
+ errors: [
22
+ {
23
+ code: 'unauthenticated',
24
+ message: 'Invalid unit cookie'
25
+ }
26
+ ]
27
+ }, {
28
+ status: 401
29
+ });
30
+ let events;
31
+ try {
32
+ events = await readEvents(req.body, options.events?.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES);
33
+ } catch (error) {
34
+ if (error instanceof EventsBodyError) return Response.json({
35
+ errors: [
36
+ {
37
+ code: error.code,
38
+ message: error.message
39
+ }
40
+ ]
41
+ }, {
42
+ status: error.status
43
+ });
44
+ throw error;
45
+ }
46
+ try {
47
+ const request = new NextRequest(req.url ?? 'https://xp.invalid/', {
48
+ headers: req.headers,
49
+ method: req.method
50
+ });
51
+ // API paths bypass the proxy: re-check consent even when a stale signed cookie exists.
52
+ try {
53
+ if (proxy.cookie.consent?.(request) === false) return accepted();
54
+ } catch {
55
+ return accepted();
56
+ }
57
+ if (options.events?.rateLimit && await options.events.rateLimit({
58
+ req,
59
+ unitId: visitorId
60
+ }) !== true) return accepted();
61
+ const manifest = await serverManifest(proxy);
62
+ if (!manifest) return accepted();
63
+ const source = sourceRequest(request);
64
+ const unit = await proxy.getUnit?.(source ?? request);
65
+ const user = req.user ?? null;
66
+ const userId = user ? runtime.userIdOf(user) : undefined;
67
+ // The beacon URL is an API route, not the page whose content was rendered.
68
+ // A referrer supplies page context only, never an identity or an assigned arm.
69
+ const attributes = requestAttributes({
70
+ ...unit?.attributes,
71
+ ...runtime.attributesOf(user)
72
+ }, source?.nextUrl.pathname);
73
+ const unresolvedStaff = Boolean(options.attributeKeys?.staff && unit?.sessionUnknown && !user);
74
+ const assignments = assign(manifest, {
75
+ visitorId,
76
+ userId
77
+ }, attributes, {
78
+ now: Math.floor(Date.now() / 1000),
79
+ method: req.method,
80
+ staff: unit?.attributes.staff === true || attributes.staff === true,
81
+ bot: isBot(req.headers.get('user-agent')),
82
+ preview: isPreview(request)
83
+ });
84
+ if (unresolvedStaff) for (const assignment of Object.values(assignments.bySlug))assignment.exposable = false;
85
+ const rows = eventRows(events, manifest, assignments, options, visitorId, userId, new Date().toISOString(), attributes);
86
+ // §7.8 events insert / assignment lookup exception: pooled SQL and cached manifest,
87
+ // no Local API calls (and hence no implicit overrideAccess defaults).
88
+ if (rows.length) await insertEvents(pooledExecutor(req.payload), rows, (message)=>req.payload.logger.error(message));
89
+ } catch (error) {
90
+ try {
91
+ req.payload.logger.error({
92
+ err: error,
93
+ msg: 'Experiments: events endpoint failed'
94
+ });
95
+ } catch {}
96
+ }
97
+ return accepted();
98
+ }
@@ -0,0 +1,18 @@
1
+ import type { Endpoint, PayloadRequest } from 'payload';
2
+ import type { ResolvedOptions } from '../types/contracts.js';
3
+ import type { AccessKind, EndpointContext } from '../types/endpoints.js';
4
+ export declare const ROUTES: Array<{
5
+ method: Endpoint['method'];
6
+ path: string;
7
+ access: AccessKind;
8
+ handler: <TUser extends {
9
+ id: string | number;
10
+ }>(req: PayloadRequest, ctx: EndpointContext<TUser>) => Promise<Response>;
11
+ }>;
12
+ export declare function buildEndpoints<TUser extends {
13
+ id: string | number;
14
+ } = {
15
+ id: string | number;
16
+ }>(ctx: {
17
+ options: ResolvedOptions<TUser>;
18
+ }): Endpoint[];
@@ -0,0 +1,160 @@
1
+ import { createHmac, timingSafeEqual } from 'node:crypto';
2
+ import { XP_MANIFEST_KEY_HEADER } from '../constants.js';
3
+ import { XpNotImplemented, XpRefusal } from '../errors.js';
4
+ import { readUnitCookie } from '../runtime/cookie.js';
5
+ import { handleManifest } from './manifest.js';
6
+ import { handleEvents } from './events.js';
7
+ import { handleStart, handleAllocate, handleAddMetric, handleStop, handleDecide, handleReleaseHoldout, handleMakeContent, handleArchive } from './transitions.js';
8
+ import { handlePreflight } from './preflight.js';
9
+ import { handlePreviewToken } from './previewToken.js';
10
+ import { handleResults } from './results.js';
11
+ import { handleRecompute } from './recompute.js';
12
+ import { handleSummary } from './summary.js';
13
+ export const ROUTES = [
14
+ {
15
+ method: 'get',
16
+ path: '/xp/manifest',
17
+ access: 'manifestKey',
18
+ handler: handleManifest
19
+ },
20
+ {
21
+ method: 'post',
22
+ path: '/xp/events',
23
+ access: 'signedCookie',
24
+ handler: handleEvents
25
+ },
26
+ {
27
+ method: 'post',
28
+ path: '/xp/experiments/:id/start',
29
+ access: 'manage',
30
+ handler: handleStart
31
+ },
32
+ {
33
+ method: 'post',
34
+ path: '/xp/experiments/:id/allocate',
35
+ access: 'manage',
36
+ handler: handleAllocate
37
+ },
38
+ {
39
+ method: 'post',
40
+ path: '/xp/experiments/:id/add-metric',
41
+ access: 'manage',
42
+ handler: handleAddMetric
43
+ },
44
+ {
45
+ method: 'post',
46
+ path: '/xp/experiments/:id/stop',
47
+ access: 'manage',
48
+ handler: handleStop
49
+ },
50
+ {
51
+ method: 'post',
52
+ path: '/xp/experiments/:id/decide',
53
+ access: 'manage',
54
+ handler: handleDecide
55
+ },
56
+ {
57
+ method: 'post',
58
+ path: '/xp/experiments/:id/release-holdout',
59
+ access: 'manage',
60
+ handler: handleReleaseHoldout
61
+ },
62
+ {
63
+ method: 'post',
64
+ path: '/xp/experiments/:id/make-content',
65
+ access: 'manage',
66
+ handler: handleMakeContent
67
+ },
68
+ {
69
+ method: 'post',
70
+ path: '/xp/experiments/:id/archive',
71
+ access: 'manage',
72
+ handler: handleArchive
73
+ },
74
+ {
75
+ method: 'get',
76
+ path: '/xp/experiments/:id/preflight',
77
+ access: 'manage',
78
+ handler: handlePreflight
79
+ },
80
+ {
81
+ method: 'get',
82
+ path: '/xp/experiments/:id/preview-token',
83
+ access: 'manage',
84
+ handler: handlePreviewToken
85
+ },
86
+ {
87
+ method: 'get',
88
+ path: '/xp/experiments/:id/results',
89
+ access: 'readResults',
90
+ handler: handleResults
91
+ },
92
+ {
93
+ method: 'post',
94
+ path: '/xp/experiments/:id/recompute',
95
+ access: 'manage',
96
+ handler: handleRecompute
97
+ },
98
+ {
99
+ method: 'get',
100
+ path: '/xp/experiments/summary',
101
+ access: 'readResults',
102
+ handler: handleSummary
103
+ }
104
+ ];
105
+ function error(status, code, message, detail) {
106
+ return Response.json({
107
+ errors: [
108
+ {
109
+ code,
110
+ message,
111
+ ...detail === undefined ? {} : {
112
+ detail
113
+ }
114
+ }
115
+ ]
116
+ }, {
117
+ status
118
+ });
119
+ }
120
+ export function buildEndpoints(ctx) {
121
+ return ROUTES.map((route)=>({
122
+ method: route.method,
123
+ path: route.path,
124
+ handler: async (req)=>{
125
+ const { options } = ctx;
126
+ const handlerContext = {
127
+ options
128
+ };
129
+ try {
130
+ if (route.access === 'manage' || route.access === 'readResults') {
131
+ if (!req.user) return error(401, 'unauthenticated', 'Authentication required');
132
+ const access = route.access === 'readResults' ? options.access.readResults ?? options.access.manage : options.access.manage;
133
+ if (await access({
134
+ req
135
+ }) !== true) return error(403, 'forbidden', 'Access denied');
136
+ } else if (route.access === 'manifestKey') {
137
+ const secret = options.runtime.proxyRuntime.secrets[0];
138
+ if (!secret) return error(401, 'unauthenticated', 'Invalid manifest key');
139
+ const expected = createHmac('sha256', secret).update('xp:manifest-key').digest('hex');
140
+ const supplied = req.headers.get(XP_MANIFEST_KEY_HEADER);
141
+ // Compare the wire hex bytes, including length and case, rather than permissive hex decoding.
142
+ if (!supplied || Buffer.byteLength(supplied) !== Buffer.byteLength(expected) || !timingSafeEqual(Buffer.from(supplied), Buffer.from(expected))) {
143
+ return error(401, 'unauthenticated', 'Invalid manifest key');
144
+ }
145
+ } else {
146
+ const visitorId = readUnitCookie(req.headers, options.runtime.proxyRuntime);
147
+ if (!visitorId) return error(401, 'unauthenticated', 'Invalid unit cookie');
148
+ handlerContext.unit = {
149
+ visitorId
150
+ };
151
+ }
152
+ return await route.handler(req, handlerContext);
153
+ } catch (cause) {
154
+ if (cause instanceof XpRefusal) return error(cause.status, cause.code, cause.message, cause.detail);
155
+ if (cause instanceof XpNotImplemented) return error(501, 'not-implemented', cause.lane);
156
+ throw cause;
157
+ }
158
+ }
159
+ }));
160
+ }
@@ -0,0 +1,7 @@
1
+ import type { PayloadRequest } from 'payload';
2
+ import type { EndpointContext } from '../types/endpoints.js';
3
+ export declare function handleManifest<TUser extends {
4
+ id: string | number;
5
+ } = {
6
+ id: string | number;
7
+ }>(req: PayloadRequest, _ctx: EndpointContext<TUser>): Promise<Response>;
@@ -0,0 +1,30 @@
1
+ import { pooledExecutor } from '../db/executor.js';
2
+ import { readManifestRow } from '../db/manifest.js';
3
+ export async function handleManifest(req, _ctx) {
4
+ const row = await readManifestRow(pooledExecutor(req.payload));
5
+ if (!row) return Response.json({
6
+ errors: [
7
+ {
8
+ code: 'not-found',
9
+ message: 'Manifest not found'
10
+ }
11
+ ]
12
+ }, {
13
+ status: 404,
14
+ headers: {
15
+ 'Cache-Control': 'no-store'
16
+ }
17
+ });
18
+ const headers = {
19
+ ETag: `"${row.revision}"`,
20
+ 'Cache-Control': 'no-store'
21
+ };
22
+ const ifRevision = req.headers.get('if-none-match');
23
+ if (ifRevision === String(row.revision) || ifRevision === headers.ETag) return new Response(null, {
24
+ status: 304,
25
+ headers
26
+ });
27
+ return Response.json(row.manifest, {
28
+ headers
29
+ });
30
+ }
@@ -0,0 +1,8 @@
1
+ import type { PayloadRequest } from 'payload';
2
+ import type { EndpointContext } from '../types/endpoints.js';
3
+ import type { PreflightDependencies } from '../transitions/preflightData.js';
4
+ export declare function handlePreflight<TUser extends {
5
+ id: string | number;
6
+ } = {
7
+ id: string | number;
8
+ }>(req: PayloadRequest, ctx: EndpointContext<TUser>, dependencies?: PreflightDependencies): Promise<Response>;
@@ -0,0 +1,25 @@
1
+ import { preflightData } from '../transitions/preflightData.js';
2
+ export async function handlePreflight(req, ctx, dependencies) {
3
+ const id = req.routeParams?.id;
4
+ const result = typeof id === 'string' || typeof id === 'number' ? await preflightData(req, {
5
+ experimentId: id,
6
+ options: ctx.options
7
+ }, dependencies) : null;
8
+ const headers = {
9
+ 'Cache-Control': 'no-store'
10
+ };
11
+ if (!result) return Response.json({
12
+ errors: [
13
+ {
14
+ code: 'not-found',
15
+ message: 'Experiment not found'
16
+ }
17
+ ]
18
+ }, {
19
+ status: 404,
20
+ headers
21
+ });
22
+ return Response.json(result, {
23
+ headers
24
+ });
25
+ }
@@ -0,0 +1,7 @@
1
+ import type { PayloadRequest } from 'payload';
2
+ import type { EndpointContext } from '../types/endpoints.js';
3
+ export declare function handlePreviewToken<TUser extends {
4
+ id: string | number;
5
+ } = {
6
+ id: string | number;
7
+ }>(req: PayloadRequest, { options }: EndpointContext<TUser>): Promise<Response>;
@@ -0,0 +1,54 @@
1
+ import { signPreviewToken } from '../runtime/previewToken.js';
2
+ export async function handlePreviewToken(req, { options }) {
3
+ const headers = {
4
+ 'Cache-Control': 'no-store'
5
+ };
6
+ const refuse = (status, code, message)=>Response.json({
7
+ errors: [
8
+ {
9
+ code,
10
+ message
11
+ }
12
+ ]
13
+ }, {
14
+ status,
15
+ headers
16
+ });
17
+ const id = req.routeParams?.id;
18
+ if (typeof id !== 'string' && typeof id !== 'number') return refuse(404, 'not-found', 'Experiment not found');
19
+ const result = await req.payload.find({
20
+ collection: 'xp-experiments',
21
+ req,
22
+ overrideAccess: false,
23
+ depth: 0,
24
+ limit: 1,
25
+ where: {
26
+ id: {
27
+ equals: id
28
+ }
29
+ }
30
+ });
31
+ const experiment = result.docs[0];
32
+ if (!experiment) return refuse(404, 'not-found', 'Experiment not found');
33
+ if (![
34
+ 'draft',
35
+ 'running',
36
+ 'stopped',
37
+ 'decided'
38
+ ].includes(experiment.status)) return refuse(409, 'invalid-status', 'This experiment cannot be previewed');
39
+ const arm = req.searchParams?.get('arm') ?? (req.url ? new URL(req.url).searchParams.get('arm') : null);
40
+ const arms = experiment.arms;
41
+ if (!arm || !arms.some((candidate)=>candidate.key === arm)) return refuse(400, 'invalid-body', 'Unknown experiment arm');
42
+ const now = Math.floor(Date.now() / 1000);
43
+ const token = signPreviewToken({
44
+ e: experiment.id,
45
+ a: arm,
46
+ p: experiment.phase,
47
+ exp: now + 3600
48
+ }, options.runtime.proxyRuntime.secrets, now);
49
+ return Response.json({
50
+ token
51
+ }, {
52
+ headers
53
+ });
54
+ }
@@ -0,0 +1,7 @@
1
+ import type { PayloadRequest } from 'payload';
2
+ import type { EndpointContext } from '../types/endpoints.js';
3
+ export declare function handleRecompute<TUser extends {
4
+ id: string | number;
5
+ } = {
6
+ id: string | number;
7
+ }>(req: PayloadRequest, _ctx: EndpointContext<TUser>): Promise<Response>;
@@ -0,0 +1,41 @@
1
+ import { runResults } from '../server/runResults.js';
2
+ import { readSnapshots, resultsError } from './results.js';
3
+ export async function handleRecompute(req, _ctx) {
4
+ const id = req.routeParams?.id;
5
+ if (typeof id !== 'string' && typeof id !== 'number') return resultsError(404, 'not-found', 'Experiment not found');
6
+ const { docs } = await req.payload.find({
7
+ collection: 'xp-experiments',
8
+ req,
9
+ overrideAccess: false,
10
+ depth: 0,
11
+ limit: 1,
12
+ where: {
13
+ id: {
14
+ equals: id
15
+ }
16
+ }
17
+ });
18
+ if (!docs[0]) return resultsError(404, 'not-found', 'Experiment not found');
19
+ try {
20
+ const result = await runResults(req.payload, {
21
+ experimentId: docs[0].id,
22
+ sweep: false
23
+ });
24
+ if (result.skipped || result.budgetExhausted) return resultsError(503, 'recompute-failed', 'Results could not be recomputed. Try again.');
25
+ const latest = (await readSnapshots(req, [
26
+ docs[0].id
27
+ ], {
28
+ per: 'experiment'
29
+ }))[0];
30
+ if (!latest) return resultsError(422, 'recompute-failed', 'There is no round to analyse yet.');
31
+ return Response.json({
32
+ snapshotId: latest.id
33
+ }, {
34
+ headers: {
35
+ 'Cache-Control': 'no-store'
36
+ }
37
+ });
38
+ } catch {
39
+ return resultsError(503, 'recompute-failed', 'Results could not be recomputed. Try again.');
40
+ }
41
+ }