@delmaredigital/payload-experiments 0.3.0-rc.1 → 0.4.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (338) hide show
  1. package/README.md +172 -21
  2. package/dist/admin/MetricSourceSelect.js +3 -3
  3. package/dist/admin/PlacementStatus.d.ts +3 -0
  4. package/dist/admin/PlacementStatus.js +54 -0
  5. package/dist/admin/PreviewLinks.d.ts +2 -1
  6. package/dist/admin/PreviewLinks.js +47 -26
  7. package/dist/admin/TestsNavLink.d.ts +3 -1
  8. package/dist/admin/TestsNavLink.js +2 -2
  9. package/dist/admin/actions/ConfirmDialog.d.ts +2 -1
  10. package/dist/admin/actions/ConfirmDialog.js +2 -2
  11. package/dist/admin/actions/DecideDialog.d.ts +2 -1
  12. package/dist/admin/actions/DecideDialog.js +2 -2
  13. package/dist/admin/api.d.ts +11 -2
  14. package/dist/admin/api.js +20 -2
  15. package/dist/admin/armSelect/ArmSelectField.js +14 -22
  16. package/dist/admin/arms/arms.model.d.ts +1 -21
  17. package/dist/admin/arms/arms.model.js +1 -153
  18. package/dist/admin/blocks/BlockPanel.d.ts +4 -0
  19. package/dist/admin/blocks/BlockPanel.js +153 -0
  20. package/dist/admin/blocks/TestBlockField.d.ts +5 -0
  21. package/dist/admin/blocks/TestBlockField.js +163 -0
  22. package/dist/admin/blocks/formApi.d.ts +43 -0
  23. package/dist/admin/blocks/formApi.js +153 -0
  24. package/dist/admin/blocks/inject.d.ts +7 -0
  25. package/dist/admin/blocks/inject.js +41 -0
  26. package/dist/admin/blocks/model.d.ts +17 -0
  27. package/dist/admin/blocks/model.js +45 -0
  28. package/dist/admin/blocks/statusCache.d.ts +7 -0
  29. package/dist/admin/blocks/statusCache.js +83 -0
  30. package/dist/admin/blocks/useBlockForm.d.ts +17 -0
  31. package/dist/admin/blocks/useBlockForm.js +46 -0
  32. package/dist/admin/copy/actions.d.ts +2 -0
  33. package/dist/admin/copy/actions.js +9 -0
  34. package/dist/admin/copy/arms.d.ts +1 -6
  35. package/dist/admin/copy/arms.js +1 -16
  36. package/dist/admin/copy/blocks.d.ts +27 -0
  37. package/dist/admin/copy/blocks.js +38 -0
  38. package/dist/admin/copy/goals.d.ts +2 -0
  39. package/dist/admin/copy/goals.js +4 -1
  40. package/dist/admin/copy/newTest.d.ts +10 -0
  41. package/dist/admin/copy/newTest.js +10 -0
  42. package/dist/admin/copy/pages.d.ts +26 -0
  43. package/dist/admin/copy/pages.js +26 -0
  44. package/dist/admin/copy/readiness.d.ts +25 -3
  45. package/dist/admin/copy/readiness.js +25 -3
  46. package/dist/admin/copy/results.d.ts +3 -0
  47. package/dist/admin/copy/results.js +4 -1
  48. package/dist/admin/copy/review.d.ts +1 -1
  49. package/dist/admin/copy/review.js +1 -1
  50. package/dist/admin/copy/surface.d.ts +0 -1
  51. package/dist/admin/copy/surface.js +0 -1
  52. package/dist/admin/copy/testPage.d.ts +4 -0
  53. package/dist/admin/copy/testPage.js +4 -0
  54. package/dist/admin/goals/XpGoalField.d.ts +2 -0
  55. package/dist/admin/goals/XpGoalField.js +152 -0
  56. package/dist/admin/metricSource.model.d.ts +2 -0
  57. package/dist/admin/metricSource.model.js +4 -0
  58. package/dist/admin/pageCollections.d.ts +10 -0
  59. package/dist/admin/pageCollections.js +20 -0
  60. package/dist/admin/paths.d.ts +8 -1
  61. package/dist/admin/paths.js +8 -1
  62. package/dist/admin/placementStatus.model.d.ts +2 -0
  63. package/dist/admin/placementStatus.model.js +19 -0
  64. package/dist/admin/placements/placementLabels.d.ts +11 -0
  65. package/dist/admin/placements/placementLabels.js +21 -0
  66. package/dist/admin/placements/placementLabels.model.d.ts +13 -0
  67. package/dist/admin/placements/placementLabels.model.js +12 -0
  68. package/dist/admin/placements/usePlacementLabels.d.ts +12 -0
  69. package/dist/admin/placements/usePlacementLabels.js +102 -0
  70. package/dist/admin/previewLinks.model.d.ts +2 -0
  71. package/dist/admin/previewLinks.model.js +3 -0
  72. package/dist/admin/results/DataQualityPanel.js +4 -1
  73. package/dist/admin/results/legacy.d.ts +16 -0
  74. package/dist/admin/results/legacy.js +27 -0
  75. package/dist/admin/results/results.model.d.ts +9 -6
  76. package/dist/admin/results/results.model.js +22 -9
  77. package/dist/admin/results/snapshot.d.ts +14 -0
  78. package/dist/admin/results/snapshot.js +3 -0
  79. package/dist/admin/surface/TestsHome.js +43 -15
  80. package/dist/admin/surface/TestsSurface.js +5 -1
  81. package/dist/admin/surface/contracts.d.ts +6 -5
  82. package/dist/admin/surface/goals/GoalForm.js +67 -34
  83. package/dist/admin/surface/goals/GoalsList.js +3 -3
  84. package/dist/admin/surface/goals/data.js +19 -16
  85. package/dist/admin/surface/goals/goals.model.d.ts +1 -2
  86. package/dist/admin/surface/goals/goals.model.js +1 -3
  87. package/dist/admin/surface/home.model.d.ts +2 -1
  88. package/dist/admin/surface/home.model.js +8 -4
  89. package/dist/admin/surface/newTest/NewTest.js +58 -17
  90. package/dist/admin/surface/newTest/PageWizard.d.ts +7 -0
  91. package/dist/admin/surface/newTest/PageWizard.js +209 -0
  92. package/dist/admin/surface/pagePicker/CopyRoute.d.ts +6 -0
  93. package/dist/admin/surface/pagePicker/CopyRoute.js +53 -0
  94. package/dist/admin/surface/pagePicker/PagePicker.d.ts +10 -0
  95. package/dist/admin/surface/pagePicker/PagePicker.js +172 -0
  96. package/dist/admin/surface/pagePicker/pagePicker.model.d.ts +25 -0
  97. package/dist/admin/surface/pagePicker/pagePicker.model.js +25 -0
  98. package/dist/admin/surface/readiness/ActionDialogs.js +103 -69
  99. package/dist/admin/surface/readiness/Banner.js +30 -6
  100. package/dist/admin/surface/readiness/readiness.model.d.ts +8 -1
  101. package/dist/admin/surface/readiness/readiness.model.js +95 -6
  102. package/dist/admin/surface/results/ReleasePages.d.ts +3 -1
  103. package/dist/admin/surface/results/ReleasePages.js +3 -7
  104. package/dist/admin/surface/results/ResultsBody.js +23 -3
  105. package/dist/admin/surface/results/ResultsHeader.js +2 -5
  106. package/dist/admin/surface/results/recommendation.model.d.ts +3 -2
  107. package/dist/admin/surface/results/recommendation.model.js +10 -3
  108. package/dist/admin/surface/results/useResults.d.ts +10 -1
  109. package/dist/admin/surface/surface.css +0 -1
  110. package/dist/admin/surface/test/AdvancedSection.js +111 -104
  111. package/dist/admin/surface/test/GoalSection.js +97 -22
  112. package/dist/admin/surface/test/LengthSection.js +1 -1
  113. package/dist/admin/surface/test/PageHoldout.d.ts +3 -0
  114. package/dist/admin/surface/test/PageHoldout.js +67 -0
  115. package/dist/admin/surface/test/TestPage.js +2 -1
  116. package/dist/admin/surface/test/VersionsSection.js +12 -5
  117. package/dist/admin/surface/test/fields.d.ts +13 -4
  118. package/dist/admin/surface/test/fields.js +46 -14
  119. package/dist/admin/surface/test/jumpToSection.d.ts +1 -1
  120. package/dist/admin/surface/test/jumpToSection.js +4 -3
  121. package/dist/admin/surface/test/pageParts.d.ts +4 -1
  122. package/dist/admin/surface/test/pageParts.js +79 -2
  123. package/dist/admin/surface/test/testPage.css +8 -1
  124. package/dist/admin/surface/test/testPage.model.d.ts +2 -3
  125. package/dist/admin/surface/test/testPage.model.js +1 -21
  126. package/dist/admin/surface/testDocument.model.d.ts +1 -0
  127. package/dist/admin/surface/testDocument.model.js +2 -1
  128. package/dist/admin/surface/useTestDocument.d.ts +11 -2
  129. package/dist/admin/surface/useTestDocument.js +55 -16
  130. package/dist/admin/useApi.d.ts +11 -2
  131. package/dist/admin/useApi.js +3 -5
  132. package/dist/admin/useLayerUsage.js +14 -23
  133. package/dist/admin/views/ExperimentsRootView.js +58 -16
  134. package/dist/admin/views/TestsNavLinkServer.d.ts +2 -0
  135. package/dist/admin/views/TestsNavLinkServer.js +43 -0
  136. package/dist/arms/copy.d.ts +6 -0
  137. package/dist/arms/copy.js +16 -0
  138. package/dist/arms/model.d.ts +23 -0
  139. package/dist/arms/model.js +173 -0
  140. package/dist/blocks/bindings.d.ts +3 -0
  141. package/dist/blocks/bindings.js +34 -0
  142. package/dist/blocks/createExperimentBlock.d.ts +6 -0
  143. package/dist/blocks/createExperimentBlock.js +77 -0
  144. package/dist/blocks/formState.d.ts +31 -0
  145. package/dist/blocks/formState.js +146 -0
  146. package/dist/blocks/model.d.ts +41 -0
  147. package/dist/blocks/model.js +34 -0
  148. package/dist/client/ExperimentGroup.d.ts +6 -0
  149. package/dist/client/ExperimentGroup.js +9 -0
  150. package/dist/client/ExperimentsProvider.js +20 -4
  151. package/dist/client/clickGoals.d.ts +4 -0
  152. package/dist/client/clickGoals.js +43 -0
  153. package/dist/collections/experiments/config.js +12 -2
  154. package/dist/collections/experiments/deleteGuard.d.ts +2 -1
  155. package/dist/collections/experiments/deleteGuard.js +12 -1
  156. package/dist/collections/experiments/editability.js +4 -2
  157. package/dist/collections/experiments/pickSlug.d.ts +2 -0
  158. package/dist/collections/experiments/pickSlug.js +8 -0
  159. package/dist/collections/labels.d.ts +3 -0
  160. package/dist/collections/labels.js +20 -0
  161. package/dist/collections/metrics.js +12 -2
  162. package/dist/collections/phases.js +1 -0
  163. package/dist/collections/serialize.js +7 -0
  164. package/dist/db/appends.d.ts +6 -2
  165. package/dist/db/appends.js +10 -4
  166. package/dist/db/clickGoals.d.ts +9 -0
  167. package/dist/db/clickGoals.js +22 -0
  168. package/dist/db/delete.d.ts +1 -0
  169. package/dist/db/delete.js +1 -0
  170. package/dist/db/locks.d.ts +14 -0
  171. package/dist/db/locks.js +36 -1
  172. package/dist/db/names.d.ts +4 -0
  173. package/dist/db/names.js +4 -0
  174. package/dist/db/pageBindings.d.ts +4 -0
  175. package/dist/db/pageBindings.js +17 -0
  176. package/dist/db/pageBusy.d.ts +2 -0
  177. package/dist/db/pageBusy.js +10 -0
  178. package/dist/db/pageContent.d.ts +10 -0
  179. package/dist/db/pageContent.js +79 -0
  180. package/dist/db/pageOccupancy.d.ts +12 -0
  181. package/dist/db/pageOccupancy.js +45 -0
  182. package/dist/db/placements.d.ts +33 -0
  183. package/dist/db/placements.js +54 -0
  184. package/dist/db/slugs.d.ts +8 -0
  185. package/dist/db/slugs.js +40 -0
  186. package/dist/db/snapshots.d.ts +6 -0
  187. package/dist/db/snapshots.js +9 -5
  188. package/dist/endpoints/clickKeys.d.ts +5 -0
  189. package/dist/endpoints/clickKeys.js +17 -0
  190. package/dist/endpoints/events.js +4 -1
  191. package/dist/endpoints/index.js +28 -0
  192. package/dist/endpoints/pages.d.ts +5 -0
  193. package/dist/endpoints/pages.js +120 -0
  194. package/dist/endpoints/placementStatus.d.ts +6 -0
  195. package/dist/endpoints/placementStatus.js +238 -0
  196. package/dist/endpoints/readBody.d.ts +2 -0
  197. package/dist/endpoints/readBody.js +24 -0
  198. package/dist/endpoints/results.js +5 -1
  199. package/dist/endpoints/summary.js +11 -4
  200. package/dist/endpoints/wrapBlock.d.ts +6 -0
  201. package/dist/endpoints/wrapBlock.js +76 -0
  202. package/dist/errors.d.ts +3 -3
  203. package/dist/errors.js +4 -4
  204. package/dist/events/rows.d.ts +1 -1
  205. package/dist/events/rows.js +5 -3
  206. package/dist/exports/adminClient.d.ts +5 -0
  207. package/dist/exports/adminClient.js +5 -0
  208. package/dist/exports/adminRsc.d.ts +1 -0
  209. package/dist/exports/adminRsc.js +1 -0
  210. package/dist/exports/blocks.d.ts +2 -0
  211. package/dist/exports/blocks.js +1 -0
  212. package/dist/exports/client.d.ts +1 -0
  213. package/dist/exports/client.js +1 -0
  214. package/dist/exports/index.d.ts +6 -1
  215. package/dist/exports/index.js +3 -0
  216. package/dist/exports/puck.d.ts +5 -2
  217. package/dist/exports/puck.js +3 -1
  218. package/dist/exports/puckEditor.d.ts +6 -1
  219. package/dist/exports/puckEditor.js +4 -1
  220. package/dist/exports/rsc.d.ts +4 -0
  221. package/dist/exports/rsc.js +2 -0
  222. package/dist/fields/xpGoal.d.ts +3 -0
  223. package/dist/fields/xpGoal.js +28 -0
  224. package/dist/globals/state.js +10 -0
  225. package/dist/goals/cache.d.ts +3 -0
  226. package/dist/goals/cache.js +35 -0
  227. package/dist/goals/fairness.d.ts +40 -0
  228. package/dist/goals/fairness.js +200 -0
  229. package/dist/goals/key.d.ts +4 -0
  230. package/dist/goals/key.js +3 -0
  231. package/dist/goals/picker/clickGoalPicker.model.d.ts +29 -0
  232. package/dist/goals/picker/clickGoalPicker.model.js +45 -0
  233. package/dist/goals/picker/copy.d.ts +16 -0
  234. package/dist/goals/picker/copy.js +15 -0
  235. package/dist/goals/picker/useClickGoalPicker.d.ts +30 -0
  236. package/dist/goals/picker/useClickGoalPicker.js +220 -0
  237. package/dist/hooks/pageGuard.d.ts +5 -1
  238. package/dist/hooks/pageGuard.js +160 -80
  239. package/dist/lib/fetchAllDocs.d.ts +16 -0
  240. package/dist/lib/fetchAllDocs.js +56 -0
  241. package/dist/onInit.js +29 -0
  242. package/dist/options.d.ts +2 -0
  243. package/dist/options.js +43 -10
  244. package/dist/plugin.js +45 -28
  245. package/dist/puck/ExperimentPicker.js +47 -16
  246. package/dist/puck/ExperimentSlot.d.ts +51 -1
  247. package/dist/puck/ExperimentSlot.js +23 -12
  248. package/dist/puck/copy.d.ts +6 -4
  249. package/dist/puck/copy.js +6 -4
  250. package/dist/puck/editor/CreateExperimentDrawer.js +46 -56
  251. package/dist/puck/editor/ExperimentSlotEditor.d.ts +52 -1
  252. package/dist/puck/editor/ExperimentSlotEditor.js +292 -137
  253. package/dist/puck/editor/ResultsReadout.d.ts +6 -0
  254. package/dist/puck/editor/ResultsReadout.js +99 -0
  255. package/dist/puck/editor/VersionBuilder.d.ts +34 -0
  256. package/dist/puck/editor/VersionBuilder.js +317 -0
  257. package/dist/puck/editor/armWrites.d.ts +7 -0
  258. package/dist/puck/editor/armWrites.js +71 -0
  259. package/dist/puck/editor/armsCache.d.ts +14 -0
  260. package/dist/puck/editor/armsCache.js +78 -0
  261. package/dist/puck/editor/copy.d.ts +33 -1
  262. package/dist/puck/editor/copy.js +45 -8
  263. package/dist/puck/editor/createTest.d.ts +1 -1
  264. package/dist/puck/editor/createTest.js +25 -14
  265. package/dist/puck/editor/editor.chrome.css +48 -161
  266. package/dist/puck/editor/editor.css +48 -161
  267. package/dist/puck/editor/editor.model.d.ts +8 -3
  268. package/dist/puck/editor/editor.model.js +16 -10
  269. package/dist/puck/editor/experimentOutlinePlugin.d.ts +3 -0
  270. package/dist/puck/editor/experimentOutlinePlugin.js +89 -0
  271. package/dist/puck/editor/integration.d.ts +10 -0
  272. package/dist/puck/editor/integration.js +30 -0
  273. package/dist/puck/editor/xpGoalPuckEditorField.d.ts +7 -0
  274. package/dist/puck/editor/xpGoalPuckEditorField.js +134 -0
  275. package/dist/puck/experimentSlotField.d.ts +2 -0
  276. package/dist/puck/experimentSlotField.js +11 -3
  277. package/dist/puck/hostContext.d.ts +10 -0
  278. package/dist/puck/hostContext.js +12 -0
  279. package/dist/puck/integration.d.ts +6 -0
  280. package/dist/puck/integration.js +21 -0
  281. package/dist/puck/routes.d.ts +12 -0
  282. package/dist/puck/routes.js +11 -0
  283. package/dist/puck/slotConfig.d.ts +1 -1
  284. package/dist/puck/slotConfig.js +2 -2
  285. package/dist/puck/xpGoalField.d.ts +3 -0
  286. package/dist/puck/xpGoalField.js +6 -0
  287. package/dist/results/health.d.ts +4 -0
  288. package/dist/results/health.js +27 -0
  289. package/dist/results/snapshot.js +15 -2
  290. package/dist/sanitizeReadinessDetail.js +51 -1
  291. package/dist/server/metadata.js +9 -2
  292. package/dist/server/normaliseContentRelationships.d.ts +7 -0
  293. package/dist/server/normaliseContentRelationships.js +62 -0
  294. package/dist/server/pageKindSeoContract.d.ts +27 -0
  295. package/dist/server/pageKindSeoContract.js +80 -0
  296. package/dist/server/recordContentChange.d.ts +1 -1
  297. package/dist/server/recordContentChange.js +72 -13
  298. package/dist/server/resolveExperimentBlocks.d.ts +5 -0
  299. package/dist/server/resolveExperimentBlocks.js +54 -0
  300. package/dist/server/runResults.js +92 -19
  301. package/dist/server/toClientAssignments.js +2 -1
  302. package/dist/transitions/addMetric.d.ts +2 -1
  303. package/dist/transitions/addMetric.js +28 -1
  304. package/dist/transitions/checkStart.d.ts +17 -1
  305. package/dist/transitions/checkStart.js +99 -7
  306. package/dist/transitions/decide.js +40 -1
  307. package/dist/transitions/deleteExperiment.d.ts +3 -1
  308. package/dist/transitions/deleteExperiment.js +27 -1
  309. package/dist/transitions/kernel.js +4 -0
  310. package/dist/transitions/makeContent.d.ts +5 -0
  311. package/dist/transitions/makeContent.js +39 -11
  312. package/dist/transitions/pageCollection.d.ts +10 -0
  313. package/dist/transitions/pageCollection.js +20 -0
  314. package/dist/transitions/pageVersions.d.ts +10 -0
  315. package/dist/transitions/pageVersions.js +44 -0
  316. package/dist/transitions/phaseRecord.d.ts +1 -0
  317. package/dist/transitions/placementInteractions.d.ts +40 -0
  318. package/dist/transitions/placementInteractions.js +94 -0
  319. package/dist/transitions/placements.d.ts +10 -6
  320. package/dist/transitions/placements.js +156 -2
  321. package/dist/transitions/preflightData.d.ts +8 -0
  322. package/dist/transitions/preflightData.js +248 -36
  323. package/dist/transitions/resolveCaps.d.ts +1 -0
  324. package/dist/transitions/resolveCaps.js +6 -2
  325. package/dist/transitions/start.js +77 -3
  326. package/dist/types/admin.d.ts +1 -1
  327. package/dist/types/assignments.d.ts +1 -0
  328. package/dist/types/contracts.d.ts +31 -10
  329. package/dist/types/endpoints.d.ts +92 -5
  330. package/dist/types/index.d.ts +3 -0
  331. package/dist/types/puck.d.ts +7 -1
  332. package/dist/types/snapshot.d.ts +17 -4
  333. package/dist/types/surface.d.ts +13 -0
  334. package/dist/ui/Tabs.d.ts +4 -2
  335. package/dist/ui/Tabs.js +33 -26
  336. package/dist/ui/accent.d.ts +22 -3
  337. package/dist/ui/accent.js +74 -16
  338. package/package.json +12 -2
@@ -7,7 +7,7 @@ import type { Snapshot, VerdictCode } from './snapshot.js';
7
7
  export type ArmIssueCode = 'weights-total' | 'control-count' | 'control-first' | 'holdout-last' | 'holdout-count' | 'variant-count' | 'duplicate-key' | 'duplicate-slot' | 'slot-required' | 'invalid-key';
8
8
  /** Content-report dismiss/restore refusals (CORE-44). */
9
9
  export type ContentEntryRefusal = 'not-decided' | 'entry-not-found' | 'entry-ok' | 'entry-dismissed' | 'entry-not-dismissed';
10
- export type RefusalCode = ArmIssueCode | 'not-found' | ContentEntryRefusal | 'no-planned-duration' | 'no-primary-metric' | 'no-placements' | 'empty-slot' | 'page-no-targets' | 'page-target-unpublished' | 'page-user-unit' | 'version-page-unavailable' | 'version-page-unchecked' | 'flag-no-point' | 'flag-params-invalid' | 'flag-point-busy' | 'flag-placement-missing' | 'layer-full' | 'layer-unit-mismatch' | 'unknown-metric-source' | 'unit-source-mismatch' | 'no-reach-per-day' | 'invalid-status' | 'stale-draft' | 'stale-edit' | 'commit-unverified' | 'allocation-lower' | 'confirmation-required' | 'invalid-winner' | 'slices-taken' | 'locked-placement' | 'not-implemented' | 'forbidden' | 'unauthenticated' | 'body-too-large' | 'invalid-body' | 'too-many-ids' | 'invalid-phase' | 'invalid-ids' | 'recompute-failed' | 'analysis-failed' | 'undeclared-targeting-key' | 'invalid-targeting-rule' | 'block-path-rule' | 'duplicate-metric';
10
+ export type RefusalCode = ArmIssueCode | 'goal-in-some-versions' | 'placement-unchecked' | 'placement-unpublished' | 'duplicate-version-row' | 'interacting-tests' | 'placements-changed' | 'page-busy' | 'wrap-target-missing' | 'version-identical' | 'version-same-address' | 'unknown-collection' | 'not-found' | ContentEntryRefusal | 'no-planned-duration' | 'no-primary-metric' | 'no-placements' | 'empty-slot' | 'page-in-use' | 'page-no-targets' | 'page-target-unpublished' | 'page-user-unit' | 'version-page-unavailable' | 'version-page-unchecked' | 'flag-no-point' | 'flag-params-invalid' | 'flag-point-busy' | 'flag-placement-missing' | 'layer-full' | 'layer-unit-mismatch' | 'unknown-metric-source' | 'unit-source-mismatch' | 'no-reach-per-day' | 'invalid-status' | 'stale-draft' | 'stale-edit' | 'commit-unverified' | 'allocation-lower' | 'confirmation-required' | 'invalid-winner' | 'slices-taken' | 'locked-placement' | 'not-implemented' | 'forbidden' | 'unauthenticated' | 'body-too-large' | 'invalid-body' | 'too-many-ids' | 'invalid-phase' | 'invalid-ids' | 'recompute-failed' | 'analysis-failed' | 'undeclared-targeting-key' | 'invalid-targeting-rule' | 'block-path-rule' | 'duplicate-metric';
11
11
  export type ErrorEnvelope = {
12
12
  errors: Array<{
13
13
  code: RefusalCode;
@@ -22,6 +22,13 @@ export type TransitionSuccess = {
22
22
  export type AllocateBody = {
23
23
  allocation: number;
24
24
  };
25
+ export type AddMetricWarning = {
26
+ code: 'goal-in-some-versions' | 'goal-added-mid-test' | 'placement-unchecked';
27
+ detail?: unknown;
28
+ };
29
+ export type AddMetricResult = TransitionSuccess & {
30
+ warnings: AddMetricWarning[];
31
+ };
25
32
  export type AddMetricBody = {
26
33
  metric: DocId;
27
34
  role: 'secondary' | 'guardrail';
@@ -57,12 +64,27 @@ export type MakeContentResult = TransitionSuccess & {
57
64
  }>;
58
65
  };
59
66
  export type PreflightResult = {
67
+ goalNotes?: Array<{
68
+ metricId: DocId;
69
+ message: string;
70
+ }>;
60
71
  checks: Array<{
61
72
  code: ReadinessCode;
62
73
  ok: boolean;
63
74
  detail?: unknown;
64
75
  warning?: boolean;
65
76
  }>;
77
+ pagePlacements?: Array<{
78
+ arm: string;
79
+ path: string;
80
+ collection?: string;
81
+ id?: DocId | null;
82
+ title?: string;
83
+ published: boolean;
84
+ slug?: string;
85
+ url?: string;
86
+ editorUrl?: string;
87
+ }>;
66
88
  placements: Array<{
67
89
  collection: string;
68
90
  id: DocId;
@@ -104,6 +126,7 @@ export interface PhaseSummary {
104
126
  name: string;
105
127
  kind: MetricSource['kind'];
106
128
  role: 'primary' | 'secondary' | 'guardrail';
129
+ clickGoal?: true;
107
130
  resolvedCap?: number | null;
108
131
  historyUnits?: number;
109
132
  capNotice?: 'no-history';
@@ -137,10 +160,7 @@ export type SummaryResponse = Record<DocId, {
137
160
  code: ReadinessCode;
138
161
  detail?: unknown;
139
162
  };
140
- health?: Array<{
141
- code: 'version-page-unavailable' | 'version-page-unchecked';
142
- path: string;
143
- }>;
163
+ health?: import('./snapshot.js').SnapshotHealth[];
144
164
  exposures: number;
145
165
  days: number;
146
166
  verdict: {
@@ -174,3 +194,70 @@ export type EndpointContext<TUser extends {
174
194
  visitorId: string;
175
195
  };
176
196
  };
197
+ export type ClickKeysResult = {
198
+ keys: Array<{
199
+ key: string;
200
+ arms: string[];
201
+ label: string;
202
+ }>;
203
+ };
204
+ export type PagesResult = {
205
+ docs: Array<{
206
+ id: DocId;
207
+ title: string;
208
+ path: string | null;
209
+ pathKey: string | null;
210
+ published: boolean;
211
+ }>;
212
+ hasNextPage: boolean;
213
+ };
214
+ export type PlacementStatusBody = {
215
+ docs?: Array<{
216
+ collection: string;
217
+ id: DocId;
218
+ }>;
219
+ slugs?: string[];
220
+ };
221
+ export type PageTestStatus = {
222
+ slug: string;
223
+ name: string;
224
+ status: Status;
225
+ role: 'original' | 'version';
226
+ winner: boolean;
227
+ armLabel: string;
228
+ winnerLabel?: string;
229
+ winnerTitle?: string;
230
+ winnerPath?: string;
231
+ originalPath?: string;
232
+ rollout: boolean;
233
+ pagesReleased: boolean;
234
+ };
235
+ export type PlacementStatusResult = {
236
+ docs: Record<string, {
237
+ path: string | null;
238
+ tests: PageTestStatus[];
239
+ }>;
240
+ slugs: Record<string, {
241
+ missing: true;
242
+ } | {
243
+ deleted: true;
244
+ } | {
245
+ name: string;
246
+ status: Status;
247
+ kind: ExperimentKind;
248
+ arms: Array<{
249
+ key: string;
250
+ label: string;
251
+ slot?: string;
252
+ }>;
253
+ winnerArm?: string;
254
+ rollout: boolean;
255
+ }>;
256
+ };
257
+ export type WrapBlockBody = {
258
+ collection: string;
259
+ id: DocId;
260
+ field: string;
261
+ blockId: string;
262
+ experiment: string;
263
+ };
@@ -7,3 +7,6 @@ export * from './admin.js';
7
7
  export * from './endpoints.js';
8
8
  export * from './snapshot.js';
9
9
  export * from './reasons.js';
10
+ export type { HostBlock, ExperimentBlock, ExperimentBlockGroup, ResolvedHostBlock, ResolvedExperimentBlocks } from '../blocks/model.js';
11
+ export type { WrapBlockBody } from './endpoints.js';
12
+ export type { SnapshotHealth } from './snapshot.js';
@@ -1,5 +1,5 @@
1
1
  import type { DocId } from './contracts.js';
2
- import type { Slot } from '@puckeditor/core';
2
+ import type { ComponentConfig, Config, Plugin, Slot } from '@puckeditor/core';
3
3
  export type { Slot } from '@puckeditor/core';
4
4
  export type ArmSlot = 'control' | 'variantA' | 'variantB' | 'variantC';
5
5
  export type ExperimentRef = {
@@ -21,3 +21,9 @@ export type ExperimentSlotProps = {
21
21
  };
22
22
  };
23
23
  export declare const EXPERIMENT_SLOT_TYPE: "ExperimentSlot";
24
+ export interface ExperimentsPuckIntegration {
25
+ name: string;
26
+ components: Record<string, ComponentConfig<ExperimentSlotProps>>;
27
+ categories: Config['categories'];
28
+ plugins?: readonly Plugin[];
29
+ }
@@ -1,3 +1,18 @@
1
+ export type SnapshotHealth = {
2
+ code: 'version-page-unavailable' | 'version-page-unchecked';
3
+ path: string;
4
+ } | {
5
+ code: 'goal-in-some-versions';
6
+ metricId: string | number;
7
+ name: string;
8
+ } | {
9
+ code: 'placement-unchecked';
10
+ detail: {
11
+ collection?: string;
12
+ id?: string | number;
13
+ path?: string;
14
+ };
15
+ };
1
16
  import type { GaussianPrior } from '@delmaredigital/experiments-stats';
2
17
  import type { DocId, MetricSource } from './contracts.js';
3
18
  import type { MetricUnavailableReason } from './reasons.js';
@@ -108,10 +123,8 @@ export interface Snapshot {
108
123
  guardrailLine?: string;
109
124
  worstArm?: string;
110
125
  };
111
- health?: Array<{
112
- code: 'version-page-unavailable' | 'version-page-unchecked';
113
- path: string;
114
- }>;
126
+ goalFairnessNotice?: string;
127
+ health?: SnapshotHealth[];
115
128
  contentChanges: Array<{
116
129
  at: string;
117
130
  doc: {
@@ -7,6 +7,9 @@ export interface TestsSurfaceProps {
7
7
  returnToTest?: string;
8
8
  adminRoute: string;
9
9
  apiRoute: string;
10
+ collectionTitles?: Record<string, string>;
11
+ userId?: string | number;
12
+ userCollection?: string;
10
13
  isAdmin: boolean;
11
14
  pages?: Array<{
12
15
  collection: string;
@@ -14,6 +17,15 @@ export interface TestsSurfaceProps {
14
17
  id: DocId;
15
18
  title: string;
16
19
  editorUrl: string;
20
+ editor: 'puck' | 'blocks';
21
+ }>;
22
+ pageCollections?: Array<{
23
+ slug: string;
24
+ label: string;
25
+ contentChecks: boolean;
26
+ copyHint?: string;
27
+ copyUrl?: string;
28
+ copyLabel?: string;
17
29
  }>;
18
30
  canManage: boolean;
19
31
  canReadResults: boolean;
@@ -22,6 +34,7 @@ export interface TestsSurfaceProps {
22
34
  metricSources: SerializedMetricSource[];
23
35
  admin?: {
24
36
  descriptions?: {
37
+ page?: string;
25
38
  block?: string;
26
39
  document?: string;
27
40
  setting?: string;
package/dist/ui/Tabs.d.ts CHANGED
@@ -1,14 +1,16 @@
1
- import type { ReactNode } from 'react';
1
+ import type { ReactNode, Ref } from 'react';
2
2
  import './ui.css';
3
3
  export interface Tab {
4
4
  id: string;
5
5
  label: string;
6
6
  content: ReactNode;
7
+ actions?: ReactNode;
7
8
  disabled?: boolean;
8
9
  }
9
- export declare function Tabs({ items, label, value, onChange }: {
10
+ export declare function Tabs({ items, label, value, onChange, tabListRef }: {
10
11
  items: Tab[];
11
12
  label: string;
12
13
  value?: string;
13
14
  onChange?: (id: string) => void;
15
+ tabListRef?: Ref<HTMLDivElement>;
14
16
  }): import("react").JSX.Element;
package/dist/ui/Tabs.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useId, useState } from 'react';
4
4
  import './ui.css';
5
- export function Tabs({ items, label, value, onChange }) {
5
+ export function Tabs({ items, label, value, onChange, tabListRef }) {
6
6
  const prefix = useId();
7
7
  const [local, setLocal] = useState(()=>items.find((item)=>!item.disabled)?.id);
8
8
  const requested = value ?? local;
@@ -14,31 +14,38 @@ export function Tabs({ items, label, value, onChange }) {
14
14
  return /*#__PURE__*/ _jsxs("div", {
15
15
  className: "xp-ui-tabs",
16
16
  children: [
17
- /*#__PURE__*/ _jsx("div", {
18
- role: "tablist",
19
- "aria-label": label,
20
- className: "xp-ui-tablist",
21
- children: items.map((item)=>/*#__PURE__*/ _jsx("button", {
22
- type: "button",
23
- role: "tab",
24
- id: `${prefix}-tab-${item.id}`,
25
- "aria-controls": `${prefix}-panel-${item.id}`,
26
- "aria-selected": selected === item.id,
27
- tabIndex: selected === item.id ? 0 : -1,
28
- disabled: item.disabled,
29
- className: "xp-ui-tab",
30
- onClick: ()=>select(item.id),
31
- onKeyDown: (event)=>{
32
- const enabled = items.filter((tab)=>!tab.disabled);
33
- const index = enabled.findIndex((tab)=>tab.id === item.id);
34
- const next = event.key === 'ArrowRight' ? enabled[(index + 1) % enabled.length] : event.key === 'ArrowLeft' ? enabled[(index - 1 + enabled.length) % enabled.length] : event.key === 'Home' ? enabled[0] : event.key === 'End' ? enabled.at(-1) : undefined;
35
- if (!next) return;
36
- event.preventDefault();
37
- select(next.id);
38
- event.currentTarget.ownerDocument.getElementById(`${prefix}-tab-${next.id}`)?.focus();
39
- },
40
- children: item.label
41
- }, item.id))
17
+ /*#__PURE__*/ _jsxs("div", {
18
+ ref: tabListRef,
19
+ className: "xp-ui-tabs-header",
20
+ children: [
21
+ /*#__PURE__*/ _jsx("div", {
22
+ role: "tablist",
23
+ "aria-label": label,
24
+ className: "xp-ui-tablist",
25
+ children: items.map((item)=>/*#__PURE__*/ _jsx("button", {
26
+ type: "button",
27
+ role: "tab",
28
+ id: `${prefix}-tab-${item.id}`,
29
+ "aria-controls": `${prefix}-panel-${item.id}`,
30
+ "aria-selected": selected === item.id,
31
+ tabIndex: selected === item.id ? 0 : -1,
32
+ disabled: item.disabled,
33
+ className: "xp-ui-tab",
34
+ onClick: ()=>select(item.id),
35
+ onKeyDown: (event)=>{
36
+ const enabled = items.filter((tab)=>!tab.disabled);
37
+ const index = enabled.findIndex((tab)=>tab.id === item.id);
38
+ const next = event.key === 'ArrowRight' ? enabled[(index + 1) % enabled.length] : event.key === 'ArrowLeft' ? enabled[(index - 1 + enabled.length) % enabled.length] : event.key === 'Home' ? enabled[0] : event.key === 'End' ? enabled.at(-1) : undefined;
39
+ if (!next) return;
40
+ event.preventDefault();
41
+ select(next.id);
42
+ event.currentTarget.ownerDocument.getElementById(`${prefix}-tab-${next.id}`)?.focus();
43
+ },
44
+ children: item.label
45
+ }, item.id))
46
+ }),
47
+ items.find((item)=>item.id === selected)?.actions
48
+ ]
42
49
  }),
43
50
  items.map((item)=>/*#__PURE__*/ _jsx("div", {
44
51
  role: "tabpanel",
@@ -10,12 +10,31 @@ export interface AccentSet {
10
10
  }
11
11
  /** WCAG 2.x relative luminance contrast. */
12
12
  export declare function contrast(a: string, b: string): number;
13
- /** Pure shared host-accent derivation. Reference surfaces match theme.css's Payload
14
- * 3.90.2 white / elevation-50 dark. Dark adjustment increases HSL lightness until
15
- * the quantised hex reaches 3:1. Hover/active move up to 12/24% in the interaction direction, clamped to preserve text contrast; subtle is 8% tint. */
13
+ /** Contrast floors for accent text and focus rings. Black or white always clears them on a real
14
+ * surface, so the fallbacks below are defensive; tests raise the floors to exercise them. */
15
+ type Floors = {
16
+ text: number;
17
+ focus: number;
18
+ };
19
+ /** Pure shared host-accent derivation using theme.css's reference surfaces. */
16
20
  export declare function deriveAccent(input: string): {
17
21
  light: AccentSet;
18
22
  dark: AccentSet;
19
23
  };
24
+ /** Module-only surface injection; not a package API. Dark adjustment raises HSL
25
+ * lightness to 3:1. Interaction steps preserve the foreground and prefer the
26
+ * original direction when both steps reach 1.1:1; subtle remains an 8% tint. */
27
+ export declare function deriveAccentFor(input: string, surfaces: {
28
+ light: string;
29
+ dark: string;
30
+ }, floors?: Floors): {
31
+ light: AccentSet;
32
+ dark: AccentSet;
33
+ };
20
34
  /** Validated hex values only; specificity exceeds theme.css regardless of load order. */
21
35
  export declare function accentStyles(input?: string): string | undefined;
36
+ /** The plugin and Puck factory accept exactly the same host accent. */
37
+ export declare function validateAccent(accent: unknown): void;
38
+ /** Inline variables use the same names as the admin accent stylesheet. */
39
+ export declare function accentVariables(set: AccentSet): Record<string, string>;
40
+ export {};
package/dist/ui/accent.js CHANGED
@@ -38,11 +38,8 @@ function mix(a, b, amount) {
38
38
  }
39
39
  return undefined;
40
40
  }
41
- function set(accent, surface, dark = false) {
42
- const fg = contrast(accent, '#ffffff') >= contrast(accent, '#141414') ? '#ffffff' : '#141414';
43
- // Light fills with dark text lighten on interaction. Dark-theme fills follow
44
- // the default dark tokens and also lighten. Clamp to preserve one foreground.
45
- const target = dark || fg === '#141414' ? '#ffffff' : '#000000';
41
+ const interactionStep = (accent, states)=>Math.min(contrast(accent, states.hover), contrast(states.hover, states.active));
42
+ /** Keep the old 12/24% steps when visible, otherwise search the full direction. */ function interaction(accent, fg, target) {
46
43
  let low = 0, high = 0.24;
47
44
  for(let i = 0; i < 24; i++){
48
45
  const amount = (low + high) / 2;
@@ -50,32 +47,93 @@ function set(accent, surface, dark = false) {
50
47
  else high = amount;
51
48
  }
52
49
  const amount = contrast(mix(accent, target, 0.24), fg) >= 4.5 ? 0.24 : low;
53
- const accentText = againstSurface(accent, surface, 4.5);
50
+ const original = {
51
+ hover: mix(accent, target, amount / 2),
52
+ active: mix(accent, target, amount)
53
+ };
54
+ if (interactionStep(accent, original) >= 1.1) return original;
55
+ const candidates = [
56
+ accent
57
+ ];
58
+ let hover;
59
+ for(let step = 1; step <= 1000; step++){
60
+ const candidate = mix(accent, target, step / 1000);
61
+ if (contrast(candidate, fg) < 4.5) break;
62
+ candidates.push(candidate);
63
+ if (!hover && contrast(accent, candidate) >= 1.1) hover = candidate;
64
+ else if (hover && contrast(hover, candidate) >= 1.1) return {
65
+ hover,
66
+ active: candidate
67
+ };
68
+ }
69
+ // If two full steps cannot fit, maximise the smaller step to the last safe fill.
70
+ const active = candidates[candidates.length - 1];
71
+ return candidates.reduce((best, candidate)=>{
72
+ const next = {
73
+ hover: candidate,
74
+ active
75
+ };
76
+ return interactionStep(accent, next) > interactionStep(accent, best) ? next : best;
77
+ }, {
78
+ hover: accent,
79
+ active
80
+ });
81
+ }
82
+ const FLOORS = {
83
+ text: 4.5,
84
+ focus: 3
85
+ };
86
+ function set(accent, surface, dark = false, floors = FLOORS) {
87
+ const fg = contrast(accent, '#ffffff') >= contrast(accent, '#141414') ? '#ffffff' : '#141414';
88
+ const target = dark || fg === '#141414' ? '#ffffff' : '#000000';
89
+ const preferred = interaction(accent, fg, target);
90
+ const other = interaction(accent, fg, target === '#ffffff' ? '#000000' : '#ffffff');
91
+ const states = interactionStep(accent, preferred) >= 1.1 || interactionStep(accent, preferred) >= interactionStep(accent, other) ? preferred : other;
92
+ const accentText = againstSurface(accent, surface, floors.text);
54
93
  return {
55
94
  accent,
56
- hover: mix(accent, target, amount / 2),
57
- active: mix(accent, target, amount),
95
+ ...states,
58
96
  subtle: mix(surface, accent, 0.08),
59
97
  fg,
60
- focus: againstSurface(accent, surface, 3) ?? 'var(--xp-text)',
98
+ focus: againstSurface(accent, surface, floors.focus) ?? 'var(--xp-text)',
61
99
  accentText: accentText ?? 'var(--xp-text)',
62
100
  accentTextDecoration: accentText ? 'none' : 'underline'
63
101
  };
64
102
  }
65
- /** Pure shared host-accent derivation. Reference surfaces match theme.css's Payload
66
- * 3.90.2 white / elevation-50 dark. Dark adjustment increases HSL lightness until
67
- * the quantised hex reaches 3:1. Hover/active move up to 12/24% in the interaction direction, clamped to preserve text contrast; subtle is 8% tint. */ export function deriveAccent(input) {
103
+ /** Pure shared host-accent derivation using theme.css's reference surfaces. */ export function deriveAccent(input) {
104
+ return deriveAccentFor(input, {
105
+ light: '#ffffff',
106
+ dark: '#222222'
107
+ });
108
+ }
109
+ /** Module-only surface injection; not a package API. Dark adjustment raises HSL
110
+ * lightness to 3:1. Interaction steps preserve the foreground and prefer the
111
+ * original direction when both steps reach 1.1:1; subtle remains an 8% tint. */ export function deriveAccentFor(input, surfaces, floors = FLOORS) {
68
112
  const accent = hexOf(rgb(input));
69
113
  let dark = accent;
70
- for(let step = 1; contrast(dark, '#222222') < 3 && step <= 1000; step++)dark = lighten(accent, step / 1000);
114
+ for(let step = 1; contrast(dark, surfaces.dark) < 3 && step <= 1000; step++)dark = lighten(accent, step / 1000);
71
115
  return {
72
- light: set(accent, '#ffffff'),
73
- dark: set(dark, '#222222', true)
116
+ light: set(accent, surfaces.light, false, floors),
117
+ dark: set(dark, surfaces.dark, true, floors)
74
118
  };
75
119
  }
76
120
  /** Validated hex values only; specificity exceeds theme.css regardless of load order. */ export function accentStyles(input) {
77
121
  if (!input) return undefined;
78
122
  const sets = deriveAccent(input);
79
- const declarations = (s)=>Object.entries(s).map(([name, value])=>`--xp-${name === 'accentText' ? 'accent-text' : name === 'accentTextDecoration' ? 'accent-text-decoration' : name === 'focus' || name === 'accent' ? name : `accent-${name}`}:${value};`).join('');
123
+ const declarations = (s)=>Object.entries(accentVariables(s)).map(([name, value])=>`${name}:${value};`).join('');
80
124
  return `:root:root, [data-theme="light"][data-theme="light"]{${declarations(sets.light)}}\n[data-theme="dark"][data-theme="dark"]{${declarations(sets.dark)}}`;
81
125
  }
126
+ /** The plugin and Puck factory accept exactly the same host accent. */ export function validateAccent(accent) {
127
+ if (accent === undefined || accent === '') return;
128
+ if (typeof accent !== 'string' || !/^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.test(accent)) throw new Error('Experiments theme.accent must be a CSS hex colour (#rgb or #rrggbb)');
129
+ for (const [mode, set] of Object.entries(deriveAccent(accent))){
130
+ const ratio = contrast(set.accent, set.fg);
131
+ if (ratio < 4.5) throw new Error(`Experiments theme.accent ${accent}: ${mode} foreground contrast ${ratio.toFixed(2)}:1 is below 4.5:1`);
132
+ }
133
+ }
134
+ /** Inline variables use the same names as the admin accent stylesheet. */ export function accentVariables(set) {
135
+ return Object.fromEntries(Object.entries(set).map(([name, value])=>[
136
+ `--xp-${name === 'accentText' ? 'accent-text' : name === 'accentTextDecoration' ? 'accent-text-decoration' : name === 'focus' || name === 'accent' ? name : `accent-${name}`}`,
137
+ value
138
+ ]));
139
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delmaredigital/payload-experiments",
3
- "version": "0.3.0-rc.1",
3
+ "version": "0.4.0-rc.1",
4
4
  "description": "No-code A/B testing for Payload CMS and Next.js: block, page and flag experiments with holdouts, layers, exposure logging and honest statistics inside the admin — no vendor",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -37,6 +37,12 @@
37
37
  "import": "./dist/exports/rsc.js",
38
38
  "default": "./dist/exports/rsc.js"
39
39
  },
40
+ "./blocks": {
41
+ "@delmaredigital/source": "./src/exports/blocks.ts",
42
+ "types": "./dist/exports/blocks.d.ts",
43
+ "import": "./dist/exports/blocks.js",
44
+ "default": "./dist/exports/blocks.js"
45
+ },
40
46
  "./client": {
41
47
  "@delmaredigital/source": "./src/exports/client.ts",
42
48
  "types": "./dist/exports/client.d.ts",
@@ -68,6 +74,8 @@
68
74
  "default": "./dist/exports/puckEditor.js"
69
75
  },
70
76
  "./puck/editor.css": "./dist/puck/editor/editor.css",
77
+ "./admin/theme.css": "./dist/admin/theme.css",
78
+ "./admin/ui.css": "./dist/ui/ui.css",
71
79
  "./package.json": "./package.json"
72
80
  },
73
81
  "files": [
@@ -118,6 +126,7 @@
118
126
  },
119
127
  "homepage": "https://delmaredigital.com/developers",
120
128
  "devDependencies": {
129
+ "@delmaredigital/payload-puck": "0.10.3",
121
130
  "payload": "^3.90.2",
122
131
  "@payloadcms/db-postgres": "^3.90.2",
123
132
  "@payloadcms/ui": "^3.90.2",
@@ -135,7 +144,8 @@
135
144
  "@electric-sql/pglite": "^0.5.13",
136
145
  "@puckeditor/core": "^0.24.0",
137
146
  "drizzle-orm": "0.45.2",
138
- "drizzle-kit": "0.31.7"
147
+ "drizzle-kit": "0.31.7",
148
+ "yaml": "2.9.1"
139
149
  },
140
150
  "scripts": {
141
151
  "build": "swc ./src -d ./dist --strip-leading-paths --config-file ../../.swcrc && tsc -p tsconfig.build.json && copyfiles -u 1 \"src/**/*.css\" dist",