@10x-media/form-builder 0.1.0-beta.2 → 0.1.0-beta.21

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 (619) hide show
  1. package/CHANGELOG.md +360 -0
  2. package/README.md +7 -5
  3. package/dist/actions/assertNoBlockCollision.js +18 -0
  4. package/dist/actions/assertNoBlockCollision.js.map +1 -0
  5. package/dist/actions/body/converters.d.ts +20 -0
  6. package/dist/actions/body/converters.js +57 -0
  7. package/dist/actions/body/converters.js.map +1 -0
  8. package/dist/actions/body/escapeHtml.d.ts +6 -0
  9. package/dist/actions/body/escapeHtml.js +14 -0
  10. package/dist/actions/body/escapeHtml.js.map +1 -0
  11. package/dist/actions/body/serializeBody.d.ts +55 -0
  12. package/dist/actions/body/serializeBody.js +78 -0
  13. package/dist/actions/body/serializeBody.js.map +1 -0
  14. package/dist/actions/body/serializeSlate.js +31 -0
  15. package/dist/actions/body/serializeSlate.js.map +1 -0
  16. package/dist/actions/body/textOfBody.d.ts +13 -0
  17. package/dist/actions/body/textOfBody.js +35 -0
  18. package/dist/actions/body/textOfBody.js.map +1 -0
  19. package/dist/actions/body/wildcards.d.ts +10 -0
  20. package/dist/actions/body/wildcards.js +33 -0
  21. package/dist/actions/body/wildcards.js.map +1 -0
  22. package/dist/actions/buildActionBlocks.js +12 -9
  23. package/dist/actions/buildActionBlocks.js.map +1 -1
  24. package/dist/actions/builtin/confirmation.js +47 -43
  25. package/dist/actions/builtin/confirmation.js.map +1 -1
  26. package/dist/actions/builtin/emailAction.d.ts +20 -0
  27. package/dist/actions/builtin/emailAction.js +120 -0
  28. package/dist/actions/builtin/emailAction.js.map +1 -0
  29. package/dist/actions/builtin/emailTeam.js +20 -38
  30. package/dist/actions/builtin/emailTeam.js.map +1 -1
  31. package/dist/actions/builtin/index.d.ts +16 -1
  32. package/dist/actions/builtin/index.js +21 -7
  33. package/dist/actions/builtin/index.js.map +1 -1
  34. package/dist/actions/builtin/signedWebhook.js +6 -2
  35. package/dist/actions/builtin/signedWebhook.js.map +1 -1
  36. package/dist/actions/defineAction.d.ts +17 -5
  37. package/dist/actions/defineAction.js.map +1 -1
  38. package/dist/actions/dispatch.js +51 -6
  39. package/dist/actions/dispatch.js.map +1 -1
  40. package/dist/actions/dispatchContext.js +13 -0
  41. package/dist/actions/dispatchContext.js.map +1 -0
  42. package/dist/actions/emailRecipients.d.ts +12 -0
  43. package/dist/actions/emailRecipients.js +146 -0
  44. package/dist/actions/emailRecipients.js.map +1 -0
  45. package/dist/actions/fromAddresses.d.ts +37 -0
  46. package/dist/actions/fromAddresses.js +131 -0
  47. package/dist/actions/fromAddresses.js.map +1 -0
  48. package/dist/actions/recipientSources.d.ts +34 -0
  49. package/dist/actions/recipientSources.js +11 -0
  50. package/dist/actions/recipientSources.js.map +1 -0
  51. package/dist/actions/registry.d.ts +2 -1
  52. package/dist/actions/registry.js +5 -7
  53. package/dist/actions/registry.js.map +1 -1
  54. package/dist/actions/runActions.js +10 -1
  55. package/dist/actions/runActions.js.map +1 -1
  56. package/dist/actions/task.js +47 -16
  57. package/dist/actions/task.js.map +1 -1
  58. package/dist/aggregation/aggregateResponses.d.ts +9 -0
  59. package/dist/aggregation/aggregateResponses.js +8 -14
  60. package/dist/aggregation/aggregateResponses.js.map +1 -1
  61. package/dist/aggregation/resolveResultsRequest.d.ts +41 -6
  62. package/dist/aggregation/resolveResultsRequest.js +97 -11
  63. package/dist/aggregation/resolveResultsRequest.js.map +1 -1
  64. package/dist/calc/computeCalcFields.d.ts +5 -3
  65. package/dist/calc/computeCalcFields.js +6 -4
  66. package/dist/calc/computeCalcFields.js.map +1 -1
  67. package/dist/calc/evaluate.d.ts +26 -3
  68. package/dist/calc/evaluate.js +34 -13
  69. package/dist/calc/evaluate.js.map +1 -1
  70. package/dist/calc/formatCalc.d.ts +11 -0
  71. package/dist/calc/formatCalc.js +35 -0
  72. package/dist/calc/formatCalc.js.map +1 -0
  73. package/dist/calc/formatCalcValue.d.ts +17 -0
  74. package/dist/calc/formatCalcValue.js +17 -0
  75. package/dist/calc/formatCalcValue.js.map +1 -0
  76. package/dist/calc/normalizeCalc.d.ts +17 -3
  77. package/dist/calc/normalizeCalc.js +42 -33
  78. package/dist/calc/normalizeCalc.js.map +1 -1
  79. package/dist/calc/registry.d.ts +43 -0
  80. package/dist/calc/registry.js +27 -0
  81. package/dist/calc/registry.js.map +1 -0
  82. package/dist/calc/resolveCalcContext.d.ts +36 -0
  83. package/dist/calc/resolveCalcContext.js +87 -0
  84. package/dist/calc/resolveCalcContext.js.map +1 -0
  85. package/dist/calc/types.d.ts +25 -4
  86. package/dist/calc/types.js +23 -0
  87. package/dist/calc/types.js.map +1 -0
  88. package/dist/client/ByteSizeField.d.ts +29 -0
  89. package/dist/client/ByteSizeField.js +58 -0
  90. package/dist/client/ByteSizeField.js.map +1 -0
  91. package/dist/client/CalcExpressionBuilder.d.ts +20 -0
  92. package/dist/client/CalcExpressionBuilder.js +890 -0
  93. package/dist/client/CalcExpressionBuilder.js.map +1 -0
  94. package/dist/client/ClosePollButton.d.ts +15 -0
  95. package/dist/client/ClosePollButton.js +80 -0
  96. package/dist/client/ClosePollButton.js.map +1 -0
  97. package/dist/client/CondensedArray.d.ts +15 -0
  98. package/dist/client/CondensedArray.js +97 -0
  99. package/dist/client/CondensedArray.js.map +1 -0
  100. package/dist/client/ConditionBuilder.js +2 -1
  101. package/dist/client/ConditionBuilder.js.map +1 -1
  102. package/dist/client/ConsentBlockLabel.d.ts +15 -0
  103. package/dist/client/ConsentBlockLabel.js +71 -0
  104. package/dist/client/ConsentBlockLabel.js.map +1 -0
  105. package/dist/client/ConsentSourceRowLabel.d.ts +9 -0
  106. package/dist/client/ConsentSourceRowLabel.js +21 -0
  107. package/dist/client/ConsentSourceRowLabel.js.map +1 -0
  108. package/dist/client/EndpointOptionsSelect.d.ts +55 -0
  109. package/dist/client/EndpointOptionsSelect.js +112 -0
  110. package/dist/client/EndpointOptionsSelect.js.map +1 -0
  111. package/dist/client/FieldBlockLabel.d.ts +18 -0
  112. package/dist/client/FieldBlockLabel.js +34 -0
  113. package/dist/client/FieldBlockLabel.js.map +1 -0
  114. package/dist/client/FieldNameSelect.d.ts +37 -0
  115. package/dist/client/FieldNameSelect.js +72 -0
  116. package/dist/client/FieldNameSelect.js.map +1 -0
  117. package/dist/client/FlowBuilder.d.ts +2 -2
  118. package/dist/client/FlowBuilder.js +137 -88
  119. package/dist/client/FlowBuilder.js.map +1 -1
  120. package/dist/client/FormConditionField.js +1 -4
  121. package/dist/client/FormConditionField.js.map +1 -1
  122. package/dist/client/RecipientsSelect.d.ts +37 -0
  123. package/dist/client/RecipientsSelect.js +197 -0
  124. package/dist/client/RecipientsSelect.js.map +1 -0
  125. package/dist/client/RuleDescription.d.ts +21 -0
  126. package/dist/client/RuleDescription.js +19 -0
  127. package/dist/client/RuleDescription.js.map +1 -0
  128. package/dist/client/admin.css +557 -0
  129. package/dist/client/endpointOptions.d.ts +11 -0
  130. package/dist/client/endpointOptions.js +38 -0
  131. package/dist/client/endpointOptions.js.map +1 -0
  132. package/dist/client/flowAuthoring.js +142 -0
  133. package/dist/client/flowAuthoring.js.map +1 -0
  134. package/dist/client/synthesizeClientField.js +21 -10
  135. package/dist/client/synthesizeClientField.js.map +1 -1
  136. package/dist/client/toStaticLabel.js +13 -0
  137. package/dist/client/toStaticLabel.js.map +1 -0
  138. package/dist/collections/buttonFields.d.ts +34 -0
  139. package/dist/collections/buttonFields.js +35 -0
  140. package/dist/collections/buttonFields.js.map +1 -0
  141. package/dist/collections/cells/FieldCountCell.d.ts +15 -0
  142. package/dist/collections/cells/FieldCountCell.js +18 -0
  143. package/dist/collections/cells/FieldCountCell.js.map +1 -0
  144. package/dist/collections/cells/FlowStepsCell.d.ts +14 -0
  145. package/dist/collections/cells/FlowStepsCell.js +17 -0
  146. package/dist/collections/cells/FlowStepsCell.js.map +1 -0
  147. package/dist/collections/formSubmissions.js +114 -28
  148. package/dist/collections/formSubmissions.js.map +1 -1
  149. package/dist/collections/forms.js +449 -112
  150. package/dist/collections/forms.js.map +1 -1
  151. package/dist/collections/formsEndpoints.js +106 -0
  152. package/dist/collections/formsEndpoints.js.map +1 -0
  153. package/dist/collections/redirectFields.d.ts +29 -0
  154. package/dist/collections/settingsFields.d.ts +31 -0
  155. package/dist/collections/settingsFields.js +45 -0
  156. package/dist/collections/settingsFields.js.map +1 -0
  157. package/dist/conditions/conditionType.js +9 -2
  158. package/dist/conditions/conditionType.js.map +1 -1
  159. package/dist/conditions/evaluate.js +1 -1
  160. package/dist/conditions/evaluate.js.map +1 -1
  161. package/dist/conditions/fieldTypes.d.ts +3 -1
  162. package/dist/conditions/fieldTypes.js +4 -1
  163. package/dist/conditions/fieldTypes.js.map +1 -1
  164. package/dist/conditions/normalizeConditions.js +20 -6
  165. package/dist/conditions/normalizeConditions.js.map +1 -1
  166. package/dist/consent/applyConsentStatements.d.ts +14 -0
  167. package/dist/consent/applyConsentStatements.js +17 -0
  168. package/dist/consent/applyConsentStatements.js.map +1 -0
  169. package/dist/consent/captureConsent.d.ts +39 -11
  170. package/dist/consent/captureConsent.js +63 -13
  171. package/dist/consent/captureConsent.js.map +1 -1
  172. package/dist/consent/consentSourcesField.d.ts +66 -0
  173. package/dist/consent/consentSourcesField.js +98 -0
  174. package/dist/consent/consentSourcesField.js.map +1 -0
  175. package/dist/consent/effectiveStatement.js +16 -0
  176. package/dist/consent/effectiveStatement.js.map +1 -0
  177. package/dist/consent/resolveConsentEntries.d.ts +24 -0
  178. package/dist/consent/resolveConsentEntries.js +40 -0
  179. package/dist/consent/resolveConsentEntries.js.map +1 -0
  180. package/dist/consent/resolveConsentSourcesRequest.d.ts +37 -0
  181. package/dist/consent/resolveConsentSourcesRequest.js +54 -0
  182. package/dist/consent/resolveConsentSourcesRequest.js.map +1 -0
  183. package/dist/consent/resolveConsentStatements.d.ts +45 -0
  184. package/dist/consent/resolveConsentStatements.js +46 -0
  185. package/dist/consent/resolveConsentStatements.js.map +1 -0
  186. package/dist/consent/resolvePublishedVersionRef.d.ts +17 -11
  187. package/dist/consent/resolvePublishedVersionRef.js +13 -10
  188. package/dist/consent/resolvePublishedVersionRef.js.map +1 -1
  189. package/dist/consent/types.d.ts +69 -0
  190. package/dist/context/formContext.d.ts +30 -0
  191. package/dist/context/formContext.js +54 -0
  192. package/dist/context/formContext.js.map +1 -0
  193. package/dist/email/departments.d.ts +59 -0
  194. package/dist/email/departments.js +87 -0
  195. package/dist/email/departments.js.map +1 -0
  196. package/dist/email/departmentsField.d.ts +53 -0
  197. package/dist/email/departmentsField.js +73 -0
  198. package/dist/email/departmentsField.js.map +1 -0
  199. package/dist/exports/client.d.ts +14 -1
  200. package/dist/exports/client.js +14 -1
  201. package/dist/exports/i18n.d.ts +5 -2
  202. package/dist/exports/i18n.js +5 -2
  203. package/dist/exports/react.d.ts +34 -10
  204. package/dist/exports/react.js +24 -4
  205. package/dist/exports/rsc.d.ts +10 -1
  206. package/dist/exports/rsc.js +10 -1
  207. package/dist/exports/types.d.ts +20 -12
  208. package/dist/fields/buildFieldBlocks.js +46 -25
  209. package/dist/fields/buildFieldBlocks.js.map +1 -1
  210. package/dist/fields/builtin/calculation.d.ts +17 -0
  211. package/dist/fields/builtin/calculation.js +193 -18
  212. package/dist/fields/builtin/calculation.js.map +1 -1
  213. package/dist/fields/builtin/checkbox.js +23 -0
  214. package/dist/fields/builtin/checkbox.js.map +1 -1
  215. package/dist/fields/builtin/consent.js +49 -14
  216. package/dist/fields/builtin/consent.js.map +1 -1
  217. package/dist/fields/builtin/country.d.ts +13 -0
  218. package/dist/fields/builtin/country.js +30 -0
  219. package/dist/fields/builtin/country.js.map +1 -0
  220. package/dist/fields/builtin/date.js +31 -0
  221. package/dist/fields/builtin/date.js.map +1 -0
  222. package/dist/fields/builtin/email.js +8 -1
  223. package/dist/fields/builtin/email.js.map +1 -1
  224. package/dist/fields/builtin/file.d.ts +15 -0
  225. package/dist/fields/builtin/file.js +95 -8
  226. package/dist/fields/builtin/file.js.map +1 -1
  227. package/dist/fields/builtin/index.d.ts +30 -0
  228. package/dist/fields/builtin/index.js +37 -10
  229. package/dist/fields/builtin/index.js.map +1 -1
  230. package/dist/fields/builtin/message.js +30 -0
  231. package/dist/fields/builtin/message.js.map +1 -0
  232. package/dist/fields/builtin/repeater.js +8 -3
  233. package/dist/fields/builtin/repeater.js.map +1 -1
  234. package/dist/fields/builtin/select.js +28 -4
  235. package/dist/fields/builtin/select.js.map +1 -1
  236. package/dist/fields/builtin/state.d.ts +13 -0
  237. package/dist/fields/builtin/state.js +30 -0
  238. package/dist/fields/builtin/state.js.map +1 -0
  239. package/dist/fields/builtin/text.js +7 -0
  240. package/dist/fields/builtin/text.js.map +1 -1
  241. package/dist/fields/data/regions.d.ts +21 -0
  242. package/dist/fields/data/regions.js +1238 -0
  243. package/dist/fields/data/regions.js.map +1 -0
  244. package/dist/fields/fieldKey.d.ts +15 -0
  245. package/dist/fields/fieldKey.js +16 -0
  246. package/dist/fields/fieldKey.js.map +1 -0
  247. package/dist/fields/fieldNamesOfType.js +30 -0
  248. package/dist/fields/fieldNamesOfType.js.map +1 -0
  249. package/dist/fields/instanceOptions.js +20 -0
  250. package/dist/fields/instanceOptions.js.map +1 -0
  251. package/dist/fields/localizedIf.d.ts +13 -0
  252. package/dist/fields/localizedIf.js +12 -0
  253. package/dist/fields/localizedIf.js.map +1 -0
  254. package/dist/fields/registry.d.ts +3 -1
  255. package/dist/fields/registry.js +23 -8
  256. package/dist/fields/registry.js.map +1 -1
  257. package/dist/fields/sharedConfig.js +125 -59
  258. package/dist/fields/sharedConfig.js.map +1 -1
  259. package/dist/fields/types.d.ts +78 -9
  260. package/dist/flow/engine.d.ts +6 -4
  261. package/dist/flow/engine.js +11 -6
  262. package/dist/flow/engine.js.map +1 -1
  263. package/dist/flow/normalizeFlow.js +28 -9
  264. package/dist/flow/normalizeFlow.js.map +1 -1
  265. package/dist/flow/stepLabel.js +15 -0
  266. package/dist/flow/stepLabel.js.map +1 -0
  267. package/dist/flow/types.d.ts +12 -3
  268. package/dist/flow/types.js +11 -0
  269. package/dist/flow/types.js.map +1 -0
  270. package/dist/form/pollState.d.ts +23 -0
  271. package/dist/form/pollState.js +17 -0
  272. package/dist/form/pollState.js.map +1 -0
  273. package/dist/form/toFormDocument.d.ts +110 -0
  274. package/dist/form/toFormDocument.js +89 -0
  275. package/dist/form/toFormDocument.js.map +1 -0
  276. package/dist/form/types.d.ts +73 -0
  277. package/dist/index.d.ts +67 -61
  278. package/dist/index.js +120 -25
  279. package/dist/index.js.map +1 -1
  280. package/dist/options.d.ts +281 -0
  281. package/dist/plugin/applyRegistryConfig.d.ts +11 -0
  282. package/dist/plugin/applyRegistryConfig.js +20 -0
  283. package/dist/plugin/applyRegistryConfig.js.map +1 -0
  284. package/dist/plugin/customState.js +26 -0
  285. package/dist/plugin/customState.js.map +1 -0
  286. package/dist/plugin/registerCollections.js +43 -40
  287. package/dist/plugin/registerCollections.js.map +1 -1
  288. package/dist/plugin/uploadsCollection.d.ts +16 -0
  289. package/dist/plugin/uploadsCollection.js +53 -0
  290. package/dist/plugin/uploadsCollection.js.map +1 -0
  291. package/dist/poll/applyPollOptions.js +18 -0
  292. package/dist/poll/applyPollOptions.js.map +1 -0
  293. package/dist/poll/buildPollOptionSourceFields.js +50 -0
  294. package/dist/poll/buildPollOptionSourceFields.js.map +1 -0
  295. package/dist/poll/closeJob.d.ts +53 -0
  296. package/dist/poll/closeJob.js +121 -0
  297. package/dist/poll/closeJob.js.map +1 -0
  298. package/dist/poll/definePollOptionSource.d.ts +44 -0
  299. package/dist/poll/definePollOptionSource.js +6 -0
  300. package/dist/poll/definePollOptionSource.js.map +1 -0
  301. package/dist/poll/definePollType.d.ts +34 -0
  302. package/dist/poll/definePollType.js +6 -0
  303. package/dist/poll/definePollType.js.map +1 -0
  304. package/dist/poll/effectivePollOptions.d.ts +38 -0
  305. package/dist/poll/effectivePollOptions.js +64 -0
  306. package/dist/poll/effectivePollOptions.js.map +1 -0
  307. package/dist/poll/mostVoted.d.ts +21 -0
  308. package/dist/poll/mostVoted.js +32 -0
  309. package/dist/poll/mostVoted.js.map +1 -0
  310. package/dist/poll/outcomeBeforeChange.js +91 -0
  311. package/dist/poll/outcomeBeforeChange.js.map +1 -0
  312. package/dist/poll/outcomeFields.d.ts +38 -0
  313. package/dist/poll/outcomeFields.js +52 -0
  314. package/dist/poll/outcomeFields.js.map +1 -0
  315. package/dist/poll/pollTypeRegistry.d.ts +34 -0
  316. package/dist/poll/pollTypeRegistry.js +69 -0
  317. package/dist/poll/pollTypeRegistry.js.map +1 -0
  318. package/dist/poll/registry.d.ts +17 -0
  319. package/dist/poll/registry.js +12 -0
  320. package/dist/poll/registry.js.map +1 -0
  321. package/dist/poll/resolvePollCloseRequest.js +106 -0
  322. package/dist/poll/resolvePollCloseRequest.js.map +1 -0
  323. package/dist/poll/resolvePollOptions.d.ts +29 -0
  324. package/dist/poll/resolvePollOptions.js +52 -0
  325. package/dist/poll/resolvePollOptions.js.map +1 -0
  326. package/dist/poll/resolvePollOptionsRequest.js +50 -0
  327. package/dist/poll/resolvePollOptionsRequest.js.map +1 -0
  328. package/dist/poll/resolvePollOutcome.d.ts +40 -0
  329. package/dist/poll/resolvePollOutcome.js +90 -0
  330. package/dist/poll/resolvePollOutcome.js.map +1 -0
  331. package/dist/poll/resultsField.js +21 -0
  332. package/dist/poll/resultsField.js.map +1 -0
  333. package/dist/poll/votes/aggregateFromVotes.d.ts +27 -0
  334. package/dist/poll/votes/aggregateFromVotes.js +54 -0
  335. package/dist/poll/votes/aggregateFromVotes.js.map +1 -0
  336. package/dist/poll/votes/answerValues.js +16 -0
  337. package/dist/poll/votes/answerValues.js.map +1 -0
  338. package/dist/poll/votes/bumpPollVote.js +88 -0
  339. package/dist/poll/votes/bumpPollVote.js.map +1 -0
  340. package/dist/poll/votes/recountPollVotes.d.ts +20 -0
  341. package/dist/poll/votes/recountPollVotes.js +63 -0
  342. package/dist/poll/votes/recountPollVotes.js.map +1 -0
  343. package/dist/poll/votes/voteTallyHook.js +76 -0
  344. package/dist/poll/votes/voteTallyHook.js.map +1 -0
  345. package/dist/poll/votes/votesCollection.d.ts +19 -0
  346. package/dist/poll/votes/votesCollection.js +89 -0
  347. package/dist/poll/votes/votesCollection.js.map +1 -0
  348. package/dist/prefill/valuesFromSearchParams.js +5 -9
  349. package/dist/prefill/valuesFromSearchParams.js.map +1 -1
  350. package/dist/presentations/types.d.ts +3 -2
  351. package/dist/react/Form.d.ts +77 -35
  352. package/dist/react/Form.js +368 -222
  353. package/dist/react/Form.js.map +1 -1
  354. package/dist/react/FormContext.d.ts +51 -1
  355. package/dist/react/FormContext.js.map +1 -1
  356. package/dist/react/FormControls.d.ts +55 -0
  357. package/dist/react/FormControls.js +56 -0
  358. package/dist/react/FormControls.js.map +1 -0
  359. package/dist/react/FormFields.d.ts +18 -0
  360. package/dist/react/FormFields.js +114 -0
  361. package/dist/react/FormFields.js.map +1 -0
  362. package/dist/react/FormLayout.d.ts +4 -2
  363. package/dist/react/FormLayout.js +3 -2
  364. package/dist/react/FormLayout.js.map +1 -1
  365. package/dist/react/FormResults.d.ts +8 -3
  366. package/dist/react/FormResults.js +50 -29
  367. package/dist/react/FormResults.js.map +1 -1
  368. package/dist/react/FormSteps.d.ts +17 -0
  369. package/dist/react/FormSteps.js +35 -0
  370. package/dist/react/FormSteps.js.map +1 -0
  371. package/dist/react/Poll.d.ts +40 -6
  372. package/dist/react/Poll.js +137 -33
  373. package/dist/react/Poll.js.map +1 -1
  374. package/dist/react/adapters.d.ts +35 -0
  375. package/dist/react/adapters.js +11 -0
  376. package/dist/react/adapters.js.map +1 -0
  377. package/dist/react/captcha/HcaptchaCaptcha.d.ts +31 -0
  378. package/dist/react/captcha/HcaptchaCaptcha.js +53 -0
  379. package/dist/react/captcha/HcaptchaCaptcha.js.map +1 -0
  380. package/dist/react/captcha/RecaptchaCaptcha.d.ts +44 -0
  381. package/dist/react/captcha/RecaptchaCaptcha.js +93 -0
  382. package/dist/react/captcha/RecaptchaCaptcha.js.map +1 -0
  383. package/dist/react/captcha/TurnstileCaptcha.d.ts +31 -0
  384. package/dist/react/captcha/TurnstileCaptcha.js +53 -0
  385. package/dist/react/captcha/TurnstileCaptcha.js.map +1 -0
  386. package/dist/react/captcha/types.d.ts +8 -0
  387. package/dist/react/captcha/useCaptchaScript.js +63 -0
  388. package/dist/react/captcha/useCaptchaScript.js.map +1 -0
  389. package/dist/react/captcha/vendors.js +8 -0
  390. package/dist/react/captcha/vendors.js.map +1 -0
  391. package/dist/react/contract.d.ts +2 -3
  392. package/dist/react/contract.js.map +1 -1
  393. package/dist/react/fetchResults.d.ts +1 -1
  394. package/dist/react/fetchResults.js +1 -1
  395. package/dist/react/fetchResults.js.map +1 -1
  396. package/dist/react/presentation/DialogSurface.js +1 -4
  397. package/dist/react/presentation/DialogSurface.js.map +1 -1
  398. package/dist/react/primitives/Checkbox.d.ts +6 -2
  399. package/dist/react/primitives/Checkbox.js +4 -2
  400. package/dist/react/primitives/Checkbox.js.map +1 -1
  401. package/dist/react/primitives/ChoiceGroup.d.ts +40 -0
  402. package/dist/react/primitives/ChoiceGroup.js +33 -0
  403. package/dist/react/primitives/ChoiceGroup.js.map +1 -0
  404. package/dist/react/primitives/FieldShell.d.ts +13 -5
  405. package/dist/react/primitives/FieldShell.js +35 -32
  406. package/dist/react/primitives/FieldShell.js.map +1 -1
  407. package/dist/react/primitives/Input.d.ts +4 -2
  408. package/dist/react/primitives/Input.js +2 -1
  409. package/dist/react/primitives/Input.js.map +1 -1
  410. package/dist/react/renderers/calculation.js +7 -5
  411. package/dist/react/renderers/calculation.js.map +1 -1
  412. package/dist/react/renderers/checkbox.js +3 -5
  413. package/dist/react/renderers/checkbox.js.map +1 -1
  414. package/dist/react/renderers/consent.js +84 -36
  415. package/dist/react/renderers/consent.js.map +1 -1
  416. package/dist/react/renderers/date.js +33 -0
  417. package/dist/react/renderers/date.js.map +1 -0
  418. package/dist/react/renderers/email.js +4 -5
  419. package/dist/react/renderers/email.js.map +1 -1
  420. package/dist/react/renderers/file.js +29 -14
  421. package/dist/react/renderers/file.js.map +1 -1
  422. package/dist/react/renderers/index.js +7 -0
  423. package/dist/react/renderers/index.js.map +1 -1
  424. package/dist/react/renderers/message.js +41 -0
  425. package/dist/react/renderers/message.js.map +1 -0
  426. package/dist/react/renderers/number.js +1 -4
  427. package/dist/react/renderers/number.js.map +1 -1
  428. package/dist/react/renderers/region.js +43 -0
  429. package/dist/react/renderers/region.js.map +1 -0
  430. package/dist/react/renderers/repeater.js +32 -14
  431. package/dist/react/renderers/repeater.js.map +1 -1
  432. package/dist/react/renderers/select.js +21 -6
  433. package/dist/react/renderers/select.js.map +1 -1
  434. package/dist/react/renderers/text.js +4 -5
  435. package/dist/react/renderers/text.js.map +1 -1
  436. package/dist/react/renderers/textarea.js +1 -4
  437. package/dist/react/renderers/textarea.js.map +1 -1
  438. package/dist/react/state.d.ts +46 -3
  439. package/dist/react/state.js +73 -10
  440. package/dist/react/state.js.map +1 -1
  441. package/dist/react/submitForm.js +1 -1
  442. package/dist/react/submitForm.js.map +1 -1
  443. package/dist/react/uploadFile.d.ts +2 -2
  444. package/dist/react/uploadFile.js +1 -1
  445. package/dist/react/uploadFile.js.map +1 -1
  446. package/dist/react/useCalcValues.d.ts +10 -0
  447. package/dist/react/useCalcValues.js +13 -0
  448. package/dist/react/useCalcValues.js.map +1 -0
  449. package/dist/react/useField.d.ts +0 -1
  450. package/dist/react/useField.js +7 -6
  451. package/dist/react/useField.js.map +1 -1
  452. package/dist/react/useFormState.d.ts +1 -1
  453. package/dist/react/useFormState.js +1 -1
  454. package/dist/react/useFormState.js.map +1 -1
  455. package/dist/react/validateField.js +3 -6
  456. package/dist/react/validateField.js.map +1 -1
  457. package/dist/recall/interpolate.d.ts +4 -3
  458. package/dist/recall/interpolate.js +5 -4
  459. package/dist/recall/interpolate.js.map +1 -1
  460. package/dist/recall/resolver.d.ts +1 -1
  461. package/dist/recall/resolver.js +15 -3
  462. package/dist/recall/resolver.js.map +1 -1
  463. package/dist/spam/captcha.d.ts +4 -3
  464. package/dist/spam/captcha.js +4 -3
  465. package/dist/spam/captcha.js.map +1 -1
  466. package/dist/spam/clientIp.js +9 -0
  467. package/dist/spam/clientIp.js.map +1 -0
  468. package/dist/spam/constants.js +13 -1
  469. package/dist/spam/constants.js.map +1 -1
  470. package/dist/spam/identify.js +3 -6
  471. package/dist/spam/identify.js.map +1 -1
  472. package/dist/spam/providers/hcaptcha.d.ts +17 -0
  473. package/dist/spam/providers/hcaptcha.js +18 -0
  474. package/dist/spam/providers/hcaptcha.js.map +1 -0
  475. package/dist/spam/providers/recaptcha.d.ts +23 -0
  476. package/dist/spam/providers/recaptcha.js +23 -0
  477. package/dist/spam/providers/recaptcha.js.map +1 -0
  478. package/dist/spam/providers/siteverify.js +34 -0
  479. package/dist/spam/providers/siteverify.js.map +1 -0
  480. package/dist/spam/providers/turnstile.d.ts +17 -0
  481. package/dist/spam/providers/turnstile.js +18 -0
  482. package/dist/spam/providers/turnstile.js.map +1 -0
  483. package/dist/spam/resolveSpam.js +2 -1
  484. package/dist/spam/resolveSpam.js.map +1 -1
  485. package/dist/spam/spamGuard.js +25 -11
  486. package/dist/spam/spamGuard.js.map +1 -1
  487. package/dist/spam/types.d.ts +19 -4
  488. package/dist/submissions/SubmissionAnswers.js +6 -2
  489. package/dist/submissions/SubmissionAnswers.js.map +1 -1
  490. package/dist/submissions/SubmissionAnswersClient.js +100 -64
  491. package/dist/submissions/SubmissionAnswersClient.js.map +1 -1
  492. package/dist/submissions/coerceBoolean.js +14 -0
  493. package/dist/submissions/coerceBoolean.js.map +1 -0
  494. package/dist/submissions/createSubmission.d.ts +36 -0
  495. package/dist/submissions/createSubmission.js +26 -0
  496. package/dist/submissions/createSubmission.js.map +1 -0
  497. package/dist/submissions/formIdOf.js +13 -0
  498. package/dist/submissions/formIdOf.js.map +1 -0
  499. package/dist/submissions/resolveVotedSubmission.d.ts +27 -0
  500. package/dist/submissions/resolveVotedSubmission.js +51 -0
  501. package/dist/submissions/resolveVotedSubmission.js.map +1 -0
  502. package/dist/submissions/runSubmission.d.ts +10 -0
  503. package/dist/submissions/runSubmission.js +104 -54
  504. package/dist/submissions/runSubmission.js.map +1 -1
  505. package/dist/submissions/types.d.ts +11 -4
  506. package/dist/submissions/validateSubmission.js +99 -13
  507. package/dist/submissions/validateSubmission.js.map +1 -1
  508. package/dist/submissions/verifyContext.js +35 -0
  509. package/dist/submissions/verifyContext.js.map +1 -0
  510. package/dist/submissions/voteChangeEndpoint.js +105 -0
  511. package/dist/submissions/voteChangeEndpoint.js.map +1 -0
  512. package/dist/submissions/votedCookie.d.ts +15 -0
  513. package/dist/submissions/votedCookie.js +74 -0
  514. package/dist/submissions/votedCookie.js.map +1 -0
  515. package/dist/translations/clientRuntimeKeys.d.ts +17 -0
  516. package/dist/translations/clientRuntimeKeys.js +48 -0
  517. package/dist/translations/clientRuntimeKeys.js.map +1 -0
  518. package/dist/translations/de.d.ts +12 -0
  519. package/dist/translations/de.js +380 -0
  520. package/dist/translations/de.js.map +1 -0
  521. package/dist/translations/en.js +250 -28
  522. package/dist/translations/en.js.map +1 -1
  523. package/dist/translations/index.d.ts +14 -5
  524. package/dist/translations/index.js +14 -3
  525. package/dist/translations/index.js.map +1 -1
  526. package/dist/translations/keys.d.ts +245 -23
  527. package/dist/translations/keys.js +246 -24
  528. package/dist/translations/keys.js.map +1 -1
  529. package/dist/translations/makeTranslate.d.ts +7 -3
  530. package/dist/translations/makeTranslate.js +12 -3
  531. package/dist/translations/makeTranslate.js.map +1 -1
  532. package/dist/translations/server.js +8 -1
  533. package/dist/translations/server.js.map +1 -1
  534. package/dist/uploads/captureFileRef.d.ts +10 -3
  535. package/dist/uploads/captureFileRef.js +18 -9
  536. package/dist/uploads/captureFileRef.js.map +1 -1
  537. package/dist/uploads/formatBytes.d.ts +9 -0
  538. package/dist/uploads/formatBytes.js +25 -0
  539. package/dist/uploads/formatBytes.js.map +1 -0
  540. package/dist/uploads/resolveFileRef.js +3 -2
  541. package/dist/uploads/resolveFileRef.js.map +1 -1
  542. package/dist/uploads/types.d.ts +1 -2
  543. package/dist/validation/buildRuleBlocks.js +18 -17
  544. package/dist/validation/buildRuleBlocks.js.map +1 -1
  545. package/dist/validation/builtin/email.js +2 -1
  546. package/dist/validation/builtin/email.js.map +1 -1
  547. package/dist/validation/builtin/index.d.ts +14 -0
  548. package/dist/validation/builtin/index.js +14 -1
  549. package/dist/validation/builtin/index.js.map +1 -1
  550. package/dist/validation/builtin/integer.js +15 -0
  551. package/dist/validation/builtin/integer.js.map +1 -0
  552. package/dist/validation/builtin/matchesField.js +3 -7
  553. package/dist/validation/builtin/matchesField.js.map +1 -1
  554. package/dist/validation/builtin/max.js +1 -0
  555. package/dist/validation/builtin/max.js.map +1 -1
  556. package/dist/validation/builtin/maxDate.js +24 -0
  557. package/dist/validation/builtin/maxDate.js.map +1 -0
  558. package/dist/validation/builtin/maxLength.js +1 -0
  559. package/dist/validation/builtin/maxLength.js.map +1 -1
  560. package/dist/validation/builtin/min.js +1 -0
  561. package/dist/validation/builtin/min.js.map +1 -1
  562. package/dist/validation/builtin/minDate.js +24 -0
  563. package/dist/validation/builtin/minDate.js.map +1 -0
  564. package/dist/validation/builtin/minLength.js +1 -0
  565. package/dist/validation/builtin/minLength.js.map +1 -1
  566. package/dist/validation/builtin/notAlreadySubmitted.js +5 -1
  567. package/dist/validation/builtin/notAlreadySubmitted.js.map +1 -1
  568. package/dist/validation/builtin/oneOf.js +1 -0
  569. package/dist/validation/builtin/oneOf.js.map +1 -1
  570. package/dist/validation/builtin/pattern.js +11 -2
  571. package/dist/validation/builtin/pattern.js.map +1 -1
  572. package/dist/validation/builtin/url.js +1 -0
  573. package/dist/validation/builtin/url.js.map +1 -1
  574. package/dist/validation/builtin/validateDateBound.js +18 -0
  575. package/dist/validation/builtin/validateDateBound.js.map +1 -0
  576. package/dist/validation/builtin/validateRegexParams.js +44 -0
  577. package/dist/validation/builtin/validateRegexParams.js.map +1 -0
  578. package/dist/validation/emailPattern.js +10 -0
  579. package/dist/validation/emailPattern.js.map +1 -0
  580. package/dist/validation/fieldTargetParam.d.ts +22 -0
  581. package/dist/validation/fieldTargetParam.js +38 -0
  582. package/dist/validation/fieldTargetParam.js.map +1 -0
  583. package/dist/validation/registry.d.ts +2 -1
  584. package/dist/validation/registry.js +2 -9
  585. package/dist/validation/registry.js.map +1 -1
  586. package/dist/validation/runValidation.js +8 -29
  587. package/dist/validation/runValidation.js.map +1 -1
  588. package/dist/validation/types.d.ts +5 -11
  589. package/dist/validation/validateUrl.js +22 -0
  590. package/dist/validation/validateUrl.js.map +1 -0
  591. package/package.json +14 -6
  592. package/styles/form-builder.css +94 -190
  593. package/dist/collections/uploads.d.ts +0 -30
  594. package/dist/collections/uploads.js +0 -55
  595. package/dist/collections/uploads.js.map +0 -1
  596. package/dist/consent/buildConsentSourceConfig.js +0 -49
  597. package/dist/consent/buildConsentSourceConfig.js.map +0 -1
  598. package/dist/consent/builtin/index.d.ts +0 -7
  599. package/dist/consent/builtin/index.js +0 -11
  600. package/dist/consent/builtin/index.js.map +0 -1
  601. package/dist/consent/builtin/pageReference.js +0 -75
  602. package/dist/consent/builtin/pageReference.js.map +0 -1
  603. package/dist/consent/builtin/static.js +0 -44
  604. package/dist/consent/builtin/static.js.map +0 -1
  605. package/dist/consent/defineConsentSource.d.ts +0 -35
  606. package/dist/consent/defineConsentSource.js +0 -6
  607. package/dist/consent/defineConsentSource.js.map +0 -1
  608. package/dist/consent/registry.d.ts +0 -16
  609. package/dist/consent/registry.js +0 -16
  610. package/dist/consent/registry.js.map +0 -1
  611. package/dist/consent/resolveConsentLinks.d.ts +0 -19
  612. package/dist/consent/resolveConsentLinks.js +0 -26
  613. package/dist/consent/resolveConsentLinks.js.map +0 -1
  614. package/dist/presentations/registry.d.ts +0 -15
  615. package/dist/presentations/registry.js +0 -15
  616. package/dist/presentations/registry.js.map +0 -1
  617. package/dist/react/toFormDocument.d.ts +0 -24
  618. package/dist/react/toFormDocument.js +0 -22
  619. package/dist/react/toFormDocument.js.map +0 -1
@@ -0,0 +1,50 @@
1
+ import { keys } from "../translations/keys.js";
2
+ import { labelForKey, resolveDefinitionLabel } from "../translations/server.js";
3
+ //#region src/poll/buildPollOptionSourceFields.ts
4
+ /**
5
+ * Builds the `optionSource` select and `sourceConfig` group for the forms poll group; returns
6
+ * nothing when no sources are registered, so the fields only exist for hosts that opted in.
7
+ * Fields inside `sourceConfig` are annotated with `admin.condition` so only the fields of the
8
+ * currently selected source type are visible; like consent's `sourceConfig`, field names within
9
+ * the group must be unique across all registered sources. Unlike consent's `source` select there
10
+ * is no default: hand-authored options remain the baseline and a source is an explicit opt-in per
11
+ * form (the select stays clearable).
12
+ */
13
+ const buildPollOptionSourceFields = (registry) => {
14
+ const sources = [...registry.values()];
15
+ if (sources.length === 0) return [];
16
+ const sourceConfigFields = sources.flatMap((source) => (source.config ?? []).map((field) => {
17
+ const existingAdmin = typeof field.admin === "object" && field.admin !== null ? field.admin : {};
18
+ return {
19
+ ...field,
20
+ admin: {
21
+ ...existingAdmin,
22
+ condition: (data) => data?.poll?.optionSource === source.type
23
+ }
24
+ };
25
+ }));
26
+ return [{
27
+ name: "optionSource",
28
+ type: "select",
29
+ label: labelForKey(keys.pollOptionSource),
30
+ admin: { description: labelForKey(keys.pollOptionSourceDescription) },
31
+ options: sources.map((source) => ({
32
+ label: resolveDefinitionLabel(source.label),
33
+ value: source.type
34
+ }))
35
+ }, {
36
+ name: "sourceConfig",
37
+ type: "group",
38
+ label: labelForKey(keys.pollSourceConfig),
39
+ admin: { condition: (_data, siblingData) => Boolean(siblingData?.optionSource) },
40
+ fields: sourceConfigFields.length > 0 ? sourceConfigFields : [{
41
+ name: "_placeholder",
42
+ type: "text",
43
+ admin: { hidden: true }
44
+ }]
45
+ }];
46
+ };
47
+ //#endregion
48
+ export { buildPollOptionSourceFields };
49
+
50
+ //# sourceMappingURL=buildPollOptionSourceFields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildPollOptionSourceFields.js","names":[],"sources":["../../src/poll/buildPollOptionSourceFields.ts"],"sourcesContent":["import type { Field } from 'payload'\nimport { keys } from '../translations/keys'\nimport { labelForKey, resolveDefinitionLabel } from '../translations/server'\nimport type { PollOptionSourceRegistry } from './registry'\n\n/**\n * Builds the `optionSource` select and `sourceConfig` group for the forms poll group; returns\n * nothing when no sources are registered, so the fields only exist for hosts that opted in.\n * Fields inside `sourceConfig` are annotated with `admin.condition` so only the fields of the\n * currently selected source type are visible; like consent's `sourceConfig`, field names within\n * the group must be unique across all registered sources. Unlike consent's `source` select there\n * is no default: hand-authored options remain the baseline and a source is an explicit opt-in per\n * form (the select stays clearable).\n */\nexport const buildPollOptionSourceFields = (registry: PollOptionSourceRegistry): Field[] => {\n\tconst sources = [...registry.values()]\n\tif (sources.length === 0) {\n\t\treturn []\n\t}\n\n\tconst sourceConfigFields: Field[] = sources.flatMap((source) =>\n\t\t(source.config ?? []).map((field) => {\n\t\t\tconst existingAdmin =\n\t\t\t\ttypeof field.admin === 'object' && field.admin !== null ? field.admin : {}\n\t\t\treturn {\n\t\t\t\t...field,\n\t\t\t\tadmin: {\n\t\t\t\t\t...existingAdmin,\n\t\t\t\t\tcondition: (data: unknown) =>\n\t\t\t\t\t\t(data as { poll?: { optionSource?: string } })?.poll?.optionSource === source.type,\n\t\t\t\t},\n\t\t\t} as Field\n\t\t})\n\t)\n\n\treturn [\n\t\t{\n\t\t\tname: 'optionSource',\n\t\t\ttype: 'select',\n\t\t\tlabel: labelForKey(keys.pollOptionSource),\n\t\t\tadmin: {\n\t\t\t\tdescription: labelForKey(keys.pollOptionSourceDescription),\n\t\t\t},\n\t\t\toptions: sources.map((source) => ({\n\t\t\t\tlabel: resolveDefinitionLabel(source.label),\n\t\t\t\tvalue: source.type,\n\t\t\t})),\n\t\t},\n\t\t{\n\t\t\tname: 'sourceConfig',\n\t\t\ttype: 'group',\n\t\t\tlabel: labelForKey(keys.pollSourceConfig),\n\t\t\tadmin: {\n\t\t\t\tcondition: (_data, siblingData) => Boolean(siblingData?.optionSource),\n\t\t\t},\n\t\t\tfields:\n\t\t\t\tsourceConfigFields.length > 0\n\t\t\t\t\t? sourceConfigFields\n\t\t\t\t\t: [{ name: '_placeholder', type: 'text', admin: { hidden: true } }],\n\t\t},\n\t]\n}\n"],"mappings":";;;;;;;;;;;;AAcA,MAAa,+BAA+B,aAAgD;CAC3F,MAAM,UAAU,CAAC,GAAG,SAAS,OAAO,CAAC;CACrC,IAAI,QAAQ,WAAW,GACtB,OAAO,CAAC;CAGT,MAAM,qBAA8B,QAAQ,SAAS,YACnD,OAAO,UAAU,CAAC,GAAG,KAAK,UAAU;EACpC,MAAM,gBACL,OAAO,MAAM,UAAU,YAAY,MAAM,UAAU,OAAO,MAAM,QAAQ,CAAC;EAC1E,OAAO;GACN,GAAG;GACH,OAAO;IACN,GAAG;IACH,YAAY,SACV,MAA+C,MAAM,iBAAiB,OAAO;GAChF;EACD;CACD,CAAC,CACF;CAEA,OAAO,CACN;EACC,MAAM;EACN,MAAM;EACN,OAAO,YAAY,KAAK,gBAAgB;EACxC,OAAO,EACN,aAAa,YAAY,KAAK,2BAA2B,EAC1D;EACA,SAAS,QAAQ,KAAK,YAAY;GACjC,OAAO,uBAAuB,OAAO,KAAK;GAC1C,OAAO,OAAO;EACf,EAAE;CACH,GACA;EACC,MAAM;EACN,MAAM;EACN,OAAO,YAAY,KAAK,gBAAgB;EACxC,OAAO,EACN,YAAY,OAAO,gBAAgB,QAAQ,aAAa,YAAY,EACrE;EACA,QACC,mBAAmB,SAAS,IACzB,qBACA,CAAC;GAAE,MAAM;GAAgB,MAAM;GAAQ,OAAO,EAAE,QAAQ,KAAK;EAAE,CAAC;CACrE,CACD;AACD"}
@@ -0,0 +1,53 @@
1
+ import { Config, Payload, PayloadRequest, TaskConfig } from "payload";
2
+
3
+ //#region src/poll/closeJob.d.ts
4
+ declare const POLL_CLOSE_TASK_SLUG = "form-builder-poll-close";
5
+ /** Input the enqueue path stores; the handler re-loads everything else from the DB. */
6
+ type PollCloseTaskInput = {
7
+ formId: number | string;
8
+ };
9
+ /** The slice of a loaded form the close gate reads. */
10
+ type PollFormLike = {
11
+ pollEnabled?: boolean | null;
12
+ poll?: unknown;
13
+ };
14
+ /**
15
+ * True when a poll's outcome should auto-resolve now: enabled, closed, still unresolved, and its
16
+ * strategy is not `manual`. Shared by the close-job handler and the results-read fallback so the two
17
+ * triggers apply the exact same gate.
18
+ */
19
+ declare const shouldAutoResolvePoll: (form: PollFormLike) => boolean;
20
+ /**
21
+ * Run the scheduled close for one form. Idempotent: re-loads the form and only resolves when the
22
+ * poll is enabled, closed, still unresolved, and not `manual`, so a re-run after the outcome was
23
+ * recorded (or a job that fires slightly early) is a safe no-op. Tolerates a deleted form.
24
+ */
25
+ declare const runPollClose: (args: {
26
+ payload: Payload;
27
+ input: PollCloseTaskInput; /** Whether the hidden tally store backs the outcome aggregation (see `resolvePollOutcome`). */
28
+ pollVotesEnabled?: boolean;
29
+ req?: PayloadRequest;
30
+ }) => Promise<void>;
31
+ /** Native Payload jobs task that resolves a poll's outcome once it closes. */
32
+ declare const buildPollCloseTask: (pollVotesEnabled?: boolean) => TaskConfig;
33
+ /** Register the poll-close task on `config.jobs.tasks`, creating the jobs config if absent. */
34
+ declare const registerPollCloseTask: (config: Config, pollVotesEnabled?: boolean) => void;
35
+ /**
36
+ * Schedule (or reschedule) the auto-close for a poll after a save. Enqueues a `waitUntil: closesAt`
37
+ * job when the poll is enabled, has a `closesAt`, is still unresolved, and its strategy is not
38
+ * `manual`, and only when a job runner is present (`payload.jobs.queue`); otherwise the resolve-on-read
39
+ * fallback is the safety net. Any prior pending close job for the form is deleted first so the newest
40
+ * `closesAt` wins. Never throws; a queue failure is logged and swallowed.
41
+ */
42
+ declare const enqueuePollClose: (args: {
43
+ payload: Payload;
44
+ form: {
45
+ id: number | string;
46
+ pollEnabled?: boolean | null;
47
+ poll?: unknown;
48
+ };
49
+ req?: PayloadRequest;
50
+ }) => Promise<void>;
51
+ //#endregion
52
+ export { POLL_CLOSE_TASK_SLUG, PollCloseTaskInput, buildPollCloseTask, enqueuePollClose, registerPollCloseTask, runPollClose, shouldAutoResolvePoll };
53
+ //# sourceMappingURL=closeJob.d.ts.map
@@ -0,0 +1,121 @@
1
+ import { isPollClosed, pollConfigOf } from "../form/pollState.js";
2
+ import { resolvePollOutcome } from "./resolvePollOutcome.js";
3
+ import { FORMS_SLUG } from "../collections/forms.js";
4
+ //#region src/poll/closeJob.ts
5
+ const POLL_CLOSE_TASK_SLUG = "form-builder-poll-close";
6
+ /** Payload's jobs collection; queried directly to supersede a superseded pending close job. */
7
+ const JOBS_SLUG = "payload-jobs";
8
+ const hasEmptyWinners = (poll) => {
9
+ const winners = poll.outcome?.winningValues;
10
+ return !Array.isArray(winners) || winners.filter((value) => typeof value === "string" && value.length > 0).length === 0;
11
+ };
12
+ const strategyOf = (poll) => typeof poll.type === "string" && poll.type.length > 0 ? poll.type : "manual";
13
+ /**
14
+ * True when a poll's outcome should auto-resolve now: enabled, closed, still unresolved, and its
15
+ * strategy is not `manual`. Shared by the close-job handler and the results-read fallback so the two
16
+ * triggers apply the exact same gate.
17
+ */
18
+ const shouldAutoResolvePoll = (form) => {
19
+ const poll = pollConfigOf(form.poll);
20
+ return form.pollEnabled === true && poll != null && isPollClosed(poll) && hasEmptyWinners(poll) && strategyOf(poll) !== "manual";
21
+ };
22
+ /**
23
+ * Run the scheduled close for one form. Idempotent: re-loads the form and only resolves when the
24
+ * poll is enabled, closed, still unresolved, and not `manual`, so a re-run after the outcome was
25
+ * recorded (or a job that fires slightly early) is a safe no-op. Tolerates a deleted form.
26
+ */
27
+ const runPollClose = async (args) => {
28
+ const { payload, input, pollVotesEnabled, req } = args;
29
+ const form = await payload.findByID({
30
+ collection: FORMS_SLUG,
31
+ id: input.formId,
32
+ depth: 0,
33
+ overrideAccess: true,
34
+ req
35
+ }).catch(() => null);
36
+ if (!form || !shouldAutoResolvePoll(form)) return;
37
+ await resolvePollOutcome({
38
+ payload,
39
+ formId: input.formId,
40
+ pollVotesEnabled,
41
+ req
42
+ });
43
+ };
44
+ /** Native Payload jobs task that resolves a poll's outcome once it closes. */
45
+ const buildPollCloseTask = (pollVotesEnabled = false) => ({
46
+ slug: POLL_CLOSE_TASK_SLUG,
47
+ inputSchema: [{
48
+ name: "formId",
49
+ type: "text",
50
+ required: true
51
+ }],
52
+ handler: async ({ input, req }) => {
53
+ await runPollClose({
54
+ payload: req.payload,
55
+ input,
56
+ pollVotesEnabled,
57
+ req
58
+ });
59
+ return { output: {} };
60
+ }
61
+ });
62
+ /** Register the poll-close task on `config.jobs.tasks`, creating the jobs config if absent. */
63
+ const registerPollCloseTask = (config, pollVotesEnabled = false) => {
64
+ config.jobs ??= {};
65
+ config.jobs.tasks ??= [];
66
+ config.jobs.tasks.push(buildPollCloseTask(pollVotesEnabled));
67
+ };
68
+ const canQueue = (payload) => typeof payload.jobs?.queue === "function";
69
+ /**
70
+ * Delete this form's pending (never-run) close jobs so a re-enqueue supersedes the prior one. Payload
71
+ * 3.85's `jobs.queue` has no concurrency key, so this mirrors core's `deleteScheduledPublishJobs`:
72
+ * a direct `db.deleteMany` on the jobs collection, scoped by task slug and the JSON `input.formId`.
73
+ * Best-effort; a failure here only risks a duplicate future job, which the idempotent handler absorbs.
74
+ */
75
+ const deletePendingCloseJobs = async (payload, formId, req) => {
76
+ try {
77
+ await payload.db.deleteMany({
78
+ collection: JOBS_SLUG,
79
+ req,
80
+ where: { and: [
81
+ { completedAt: { exists: false } },
82
+ { processing: { equals: false } },
83
+ { taskSlug: { equals: POLL_CLOSE_TASK_SLUG } },
84
+ { "input.formId": { equals: String(formId) } }
85
+ ] }
86
+ });
87
+ } catch (error) {
88
+ payload.logger?.error(`@10x-media/form-builder: failed to clear pending poll-close jobs for form ${String(formId)}: ${error instanceof Error ? error.message : String(error)}`);
89
+ }
90
+ };
91
+ /**
92
+ * Schedule (or reschedule) the auto-close for a poll after a save. Enqueues a `waitUntil: closesAt`
93
+ * job when the poll is enabled, has a `closesAt`, is still unresolved, and its strategy is not
94
+ * `manual`, and only when a job runner is present (`payload.jobs.queue`); otherwise the resolve-on-read
95
+ * fallback is the safety net. Any prior pending close job for the form is deleted first so the newest
96
+ * `closesAt` wins. Never throws; a queue failure is logged and swallowed.
97
+ */
98
+ const enqueuePollClose = async (args) => {
99
+ const { payload, form, req } = args;
100
+ const poll = pollConfigOf(form.poll);
101
+ if (form.pollEnabled !== true || !poll) return;
102
+ const closesAt = typeof poll.closesAt === "string" && poll.closesAt.length > 0 ? poll.closesAt : void 0;
103
+ if (!closesAt || !hasEmptyWinners(poll) || strategyOf(poll) === "manual" || !canQueue(payload)) return;
104
+ const waitUntil = new Date(closesAt);
105
+ if (Number.isNaN(waitUntil.getTime())) return;
106
+ await deletePendingCloseJobs(payload, form.id, req);
107
+ try {
108
+ await payload.jobs.queue({
109
+ task: POLL_CLOSE_TASK_SLUG,
110
+ input: { formId: String(form.id) },
111
+ waitUntil,
112
+ req
113
+ });
114
+ } catch (error) {
115
+ payload.logger?.error(`@10x-media/form-builder: failed to enqueue poll-close for form ${String(form.id)}: ${error instanceof Error ? error.message : String(error)}`);
116
+ }
117
+ };
118
+ //#endregion
119
+ export { POLL_CLOSE_TASK_SLUG, buildPollCloseTask, enqueuePollClose, registerPollCloseTask, runPollClose, shouldAutoResolvePoll };
120
+
121
+ //# sourceMappingURL=closeJob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"closeJob.js","names":[],"sources":["../../src/poll/closeJob.ts"],"sourcesContent":["import type { Config, Payload, PayloadRequest, TaskConfig } from 'payload'\nimport { FORMS_SLUG } from '../collections/forms'\nimport { isPollClosed, type PollConfigLike, pollConfigOf } from '../form/pollState'\nimport { resolvePollOutcome } from './resolvePollOutcome'\n\nexport const POLL_CLOSE_TASK_SLUG = 'form-builder-poll-close'\n\n/** Payload's jobs collection; queried directly to supersede a superseded pending close job. */\nconst JOBS_SLUG = 'payload-jobs'\n\n/** Input the enqueue path stores; the handler re-loads everything else from the DB. */\nexport type PollCloseTaskInput = { formId: number | string }\n\n/** The slice of a loaded form the close gate reads. */\nexport type PollFormLike = { pollEnabled?: boolean | null; poll?: unknown }\n\nconst hasEmptyWinners = (poll: PollConfigLike): boolean => {\n\tconst winners = poll.outcome?.winningValues\n\treturn (\n\t\t!Array.isArray(winners) ||\n\t\twinners.filter((value) => typeof value === 'string' && value.length > 0).length === 0\n\t)\n}\n\nconst strategyOf = (poll: PollConfigLike): string =>\n\ttypeof poll.type === 'string' && poll.type.length > 0 ? poll.type : 'manual'\n\n/**\n * True when a poll's outcome should auto-resolve now: enabled, closed, still unresolved, and its\n * strategy is not `manual`. Shared by the close-job handler and the results-read fallback so the two\n * triggers apply the exact same gate.\n */\nexport const shouldAutoResolvePoll = (form: PollFormLike): boolean => {\n\tconst poll = pollConfigOf(form.poll)\n\treturn (\n\t\tform.pollEnabled === true &&\n\t\tpoll != null &&\n\t\tisPollClosed(poll) &&\n\t\thasEmptyWinners(poll) &&\n\t\tstrategyOf(poll) !== 'manual'\n\t)\n}\n\n/**\n * Run the scheduled close for one form. Idempotent: re-loads the form and only resolves when the\n * poll is enabled, closed, still unresolved, and not `manual`, so a re-run after the outcome was\n * recorded (or a job that fires slightly early) is a safe no-op. Tolerates a deleted form.\n */\nexport const runPollClose = async (args: {\n\tpayload: Payload\n\tinput: PollCloseTaskInput\n\t/** Whether the hidden tally store backs the outcome aggregation (see `resolvePollOutcome`). */\n\tpollVotesEnabled?: boolean\n\treq?: PayloadRequest\n}): Promise<void> => {\n\tconst { payload, input, pollVotesEnabled, req } = args\n\tconst form = await payload\n\t\t.findByID({ collection: FORMS_SLUG, id: input.formId, depth: 0, overrideAccess: true, req })\n\t\t.catch(() => null)\n\tif (!form || !shouldAutoResolvePoll(form as PollFormLike)) {\n\t\treturn\n\t}\n\tawait resolvePollOutcome({ payload, formId: input.formId, pollVotesEnabled, req })\n}\n\n/** Native Payload jobs task that resolves a poll's outcome once it closes. */\nexport const buildPollCloseTask = (pollVotesEnabled = false): TaskConfig =>\n\t({\n\t\tslug: POLL_CLOSE_TASK_SLUG,\n\t\tinputSchema: [{ name: 'formId', type: 'text', required: true }],\n\t\thandler: async ({ input, req }) => {\n\t\t\tawait runPollClose({\n\t\t\t\tpayload: req.payload,\n\t\t\t\tinput: input as PollCloseTaskInput,\n\t\t\t\tpollVotesEnabled,\n\t\t\t\treq,\n\t\t\t})\n\t\t\treturn { output: {} }\n\t\t},\n\t}) as TaskConfig\n\n/** Register the poll-close task on `config.jobs.tasks`, creating the jobs config if absent. */\nexport const registerPollCloseTask = (config: Config, pollVotesEnabled = false): void => {\n\tconfig.jobs ??= {}\n\tconfig.jobs.tasks ??= []\n\tconfig.jobs.tasks.push(buildPollCloseTask(pollVotesEnabled))\n}\n\nconst canQueue = (payload: Payload): boolean => typeof payload.jobs?.queue === 'function'\n\n/**\n * Delete this form's pending (never-run) close jobs so a re-enqueue supersedes the prior one. Payload\n * 3.85's `jobs.queue` has no concurrency key, so this mirrors core's `deleteScheduledPublishJobs`:\n * a direct `db.deleteMany` on the jobs collection, scoped by task slug and the JSON `input.formId`.\n * Best-effort; a failure here only risks a duplicate future job, which the idempotent handler absorbs.\n */\nconst deletePendingCloseJobs = async (\n\tpayload: Payload,\n\tformId: number | string,\n\treq?: PayloadRequest\n): Promise<void> => {\n\ttry {\n\t\tawait payload.db.deleteMany({\n\t\t\tcollection: JOBS_SLUG,\n\t\t\treq,\n\t\t\twhere: {\n\t\t\t\tand: [\n\t\t\t\t\t{ completedAt: { exists: false } },\n\t\t\t\t\t{ processing: { equals: false } },\n\t\t\t\t\t{ taskSlug: { equals: POLL_CLOSE_TASK_SLUG } },\n\t\t\t\t\t{ 'input.formId': { equals: String(formId) } },\n\t\t\t\t],\n\t\t\t},\n\t\t})\n\t} catch (error) {\n\t\tpayload.logger?.error(\n\t\t\t`@10x-media/form-builder: failed to clear pending poll-close jobs for form ${String(formId)}: ${\n\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t}`\n\t\t)\n\t}\n}\n\n/**\n * Schedule (or reschedule) the auto-close for a poll after a save. Enqueues a `waitUntil: closesAt`\n * job when the poll is enabled, has a `closesAt`, is still unresolved, and its strategy is not\n * `manual`, and only when a job runner is present (`payload.jobs.queue`); otherwise the resolve-on-read\n * fallback is the safety net. Any prior pending close job for the form is deleted first so the newest\n * `closesAt` wins. Never throws; a queue failure is logged and swallowed.\n */\nexport const enqueuePollClose = async (args: {\n\tpayload: Payload\n\tform: { id: number | string; pollEnabled?: boolean | null; poll?: unknown }\n\treq?: PayloadRequest\n}): Promise<void> => {\n\tconst { payload, form, req } = args\n\tconst poll = pollConfigOf(form.poll)\n\tif (form.pollEnabled !== true || !poll) {\n\t\treturn\n\t}\n\tconst closesAt =\n\t\ttypeof poll.closesAt === 'string' && poll.closesAt.length > 0 ? poll.closesAt : undefined\n\tif (!closesAt || !hasEmptyWinners(poll) || strategyOf(poll) === 'manual' || !canQueue(payload)) {\n\t\treturn\n\t}\n\tconst waitUntil = new Date(closesAt)\n\tif (Number.isNaN(waitUntil.getTime())) {\n\t\treturn\n\t}\n\tawait deletePendingCloseJobs(payload, form.id, req)\n\ttry {\n\t\tawait payload.jobs.queue({\n\t\t\ttask: POLL_CLOSE_TASK_SLUG,\n\t\t\tinput: { formId: String(form.id) },\n\t\t\twaitUntil,\n\t\t\treq,\n\t\t})\n\t} catch (error) {\n\t\tpayload.logger?.error(\n\t\t\t`@10x-media/form-builder: failed to enqueue poll-close for form ${String(form.id)}: ${\n\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t}`\n\t\t)\n\t}\n}\n"],"mappings":";;;;AAKA,MAAa,uBAAuB;;AAGpC,MAAM,YAAY;AAQlB,MAAM,mBAAmB,SAAkC;CAC1D,MAAM,UAAU,KAAK,SAAS;CAC9B,OACC,CAAC,MAAM,QAAQ,OAAO,KACtB,QAAQ,QAAQ,UAAU,OAAO,UAAU,YAAY,MAAM,SAAS,CAAC,EAAE,WAAW;AAEtF;AAEA,MAAM,cAAc,SACnB,OAAO,KAAK,SAAS,YAAY,KAAK,KAAK,SAAS,IAAI,KAAK,OAAO;;;;;;AAOrE,MAAa,yBAAyB,SAAgC;CACrE,MAAM,OAAO,aAAa,KAAK,IAAI;CACnC,OACC,KAAK,gBAAgB,QACrB,QAAQ,QACR,aAAa,IAAI,KACjB,gBAAgB,IAAI,KACpB,WAAW,IAAI,MAAM;AAEvB;;;;;;AAOA,MAAa,eAAe,OAAO,SAMd;CACpB,MAAM,EAAE,SAAS,OAAO,kBAAkB,QAAQ;CAClD,MAAM,OAAO,MAAM,QACjB,SAAS;EAAE,YAAY;EAAY,IAAI,MAAM;EAAQ,OAAO;EAAG,gBAAgB;EAAM;CAAI,CAAC,EAC1F,YAAY,IAAI;CAClB,IAAI,CAAC,QAAQ,CAAC,sBAAsB,IAAoB,GACvD;CAED,MAAM,mBAAmB;EAAE;EAAS,QAAQ,MAAM;EAAQ;EAAkB;CAAI,CAAC;AAClF;;AAGA,MAAa,sBAAsB,mBAAmB,WACpD;CACA,MAAM;CACN,aAAa,CAAC;EAAE,MAAM;EAAU,MAAM;EAAQ,UAAU;CAAK,CAAC;CAC9D,SAAS,OAAO,EAAE,OAAO,UAAU;EAClC,MAAM,aAAa;GAClB,SAAS,IAAI;GACN;GACP;GACA;EACD,CAAC;EACD,OAAO,EAAE,QAAQ,CAAC,EAAE;CACrB;AACD;;AAGD,MAAa,yBAAyB,QAAgB,mBAAmB,UAAgB;CACxF,OAAO,SAAS,CAAC;CACjB,OAAO,KAAK,UAAU,CAAC;CACvB,OAAO,KAAK,MAAM,KAAK,mBAAmB,gBAAgB,CAAC;AAC5D;AAEA,MAAM,YAAY,YAA8B,OAAO,QAAQ,MAAM,UAAU;;;;;;;AAQ/E,MAAM,yBAAyB,OAC9B,SACA,QACA,QACmB;CACnB,IAAI;EACH,MAAM,QAAQ,GAAG,WAAW;GAC3B,YAAY;GACZ;GACA,OAAO,EACN,KAAK;IACJ,EAAE,aAAa,EAAE,QAAQ,MAAM,EAAE;IACjC,EAAE,YAAY,EAAE,QAAQ,MAAM,EAAE;IAChC,EAAE,UAAU,EAAE,QAAQ,qBAAqB,EAAE;IAC7C,EAAE,gBAAgB,EAAE,QAAQ,OAAO,MAAM,EAAE,EAAE;GAC9C,EACD;EACD,CAAC;CACF,SAAS,OAAO;EACf,QAAQ,QAAQ,MACf,6EAA6E,OAAO,MAAM,EAAE,IAC3F,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAEvD;CACD;AACD;;;;;;;;AASA,MAAa,mBAAmB,OAAO,SAIlB;CACpB,MAAM,EAAE,SAAS,MAAM,QAAQ;CAC/B,MAAM,OAAO,aAAa,KAAK,IAAI;CACnC,IAAI,KAAK,gBAAgB,QAAQ,CAAC,MACjC;CAED,MAAM,WACL,OAAO,KAAK,aAAa,YAAY,KAAK,SAAS,SAAS,IAAI,KAAK,WAAW,KAAA;CACjF,IAAI,CAAC,YAAY,CAAC,gBAAgB,IAAI,KAAK,WAAW,IAAI,MAAM,YAAY,CAAC,SAAS,OAAO,GAC5F;CAED,MAAM,YAAY,IAAI,KAAK,QAAQ;CACnC,IAAI,OAAO,MAAM,UAAU,QAAQ,CAAC,GACnC;CAED,MAAM,uBAAuB,SAAS,KAAK,IAAI,GAAG;CAClD,IAAI;EACH,MAAM,QAAQ,KAAK,MAAM;GACxB,MAAM;GACN,OAAO,EAAE,QAAQ,OAAO,KAAK,EAAE,EAAE;GACjC;GACA;EACD,CAAC;CACF,SAAS,OAAO;EACf,QAAQ,QAAQ,MACf,kEAAkE,OAAO,KAAK,EAAE,EAAE,IACjF,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAEvD;CACD;AACD"}
@@ -0,0 +1,44 @@
1
+ import { Field, Payload, PayloadRequest } from "payload";
2
+
3
+ //#region src/poll/definePollOptionSource.d.ts
4
+ /** One selectable poll choice. `value` is the host's stable identifier contract: it is stored in submissions and later matched against `outcome.winningValues`, so it must never change once votes exist. */
5
+ type PollOption = {
6
+ label: string;
7
+ value: string;
8
+ };
9
+ type PollOptionResolveArgs<TConfig extends Record<string, unknown> = Record<string, unknown>> = {
10
+ config: TConfig;
11
+ form: {
12
+ id: number | string;
13
+ title?: string;
14
+ };
15
+ payload: Payload;
16
+ req?: PayloadRequest;
17
+ };
18
+ /**
19
+ * A poll option source type, authored once by the host: `config` is the admin `Field[]` an author
20
+ * fills per form (e.g. an event id); `resolve` maps that config to the current options from host
21
+ * domain data. Called at render time (via `resolvePollOptions`) and again at submission time, where
22
+ * the resolved values are the only accepted answers (fail closed).
23
+ */
24
+ type PollOptionSource<TConfig extends Record<string, unknown> = Record<string, unknown>> = {
25
+ type: string; /** i18n-key or literal (resolved like a field label), or a per-locale record. */
26
+ label: string | Record<string, string>;
27
+ config?: Field[];
28
+ resolve: (args: PollOptionResolveArgs<TConfig>) => Promise<PollOption[]> | PollOption[];
29
+ /**
30
+ * Optional outcome resolver: return the winning option value(s) from domain data (e.g. the
31
+ * recorded race result), a `string[]` when the result is a tie, or `undefined` while the outcome
32
+ * is not yet decidable. Backs the built-in `source` poll type, so a form whose `poll.type` is
33
+ * `source` auto-resolves through it (via the scheduled close job, a results-read, or an explicit
34
+ * `resolvePollOutcome({ payload, formId })`); every returned value is validated against the poll's
35
+ * resolved options before it is recorded, so each must be one of the values `resolve` yields.
36
+ */
37
+ resolveOutcome?: (args: PollOptionResolveArgs<TConfig>) => Promise<string | string[] | undefined> | string | string[] | undefined;
38
+ };
39
+ /** Erased shape stored in the registry; config re-narrows per matched type at resolution. */
40
+ type AnyPollOptionSource = PollOptionSource<Record<string, unknown>>;
41
+ declare const definePollOptionSource: <TConfig extends Record<string, unknown> = Record<string, unknown>>(source: PollOptionSource<TConfig>) => PollOptionSource<TConfig>;
42
+ //#endregion
43
+ export { AnyPollOptionSource, PollOption, PollOptionResolveArgs, PollOptionSource, definePollOptionSource };
44
+ //# sourceMappingURL=definePollOptionSource.d.ts.map
@@ -0,0 +1,6 @@
1
+ //#region src/poll/definePollOptionSource.ts
2
+ const definePollOptionSource = (source) => source;
3
+ //#endregion
4
+ export { definePollOptionSource };
5
+
6
+ //# sourceMappingURL=definePollOptionSource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definePollOptionSource.js","names":[],"sources":["../../src/poll/definePollOptionSource.ts"],"sourcesContent":["import type { Field, Payload, PayloadRequest } from 'payload'\n\n/** One selectable poll choice. `value` is the host's stable identifier contract: it is stored in submissions and later matched against `outcome.winningValues`, so it must never change once votes exist. */\nexport type PollOption = { label: string; value: string }\n\nexport type PollOptionResolveArgs<\n\tTConfig extends Record<string, unknown> = Record<string, unknown>,\n> = {\n\tconfig: TConfig\n\tform: { id: number | string; title?: string }\n\tpayload: Payload\n\treq?: PayloadRequest\n}\n\n/**\n * A poll option source type, authored once by the host: `config` is the admin `Field[]` an author\n * fills per form (e.g. an event id); `resolve` maps that config to the current options from host\n * domain data. Called at render time (via `resolvePollOptions`) and again at submission time, where\n * the resolved values are the only accepted answers (fail closed).\n */\nexport type PollOptionSource<TConfig extends Record<string, unknown> = Record<string, unknown>> = {\n\ttype: string\n\t/** i18n-key or literal (resolved like a field label), or a per-locale record. */\n\tlabel: string | Record<string, string>\n\tconfig?: Field[]\n\tresolve: (args: PollOptionResolveArgs<TConfig>) => Promise<PollOption[]> | PollOption[]\n\t/**\n\t * Optional outcome resolver: return the winning option value(s) from domain data (e.g. the\n\t * recorded race result), a `string[]` when the result is a tie, or `undefined` while the outcome\n\t * is not yet decidable. Backs the built-in `source` poll type, so a form whose `poll.type` is\n\t * `source` auto-resolves through it (via the scheduled close job, a results-read, or an explicit\n\t * `resolvePollOutcome({ payload, formId })`); every returned value is validated against the poll's\n\t * resolved options before it is recorded, so each must be one of the values `resolve` yields.\n\t */\n\tresolveOutcome?: (\n\t\targs: PollOptionResolveArgs<TConfig>\n\t) => Promise<string | string[] | undefined> | string | string[] | undefined\n}\n\n/** Erased shape stored in the registry; config re-narrows per matched type at resolution. */\nexport type AnyPollOptionSource = PollOptionSource<Record<string, unknown>>\n\nexport const definePollOptionSource = <\n\tTConfig extends Record<string, unknown> = Record<string, unknown>,\n>(\n\tsource: PollOptionSource<TConfig>\n): PollOptionSource<TConfig> => source\n"],"mappings":";AA0CA,MAAa,0BAGZ,WAC+B"}
@@ -0,0 +1,34 @@
1
+ import { FieldAggregation } from "../aggregation/types.js";
2
+ import { PollConfigLike } from "../form/pollState.js";
3
+ import { Field, Payload, PayloadRequest } from "payload";
4
+
5
+ //#region src/poll/definePollType.d.ts
6
+ type PollOutcomeStrategyArgs = {
7
+ payload: Payload;
8
+ req?: PayloadRequest; /** The loaded forms document, widened so a strategy can read host-owned members (e.g. `form.tenant`). */
9
+ form: {
10
+ id: number | string;
11
+ poll?: unknown;
12
+ fields?: unknown;
13
+ } & Record<string, unknown>; /** The poll config narrowed from the form via `pollConfigOf`. */
14
+ poll: PollConfigLike; /** Aggregate the poll's results field, memoized by the caller. Null when there is no results field or no data. */
15
+ aggregate: () => Promise<FieldAggregation | null>;
16
+ };
17
+ /**
18
+ * An outcome-resolution strategy for a poll, authored once and registered via the plugin `poll.types`
19
+ * option (built-ins `manual`, `mostVoted`, `source` are always registered). The poll's `type` select
20
+ * picks one; `resolvePollOutcome` runs it when no explicit `winningValues` are passed. `resolveOutcome`
21
+ * returns the winning value set, or `undefined` when the outcome is not yet decidable so nothing is
22
+ * written. Every returned value is still validated against the poll's effective options by
23
+ * `pollOutcomeBeforeChange`, so a strategy can never record a value outside the poll's choices.
24
+ */
25
+ type PollOutcomeStrategy = {
26
+ type: string; /** i18n-key or literal (resolved like a field label), or a per-locale record. */
27
+ label: string | Record<string, string>;
28
+ config?: Field[];
29
+ resolveOutcome: (args: PollOutcomeStrategyArgs) => Promise<string[] | undefined> | string[] | undefined;
30
+ };
31
+ declare const definePollType: (strategy: PollOutcomeStrategy) => PollOutcomeStrategy;
32
+ //#endregion
33
+ export { PollOutcomeStrategy, PollOutcomeStrategyArgs, definePollType };
34
+ //# sourceMappingURL=definePollType.d.ts.map
@@ -0,0 +1,6 @@
1
+ //#region src/poll/definePollType.ts
2
+ const definePollType = (strategy) => strategy;
3
+ //#endregion
4
+ export { definePollType };
5
+
6
+ //# sourceMappingURL=definePollType.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definePollType.js","names":[],"sources":["../../src/poll/definePollType.ts"],"sourcesContent":["import type { Field, Payload, PayloadRequest } from 'payload'\nimport type { FieldAggregation } from '../aggregation/types'\nimport type { PollConfigLike } from '../form/pollState'\n\nexport type PollOutcomeStrategyArgs = {\n\tpayload: Payload\n\treq?: PayloadRequest\n\t/** The loaded forms document, widened so a strategy can read host-owned members (e.g. `form.tenant`). */\n\tform: { id: number | string; poll?: unknown; fields?: unknown } & Record<string, unknown>\n\t/** The poll config narrowed from the form via `pollConfigOf`. */\n\tpoll: PollConfigLike\n\t/** Aggregate the poll's results field, memoized by the caller. Null when there is no results field or no data. */\n\taggregate: () => Promise<FieldAggregation | null>\n}\n\n/**\n * An outcome-resolution strategy for a poll, authored once and registered via the plugin `poll.types`\n * option (built-ins `manual`, `mostVoted`, `source` are always registered). The poll's `type` select\n * picks one; `resolvePollOutcome` runs it when no explicit `winningValues` are passed. `resolveOutcome`\n * returns the winning value set, or `undefined` when the outcome is not yet decidable so nothing is\n * written. Every returned value is still validated against the poll's effective options by\n * `pollOutcomeBeforeChange`, so a strategy can never record a value outside the poll's choices.\n */\nexport type PollOutcomeStrategy = {\n\ttype: string\n\t/** i18n-key or literal (resolved like a field label), or a per-locale record. */\n\tlabel: string | Record<string, string>\n\tconfig?: Field[]\n\tresolveOutcome: (\n\t\targs: PollOutcomeStrategyArgs\n\t) => Promise<string[] | undefined> | string[] | undefined\n}\n\nexport const definePollType = (strategy: PollOutcomeStrategy): PollOutcomeStrategy => strategy\n"],"mappings":";AAiCA,MAAa,kBAAkB,aAAuD"}
@@ -0,0 +1,38 @@
1
+ import { PollOption } from "./definePollOptionSource.js";
2
+ import { FieldTypeRegistry } from "../fields/registry.js";
3
+ import { PollOptionSourceRegistry } from "./registry.js";
4
+ import { Payload, PayloadRequest } from "payload";
5
+
6
+ //#region src/poll/effectivePollOptions.d.ts
7
+ type ResolveEffectivePollOptionsArgs = {
8
+ payload: Payload;
9
+ req?: PayloadRequest; /** A forms document (or an equivalent merged view of one during a save). */
10
+ form: {
11
+ id: number | string;
12
+ title?: string | null; /** Top-level poll-enable flag; the poll config (results field, source) stays under `poll`. */
13
+ pollEnabled?: boolean | null;
14
+ poll?: unknown;
15
+ fields?: unknown;
16
+ }; /** Registry override for plugin-internal callers; defaults to the registry stashed on the config. */
17
+ sources?: PollOptionSourceRegistry;
18
+ /**
19
+ * Field-type registry override; defaults to the one stashed on the config. Lets the results
20
+ * field's definition source its own options via {@link FormFieldDefinition.resolveOptions}.
21
+ */
22
+ fieldTypes?: FieldTypeRegistry;
23
+ };
24
+ /**
25
+ * The poll's currently valid choice set, resolved in a fixed precedence: source-resolved options
26
+ * when the poll has an `optionSource`, else the results field type's own {@link
27
+ * FormFieldDefinition.resolveOptions} when it declares one (given the live field instance so a
28
+ * custom type can enumerate the values it holds), else the authored options of the field instance.
29
+ * Empty when the poll is disabled, no results field is set, or none of the branches yields options.
30
+ * This single set backs both the admin winner select (the `/:id/poll-options` endpoint) and outcome
31
+ * membership validation, so what the admin offers and what the server accepts cannot drift. Source
32
+ * and resolver failures propagate so callers fail closed; the resolver branch is cached per request
33
+ * (keyed by form id) when a `req` is provided, matching the source path.
34
+ */
35
+ declare const resolveEffectivePollOptions: (args: ResolveEffectivePollOptionsArgs) => Promise<PollOption[]>;
36
+ //#endregion
37
+ export { ResolveEffectivePollOptionsArgs, resolveEffectivePollOptions };
38
+ //# sourceMappingURL=effectivePollOptions.d.ts.map
@@ -0,0 +1,64 @@
1
+ import { fieldTypesOf } from "../fields/registry.js";
2
+ import { pollConfigOf } from "../form/pollState.js";
3
+ import { instanceOptionsOf } from "../fields/instanceOptions.js";
4
+ import { resolvePollOptions } from "./resolvePollOptions.js";
5
+ //#region src/poll/effectivePollOptions.ts
6
+ const CACHE_KEY = "formBuilderFieldOptionsCache";
7
+ const cacheOf = (req) => {
8
+ if (!req?.context) return;
9
+ const existing = req.context[CACHE_KEY];
10
+ if (existing instanceof Map) return existing;
11
+ const created = /* @__PURE__ */ new Map();
12
+ req.context[CACHE_KEY] = created;
13
+ return created;
14
+ };
15
+ /**
16
+ * The poll's currently valid choice set, resolved in a fixed precedence: source-resolved options
17
+ * when the poll has an `optionSource`, else the results field type's own {@link
18
+ * FormFieldDefinition.resolveOptions} when it declares one (given the live field instance so a
19
+ * custom type can enumerate the values it holds), else the authored options of the field instance.
20
+ * Empty when the poll is disabled, no results field is set, or none of the branches yields options.
21
+ * This single set backs both the admin winner select (the `/:id/poll-options` endpoint) and outcome
22
+ * membership validation, so what the admin offers and what the server accepts cannot drift. Source
23
+ * and resolver failures propagate so callers fail closed; the resolver branch is cached per request
24
+ * (keyed by form id) when a `req` is provided, matching the source path.
25
+ */
26
+ const resolveEffectivePollOptions = async (args) => {
27
+ const { payload, req, form, sources, fieldTypes } = args;
28
+ const poll = pollConfigOf(form.poll);
29
+ if (form.pollEnabled !== true || !poll) return [];
30
+ const sourced = await resolvePollOptions({
31
+ payload,
32
+ req,
33
+ form,
34
+ sources
35
+ });
36
+ if (sourced) return sourced;
37
+ const resultsField = typeof poll.resultsField === "string" && poll.resultsField.length > 0 ? poll.resultsField : void 0;
38
+ if (!resultsField) return [];
39
+ const instance = (Array.isArray(form.fields) ? form.fields : []).find((entry) => entry.name === resultsField);
40
+ if (!instance) return [];
41
+ const definition = (fieldTypes ?? fieldTypesOf(payload)).get(instance.blockType);
42
+ if (definition?.resolveOptions) {
43
+ const cache = cacheOf(req);
44
+ const cacheKey = String(form.id);
45
+ const cached = cache?.get(cacheKey);
46
+ if (cached) return cached;
47
+ const options = await definition.resolveOptions({
48
+ instance,
49
+ form: {
50
+ id: form.id,
51
+ title: typeof form.title === "string" ? form.title : void 0
52
+ },
53
+ payload,
54
+ req
55
+ });
56
+ cache?.set(cacheKey, options);
57
+ return options;
58
+ }
59
+ return instanceOptionsOf(instance) ?? [];
60
+ };
61
+ //#endregion
62
+ export { resolveEffectivePollOptions };
63
+
64
+ //# sourceMappingURL=effectivePollOptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effectivePollOptions.js","names":[],"sources":["../../src/poll/effectivePollOptions.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport { instanceOptionsOf } from '../fields/instanceOptions'\nimport { type FieldTypeRegistry, fieldTypesOf } from '../fields/registry'\nimport { pollConfigOf } from '../form/pollState'\nimport type { FormFieldInstance } from '../submissions/types'\nimport type { PollOption } from './definePollOptionSource'\nimport type { PollOptionSourceRegistry } from './registry'\nimport { resolvePollOptions } from './resolvePollOptions'\n\nexport type ResolveEffectivePollOptionsArgs = {\n\tpayload: Payload\n\treq?: PayloadRequest\n\t/** A forms document (or an equivalent merged view of one during a save). */\n\tform: {\n\t\tid: number | string\n\t\ttitle?: string | null\n\t\t/** Top-level poll-enable flag; the poll config (results field, source) stays under `poll`. */\n\t\tpollEnabled?: boolean | null\n\t\tpoll?: unknown\n\t\tfields?: unknown\n\t}\n\t/** Registry override for plugin-internal callers; defaults to the registry stashed on the config. */\n\tsources?: PollOptionSourceRegistry\n\t/**\n\t * Field-type registry override; defaults to the one stashed on the config. Lets the results\n\t * field's definition source its own options via {@link FormFieldDefinition.resolveOptions}.\n\t */\n\tfieldTypes?: FieldTypeRegistry\n}\n\nconst CACHE_KEY = 'formBuilderFieldOptionsCache'\n\ntype OptionsCache = Map<string, PollOption[]>\n\nconst cacheOf = (req: PayloadRequest | undefined): OptionsCache | undefined => {\n\tif (!req?.context) {\n\t\treturn undefined\n\t}\n\tconst existing = req.context[CACHE_KEY]\n\tif (existing instanceof Map) {\n\t\treturn existing as OptionsCache\n\t}\n\tconst created: OptionsCache = new Map()\n\treq.context[CACHE_KEY] = created\n\treturn created\n}\n\n/**\n * The poll's currently valid choice set, resolved in a fixed precedence: source-resolved options\n * when the poll has an `optionSource`, else the results field type's own {@link\n * FormFieldDefinition.resolveOptions} when it declares one (given the live field instance so a\n * custom type can enumerate the values it holds), else the authored options of the field instance.\n * Empty when the poll is disabled, no results field is set, or none of the branches yields options.\n * This single set backs both the admin winner select (the `/:id/poll-options` endpoint) and outcome\n * membership validation, so what the admin offers and what the server accepts cannot drift. Source\n * and resolver failures propagate so callers fail closed; the resolver branch is cached per request\n * (keyed by form id) when a `req` is provided, matching the source path.\n */\nexport const resolveEffectivePollOptions = async (\n\targs: ResolveEffectivePollOptionsArgs\n): Promise<PollOption[]> => {\n\tconst { payload, req, form, sources, fieldTypes } = args\n\tconst poll = pollConfigOf(form.poll)\n\tif (form.pollEnabled !== true || !poll) {\n\t\treturn []\n\t}\n\tconst sourced = await resolvePollOptions({ payload, req, form, sources })\n\tif (sourced) {\n\t\treturn sourced\n\t}\n\tconst resultsField =\n\t\ttypeof poll.resultsField === 'string' && poll.resultsField.length > 0\n\t\t\t? poll.resultsField\n\t\t\t: undefined\n\tif (!resultsField) {\n\t\treturn []\n\t}\n\tconst instances = Array.isArray(form.fields) ? (form.fields as FormFieldInstance[]) : []\n\tconst instance = instances.find((entry) => entry.name === resultsField)\n\tif (!instance) {\n\t\treturn []\n\t}\n\tconst definition = (fieldTypes ?? fieldTypesOf(payload)).get(instance.blockType)\n\tif (definition?.resolveOptions) {\n\t\tconst cache = cacheOf(req)\n\t\tconst cacheKey = String(form.id)\n\t\tconst cached = cache?.get(cacheKey)\n\t\tif (cached) {\n\t\t\treturn cached\n\t\t}\n\t\tconst options = await definition.resolveOptions({\n\t\t\tinstance,\n\t\t\tform: { id: form.id, title: typeof form.title === 'string' ? form.title : undefined },\n\t\t\tpayload,\n\t\t\treq,\n\t\t})\n\t\tcache?.set(cacheKey, options)\n\t\treturn options\n\t}\n\treturn instanceOptionsOf(instance) ?? []\n}\n"],"mappings":";;;;;AA8BA,MAAM,YAAY;AAIlB,MAAM,WAAW,QAA8D;CAC9E,IAAI,CAAC,KAAK,SACT;CAED,MAAM,WAAW,IAAI,QAAQ;CAC7B,IAAI,oBAAoB,KACvB,OAAO;CAER,MAAM,0BAAwB,IAAI,IAAI;CACtC,IAAI,QAAQ,aAAa;CACzB,OAAO;AACR;;;;;;;;;;;;AAaA,MAAa,8BAA8B,OAC1C,SAC2B;CAC3B,MAAM,EAAE,SAAS,KAAK,MAAM,SAAS,eAAe;CACpD,MAAM,OAAO,aAAa,KAAK,IAAI;CACnC,IAAI,KAAK,gBAAgB,QAAQ,CAAC,MACjC,OAAO,CAAC;CAET,MAAM,UAAU,MAAM,mBAAmB;EAAE;EAAS;EAAK;EAAM;CAAQ,CAAC;CACxE,IAAI,SACH,OAAO;CAER,MAAM,eACL,OAAO,KAAK,iBAAiB,YAAY,KAAK,aAAa,SAAS,IACjE,KAAK,eACL,KAAA;CACJ,IAAI,CAAC,cACJ,OAAO,CAAC;CAGT,MAAM,YADY,MAAM,QAAQ,KAAK,MAAM,IAAK,KAAK,SAAiC,CAAC,GAC5D,MAAM,UAAU,MAAM,SAAS,YAAY;CACtE,IAAI,CAAC,UACJ,OAAO,CAAC;CAET,MAAM,cAAc,cAAc,aAAa,OAAO,GAAG,IAAI,SAAS,SAAS;CAC/E,IAAI,YAAY,gBAAgB;EAC/B,MAAM,QAAQ,QAAQ,GAAG;EACzB,MAAM,WAAW,OAAO,KAAK,EAAE;EAC/B,MAAM,SAAS,OAAO,IAAI,QAAQ;EAClC,IAAI,QACH,OAAO;EAER,MAAM,UAAU,MAAM,WAAW,eAAe;GAC/C;GACA,MAAM;IAAE,IAAI,KAAK;IAAI,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,KAAA;GAAU;GACpF;GACA;EACD,CAAC;EACD,OAAO,IAAI,UAAU,OAAO;EAC5B,OAAO;CACR;CACA,OAAO,kBAAkB,QAAQ,KAAK,CAAC;AACxC"}
@@ -0,0 +1,21 @@
1
+ import { PollOutcomeStrategy } from "./definePollType.js";
2
+
3
+ //#region src/poll/mostVoted.d.ts
4
+ /**
5
+ * Every bucket value tied for the highest count, so a draw records all co-winners. Empty when there
6
+ * are no buckets. Pure: the caller decides whether the aggregation is trustworthy enough to act on.
7
+ */
8
+ declare const topBucketValues: (buckets: {
9
+ value: string;
10
+ count: number;
11
+ }[]) => string[];
12
+ /**
13
+ * Auto-resolve to whichever choice(s) received the most votes. Refuses to decide, returning
14
+ * `undefined` (no write), when there is no aggregation, no responses yet, or the aggregation was
15
+ * `truncated` (a sample cannot be trusted to name the true winner, so it is flagged for a manual
16
+ * call instead). A perfect tie records every co-winner.
17
+ */
18
+ declare const mostVotedStrategy: PollOutcomeStrategy;
19
+ //#endregion
20
+ export { mostVotedStrategy, topBucketValues };
21
+ //# sourceMappingURL=mostVoted.d.ts.map
@@ -0,0 +1,32 @@
1
+ import { keys } from "../translations/keys.js";
2
+ import { definePollType } from "./definePollType.js";
3
+ //#region src/poll/mostVoted.ts
4
+ /**
5
+ * Every bucket value tied for the highest count, so a draw records all co-winners. Empty when there
6
+ * are no buckets. Pure: the caller decides whether the aggregation is trustworthy enough to act on.
7
+ */
8
+ const topBucketValues = (buckets) => {
9
+ if (buckets.length === 0) return [];
10
+ const max = Math.max(...buckets.map((bucket) => bucket.count));
11
+ return buckets.filter((bucket) => bucket.count === max).map((bucket) => bucket.value);
12
+ };
13
+ /**
14
+ * Auto-resolve to whichever choice(s) received the most votes. Refuses to decide, returning
15
+ * `undefined` (no write), when there is no aggregation, no responses yet, or the aggregation was
16
+ * `truncated` (a sample cannot be trusted to name the true winner, so it is flagged for a manual
17
+ * call instead). A perfect tie records every co-winner.
18
+ */
19
+ const mostVotedStrategy = definePollType({
20
+ type: "mostVoted",
21
+ label: keys.pollTypeMostVoted,
22
+ resolveOutcome: async ({ aggregate }) => {
23
+ const aggregation = await aggregate();
24
+ if (!aggregation || aggregation.total === 0 || aggregation.truncated) return;
25
+ const winners = topBucketValues(aggregation.buckets);
26
+ return winners.length > 0 ? winners : void 0;
27
+ }
28
+ });
29
+ //#endregion
30
+ export { mostVotedStrategy, topBucketValues };
31
+
32
+ //# sourceMappingURL=mostVoted.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mostVoted.js","names":[],"sources":["../../src/poll/mostVoted.ts"],"sourcesContent":["import { keys } from '../translations/keys'\nimport { definePollType, type PollOutcomeStrategy } from './definePollType'\n\n/**\n * Every bucket value tied for the highest count, so a draw records all co-winners. Empty when there\n * are no buckets. Pure: the caller decides whether the aggregation is trustworthy enough to act on.\n */\nexport const topBucketValues = (buckets: { value: string; count: number }[]): string[] => {\n\tif (buckets.length === 0) {\n\t\treturn []\n\t}\n\tconst max = Math.max(...buckets.map((bucket) => bucket.count))\n\treturn buckets.filter((bucket) => bucket.count === max).map((bucket) => bucket.value)\n}\n\n/**\n * Auto-resolve to whichever choice(s) received the most votes. Refuses to decide, returning\n * `undefined` (no write), when there is no aggregation, no responses yet, or the aggregation was\n * `truncated` (a sample cannot be trusted to name the true winner, so it is flagged for a manual\n * call instead). A perfect tie records every co-winner.\n */\nexport const mostVotedStrategy: PollOutcomeStrategy = definePollType({\n\ttype: 'mostVoted',\n\tlabel: keys.pollTypeMostVoted,\n\tresolveOutcome: async ({ aggregate }) => {\n\t\tconst aggregation = await aggregate()\n\t\tif (!aggregation || aggregation.total === 0 || aggregation.truncated) {\n\t\t\treturn undefined\n\t\t}\n\t\tconst winners = topBucketValues(aggregation.buckets)\n\t\treturn winners.length > 0 ? winners : undefined\n\t},\n})\n"],"mappings":";;;;;;;AAOA,MAAa,mBAAmB,YAA0D;CACzF,IAAI,QAAQ,WAAW,GACtB,OAAO,CAAC;CAET,MAAM,MAAM,KAAK,IAAI,GAAG,QAAQ,KAAK,WAAW,OAAO,KAAK,CAAC;CAC7D,OAAO,QAAQ,QAAQ,WAAW,OAAO,UAAU,GAAG,EAAE,KAAK,WAAW,OAAO,KAAK;AACrF;;;;;;;AAQA,MAAa,oBAAyC,eAAe;CACpE,MAAM;CACN,OAAO,KAAK;CACZ,gBAAgB,OAAO,EAAE,gBAAgB;EACxC,MAAM,cAAc,MAAM,UAAU;EACpC,IAAI,CAAC,eAAe,YAAY,UAAU,KAAK,YAAY,WAC1D;EAED,MAAM,UAAU,gBAAgB,YAAY,OAAO;EACnD,OAAO,QAAQ,SAAS,IAAI,UAAU,KAAA;CACvC;AACD,CAAC"}
@@ -0,0 +1,91 @@
1
+ import { keys } from "../translations/keys.js";
2
+ import { asTranslate } from "../translations/server.js";
3
+ import { pollConfigOf } from "../form/pollState.js";
4
+ import { resolveEffectivePollOptions } from "./effectivePollOptions.js";
5
+ import { FORMS_SLUG } from "../collections/forms.js";
6
+ import { ValidationError } from "payload";
7
+ //#region src/poll/outcomeBeforeChange.ts
8
+ const WINNING_VALUES_PATH = "poll.outcome.winningValues";
9
+ /** The winning set as a deduped array of non-empty strings, ready to store and to compare. */
10
+ const normalizeWinning = (value) => {
11
+ if (!Array.isArray(value)) return [];
12
+ const seen = /* @__PURE__ */ new Set();
13
+ for (const entry of value) if (typeof entry === "string" && entry.length > 0) seen.add(entry);
14
+ return [...seen];
15
+ };
16
+ /** Set equality on two already-deduped winner arrays: order-insensitive membership. */
17
+ const sameWinners = (a, b) => {
18
+ if (a.length !== b.length) return false;
19
+ const set = new Set(a);
20
+ return b.every((value) => set.has(value));
21
+ };
22
+ /**
23
+ * Owns the poll outcome lifecycle for every create and update, admin and API alike. When the save
24
+ * writes `poll.outcome.winningValues` it validates a set or changed winner set against the poll's
25
+ * effective options (source-resolved or authored, over a merged view of the doc so partial updates
26
+ * validate against stored config) and requires the poll to be enabled; clearing is always allowed.
27
+ * A tie is a set with more than one winner; every value must be a member of the effective options.
28
+ *
29
+ * It is also the single `resolvedAt` stamping point: stamped when the winner set is set or changed,
30
+ * cleared with the set, untouched when the set is unchanged (order-insensitive). Callers can never
31
+ * write `resolvedAt` themselves (field-level access strips it during the beforeValidate traversal,
32
+ * before this hook runs), while values this hook sets survive to the database, so the stamp always
33
+ * reflects the actual transition regardless of the write path.
34
+ */
35
+ const pollOutcomeBeforeChange = async ({ data, originalDoc, req }) => {
36
+ const incomingPoll = data?.poll != null && typeof data.poll === "object" ? data.poll : void 0;
37
+ const outcome = incomingPoll?.outcome != null && typeof incomingPoll.outcome === "object" ? incomingPoll.outcome : void 0;
38
+ if (!outcome || !("winningValues" in outcome)) return data;
39
+ const next = normalizeWinning(outcome.winningValues);
40
+ outcome.winningValues = next;
41
+ if (sameWinners(next, normalizeWinning(pollConfigOf(originalDoc?.poll)?.outcome?.winningValues))) return data;
42
+ if (next.length === 0) {
43
+ outcome.resolvedAt = null;
44
+ return data;
45
+ }
46
+ const t = asTranslate(req.t);
47
+ const fail = (message) => {
48
+ throw new ValidationError({
49
+ collection: FORMS_SLUG,
50
+ errors: [{
51
+ path: WINNING_VALUES_PATH,
52
+ message
53
+ }]
54
+ }, req.t);
55
+ };
56
+ const mergedPoll = {
57
+ ...(originalDoc?.poll != null && typeof originalDoc.poll === "object" ? originalDoc.poll : void 0) ?? {},
58
+ ...incomingPoll
59
+ };
60
+ const mergedPollEnabled = {
61
+ ...originalDoc ?? {},
62
+ ...data
63
+ }.pollEnabled === true;
64
+ if (!mergedPollEnabled) fail(t(keys.validationWinningValueDisabled));
65
+ const id = originalDoc?.id ?? data?.id;
66
+ const form = {
67
+ id: id ?? "",
68
+ title: typeof data?.title === "string" ? data.title : originalDoc?.title,
69
+ pollEnabled: mergedPollEnabled,
70
+ poll: mergedPoll,
71
+ fields: data?.fields ?? originalDoc?.fields
72
+ };
73
+ let options;
74
+ try {
75
+ options = await resolveEffectivePollOptions({
76
+ payload: req.payload,
77
+ req: id != null ? req : void 0,
78
+ form
79
+ });
80
+ } catch {
81
+ return fail(t(keys.pollOptionsUnavailable));
82
+ }
83
+ const allowed = new Set(options.map((option) => option.value));
84
+ if (!next.every((value) => allowed.has(value))) fail(t(keys.validationWinningValueUnknown));
85
+ outcome.resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
86
+ return data;
87
+ };
88
+ //#endregion
89
+ export { pollOutcomeBeforeChange };
90
+
91
+ //# sourceMappingURL=outcomeBeforeChange.js.map