@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,90 @@
1
+ import { pollConfigOf } from "../form/pollState.js";
2
+ import { aggregateFieldResponses } from "../aggregation/aggregateResponses.js";
3
+ import { resolveEffectivePollOptions } from "./effectivePollOptions.js";
4
+ import { pollTypesOf } from "./pollTypeRegistry.js";
5
+ import { aggregateFromVotes } from "./votes/aggregateFromVotes.js";
6
+ import { FORMS_SLUG } from "../collections/forms.js";
7
+ //#region src/poll/resolvePollOutcome.ts
8
+ /** A strategy result narrowed to a deduped set of non-empty strings. */
9
+ const cleanWinners = (value) => value === void 0 ? [] : [...new Set(value.filter((entry) => typeof entry === "string" && entry.length > 0))];
10
+ /**
11
+ * Record a poll's final outcome server-side, in one of two modes:
12
+ * - explicit: pass `winningValues` when host code already knows the winner(s); more than one is a
13
+ * tie, and an empty array clears the outcome;
14
+ * - auto: omit it and the poll's selected outcome strategy (`poll.type`, from the `poll.types`
15
+ * registry, defaulting to `manual`) resolves the winners. `manual` is a no-op; `mostVoted` reads
16
+ * the aggregated results field; `source` delegates to the option source. A strategy that is not
17
+ * yet decidable (or `manual`) writes nothing and this returns an empty set, so auto mode is safe
18
+ * to call repeatedly from the close job and the read fallback.
19
+ *
20
+ * Every write runs through `payload.update` with `overrideAccess`, so the forms collection hook
21
+ * validates each value against the poll's effective options and owns the `poll.outcome.resolvedAt`
22
+ * stamp in both modes (auto-resolved winners are validated exactly as hand-picked ones). Throws only
23
+ * when the form does not exist or is not poll-enabled. Returns the recorded winning set (empty when
24
+ * auto mode did not decide).
25
+ */
26
+ const resolvePollOutcome = async (args) => {
27
+ const { payload, formId, pollVotesEnabled, req } = args;
28
+ const form = await payload.findByID({
29
+ collection: FORMS_SLUG,
30
+ id: formId,
31
+ depth: 0,
32
+ overrideAccess: true,
33
+ req
34
+ });
35
+ const poll = pollConfigOf(form.poll);
36
+ if (form.pollEnabled !== true || !poll) throw new Error(`Form ${String(formId)} is not poll-enabled; cannot record an outcome.`);
37
+ let winningValues;
38
+ if (args.winningValues !== void 0) winningValues = args.winningValues;
39
+ else {
40
+ const strategyType = typeof poll.type === "string" && poll.type.length > 0 ? poll.type : "manual";
41
+ const strategy = pollTypesOf(payload).get(strategyType);
42
+ if (!strategy) return [];
43
+ let aggregated;
44
+ const aggregate = async () => {
45
+ if (aggregated === void 0) {
46
+ const resultsField = typeof poll.resultsField === "string" && poll.resultsField.length > 0 ? poll.resultsField : void 0;
47
+ if (!resultsField) aggregated = null;
48
+ else if (pollVotesEnabled === true) aggregated = await aggregateFromVotes({
49
+ payload,
50
+ formId,
51
+ field: resultsField,
52
+ options: await resolveEffectivePollOptions({
53
+ payload,
54
+ req,
55
+ form
56
+ }).catch(() => []),
57
+ req
58
+ });
59
+ else aggregated = await aggregateFieldResponses({
60
+ payload,
61
+ formId,
62
+ field: resultsField,
63
+ req
64
+ });
65
+ }
66
+ return aggregated;
67
+ };
68
+ winningValues = cleanWinners(await strategy.resolveOutcome({
69
+ payload,
70
+ req,
71
+ form,
72
+ poll,
73
+ aggregate
74
+ }));
75
+ if (winningValues.length === 0) return [];
76
+ }
77
+ await payload.update({
78
+ collection: FORMS_SLUG,
79
+ id: formId,
80
+ data: { poll: { outcome: { winningValues } } },
81
+ depth: 0,
82
+ overrideAccess: true,
83
+ req
84
+ });
85
+ return winningValues;
86
+ };
87
+ //#endregion
88
+ export { resolvePollOutcome };
89
+
90
+ //# sourceMappingURL=resolvePollOutcome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolvePollOutcome.js","names":[],"sources":["../../src/poll/resolvePollOutcome.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport { aggregateFieldResponses } from '../aggregation/aggregateResponses'\nimport type { FieldAggregation } from '../aggregation/types'\nimport { FORMS_SLUG } from '../collections/forms'\nimport { pollConfigOf } from '../form/pollState'\nimport type { PollOutcomeStrategyArgs } from './definePollType'\nimport { resolveEffectivePollOptions } from './effectivePollOptions'\nimport { pollTypesOf } from './pollTypeRegistry'\nimport { aggregateFromVotes } from './votes/aggregateFromVotes'\n\nexport type ResolvePollOutcomeArgs = {\n\tpayload: Payload\n\tformId: number | string\n\t/**\n\t * The winning set: stable option values, each matching one of the poll's choice values (the\n\t * host's contract with `PollOption.value`). Pass more than one for a tie, an empty array to clear\n\t * the outcome. Omit to auto-resolve via the poll's selected outcome strategy (`poll.type`).\n\t */\n\twinningValues?: string[]\n\t/**\n\t * Whether the hidden tally store backs the auto-resolve aggregation (`mostVoted` and any host\n\t * strategy that calls `aggregate()`). False keeps the submission scan, so persist-off forms\n\t * only resolve correctly with the store enabled.\n\t */\n\tpollVotesEnabled?: boolean\n\treq?: PayloadRequest\n}\n\n/** A strategy result narrowed to a deduped set of non-empty strings. */\nconst cleanWinners = (value: string[] | undefined): string[] =>\n\tvalue === undefined\n\t\t? []\n\t\t: [\n\t\t\t\t...new Set(\n\t\t\t\t\tvalue.filter((entry): entry is string => typeof entry === 'string' && entry.length > 0)\n\t\t\t\t),\n\t\t\t]\n\n/**\n * Record a poll's final outcome server-side, in one of two modes:\n * - explicit: pass `winningValues` when host code already knows the winner(s); more than one is a\n * tie, and an empty array clears the outcome;\n * - auto: omit it and the poll's selected outcome strategy (`poll.type`, from the `poll.types`\n * registry, defaulting to `manual`) resolves the winners. `manual` is a no-op; `mostVoted` reads\n * the aggregated results field; `source` delegates to the option source. A strategy that is not\n * yet decidable (or `manual`) writes nothing and this returns an empty set, so auto mode is safe\n * to call repeatedly from the close job and the read fallback.\n *\n * Every write runs through `payload.update` with `overrideAccess`, so the forms collection hook\n * validates each value against the poll's effective options and owns the `poll.outcome.resolvedAt`\n * stamp in both modes (auto-resolved winners are validated exactly as hand-picked ones). Throws only\n * when the form does not exist or is not poll-enabled. Returns the recorded winning set (empty when\n * auto mode did not decide).\n */\nexport const resolvePollOutcome = async (args: ResolvePollOutcomeArgs): Promise<string[]> => {\n\tconst { payload, formId, pollVotesEnabled, req } = args\n\tconst form = await payload.findByID({\n\t\tcollection: FORMS_SLUG,\n\t\tid: formId,\n\t\tdepth: 0,\n\t\toverrideAccess: true,\n\t\treq,\n\t})\n\tconst poll = pollConfigOf(form.poll)\n\tif (form.pollEnabled !== true || !poll) {\n\t\tthrow new Error(`Form ${String(formId)} is not poll-enabled; cannot record an outcome.`)\n\t}\n\n\tlet winningValues: string[]\n\tif (args.winningValues !== undefined) {\n\t\twinningValues = args.winningValues\n\t} else {\n\t\tconst strategyType =\n\t\t\ttypeof poll.type === 'string' && poll.type.length > 0 ? poll.type : 'manual'\n\t\tconst strategy = pollTypesOf(payload).get(strategyType)\n\t\tif (!strategy) {\n\t\t\treturn []\n\t\t}\n\t\tlet aggregated: FieldAggregation | null | undefined\n\t\tconst aggregate = async (): Promise<FieldAggregation | null> => {\n\t\t\tif (aggregated === undefined) {\n\t\t\t\tconst resultsField =\n\t\t\t\t\ttypeof poll.resultsField === 'string' && poll.resultsField.length > 0\n\t\t\t\t\t\t? poll.resultsField\n\t\t\t\t\t\t: undefined\n\t\t\t\tif (!resultsField) {\n\t\t\t\t\taggregated = null\n\t\t\t\t} else if (pollVotesEnabled === true) {\n\t\t\t\t\t// Best-effort options: a source/resolver failure degrades to raw tally values\n\t\t\t\t\t// (the outcome hook still validates winners against the effective options).\n\t\t\t\t\tconst options = await resolveEffectivePollOptions({ payload, req, form }).catch(() => [])\n\t\t\t\t\taggregated = await aggregateFromVotes({\n\t\t\t\t\t\tpayload,\n\t\t\t\t\t\tformId,\n\t\t\t\t\t\tfield: resultsField,\n\t\t\t\t\t\toptions,\n\t\t\t\t\t\treq,\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\taggregated = await aggregateFieldResponses({ payload, formId, field: resultsField, req })\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn aggregated\n\t\t}\n\t\twinningValues = cleanWinners(\n\t\t\tawait strategy.resolveOutcome({\n\t\t\t\tpayload,\n\t\t\t\treq,\n\t\t\t\tform: form as unknown as PollOutcomeStrategyArgs['form'],\n\t\t\t\tpoll,\n\t\t\t\taggregate,\n\t\t\t})\n\t\t)\n\t\tif (winningValues.length === 0) {\n\t\t\treturn []\n\t\t}\n\t}\n\n\tawait payload.update({\n\t\tcollection: FORMS_SLUG,\n\t\tid: formId,\n\t\tdata: { poll: { outcome: { winningValues } } },\n\t\tdepth: 0,\n\t\toverrideAccess: true,\n\t\treq,\n\t})\n\treturn winningValues\n}\n"],"mappings":";;;;;;;;AA6BA,MAAM,gBAAgB,UACrB,UAAU,KAAA,IACP,CAAC,IACD,CACA,GAAG,IAAI,IACN,MAAM,QAAQ,UAA2B,OAAO,UAAU,YAAY,MAAM,SAAS,CAAC,CACvF,CACD;;;;;;;;;;;;;;;;;AAkBH,MAAa,qBAAqB,OAAO,SAAoD;CAC5F,MAAM,EAAE,SAAS,QAAQ,kBAAkB,QAAQ;CACnD,MAAM,OAAO,MAAM,QAAQ,SAAS;EACnC,YAAY;EACZ,IAAI;EACJ,OAAO;EACP,gBAAgB;EAChB;CACD,CAAC;CACD,MAAM,OAAO,aAAa,KAAK,IAAI;CACnC,IAAI,KAAK,gBAAgB,QAAQ,CAAC,MACjC,MAAM,IAAI,MAAM,QAAQ,OAAO,MAAM,EAAE,gDAAgD;CAGxF,IAAI;CACJ,IAAI,KAAK,kBAAkB,KAAA,GAC1B,gBAAgB,KAAK;MACf;EACN,MAAM,eACL,OAAO,KAAK,SAAS,YAAY,KAAK,KAAK,SAAS,IAAI,KAAK,OAAO;EACrE,MAAM,WAAW,YAAY,OAAO,EAAE,IAAI,YAAY;EACtD,IAAI,CAAC,UACJ,OAAO,CAAC;EAET,IAAI;EACJ,MAAM,YAAY,YAA8C;GAC/D,IAAI,eAAe,KAAA,GAAW;IAC7B,MAAM,eACL,OAAO,KAAK,iBAAiB,YAAY,KAAK,aAAa,SAAS,IACjE,KAAK,eACL,KAAA;IACJ,IAAI,CAAC,cACJ,aAAa;SACP,IAAI,qBAAqB,MAI/B,aAAa,MAAM,mBAAmB;KACrC;KACA;KACA,OAAO;KACP,SAAA,MALqB,4BAA4B;MAAE;MAAS;MAAK;KAAK,CAAC,EAAE,YAAY,CAAC,CAAC;KAMvF;IACD,CAAC;SAED,aAAa,MAAM,wBAAwB;KAAE;KAAS;KAAQ,OAAO;KAAc;IAAI,CAAC;GAE1F;GACA,OAAO;EACR;EACA,gBAAgB,aACf,MAAM,SAAS,eAAe;GAC7B;GACA;GACM;GACN;GACA;EACD,CAAC,CACF;EACA,IAAI,cAAc,WAAW,GAC5B,OAAO,CAAC;CAEV;CAEA,MAAM,QAAQ,OAAO;EACpB,YAAY;EACZ,IAAI;EACJ,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,EAAE;EAC7C,OAAO;EACP,gBAAgB;EAChB;CACD,CAAC;CACD,OAAO;AACR"}
@@ -0,0 +1,21 @@
1
+ import { fieldNamesOfType } from "../fields/fieldNamesOfType.js";
2
+ import { keys } from "../translations/keys.js";
3
+ import { asTranslate } from "../translations/server.js";
4
+ //#region src/poll/resultsField.ts
5
+ /** Slugs of registered field types choosable as a poll's results field (`pollEligible: true`). */
6
+ const pollEligibleTypes = (registry) => [...registry.values()].filter((definition) => definition.pollEligible === true).map((definition) => definition.type);
7
+ /**
8
+ * Validate for `poll.resultsField`, closed over the eligible type slugs derived from the registry
9
+ * at collection-build time (mirrors the confirmation action's `toField`): unset is fine, otherwise
10
+ * the value must name an existing, named field instance whose type is poll-eligible. Payload skips
11
+ * the validate while the poll is disabled (the field's `admin.condition`), so a stale reference on
12
+ * a disabled poll never blocks a save.
13
+ */
14
+ const buildValidateResultsField = (eligibleTypes) => (value, { data, req }) => {
15
+ if (typeof value !== "string" || value.length === 0) return true;
16
+ return fieldNamesOfType(data && typeof data === "object" ? data.fields : void 0, eligibleTypes).includes(value) ? true : asTranslate(req.t)(keys.validationResultsFieldUnknown);
17
+ };
18
+ //#endregion
19
+ export { buildValidateResultsField, pollEligibleTypes };
20
+
21
+ //# sourceMappingURL=resultsField.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resultsField.js","names":[],"sources":["../../src/poll/resultsField.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\nimport { fieldNamesOfType } from '../fields/fieldNamesOfType'\nimport type { FieldTypeRegistry } from '../fields/registry'\nimport { keys } from '../translations/keys'\nimport { asTranslate } from '../translations/server'\n\n/** Slugs of registered field types choosable as a poll's results field (`pollEligible: true`). */\nexport const pollEligibleTypes = (registry: FieldTypeRegistry): string[] =>\n\t[...registry.values()]\n\t\t.filter((definition) => definition.pollEligible === true)\n\t\t.map((definition) => definition.type)\n\n/**\n * Validate for `poll.resultsField`, closed over the eligible type slugs derived from the registry\n * at collection-build time (mirrors the confirmation action's `toField`): unset is fine, otherwise\n * the value must name an existing, named field instance whose type is poll-eligible. Payload skips\n * the validate while the poll is disabled (the field's `admin.condition`), so a stale reference on\n * a disabled poll never blocks a save.\n */\nexport const buildValidateResultsField =\n\t(eligibleTypes: readonly string[]) =>\n\t(value: unknown, { data, req }: { data?: unknown; req: PayloadRequest }): string | true => {\n\t\tif (typeof value !== 'string' || value.length === 0) {\n\t\t\treturn true\n\t\t}\n\t\tconst fields =\n\t\t\tdata && typeof data === 'object' ? (data as Record<string, unknown>).fields : undefined\n\t\treturn fieldNamesOfType(fields, eligibleTypes).includes(value)\n\t\t\t? true\n\t\t\t: asTranslate(req.t)(keys.validationResultsFieldUnknown)\n\t}\n"],"mappings":";;;;;AAOA,MAAa,qBAAqB,aACjC,CAAC,GAAG,SAAS,OAAO,CAAC,EACnB,QAAQ,eAAe,WAAW,iBAAiB,IAAI,EACvD,KAAK,eAAe,WAAW,IAAI;;;;;;;;AAStC,MAAa,6BACX,mBACA,OAAgB,EAAE,MAAM,UAAkE;CAC1F,IAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GACjD,OAAO;CAIR,OAAO,iBADN,QAAQ,OAAO,SAAS,WAAY,KAAiC,SAAS,KAAA,GAC/C,aAAa,EAAE,SAAS,KAAK,IAC1D,OACA,YAAY,IAAI,CAAC,EAAE,KAAK,6BAA6B;AACzD"}
@@ -0,0 +1,27 @@
1
+ import { FieldAggregation } from "../../aggregation/types.js";
2
+ import { Payload, PayloadRequest } from "payload";
3
+
4
+ //#region src/poll/votes/aggregateFromVotes.d.ts
5
+ /**
6
+ * Poll results from the tally store: one find over the form's rows for the field (O(options),
7
+ * no scan, no truncation), buckets zero-seeded from the effective options so order and labels
8
+ * match the scan-based shape. Counts sum across shard rows (see VOTE_SHARDS). `total` comes
9
+ * from the respondents rows, keeping the percentage denominator exact for multi-value answers.
10
+ */
11
+ declare const aggregateFromVotes: (args: {
12
+ payload: Payload;
13
+ formId: number | string;
14
+ field: string;
15
+ meta?: {
16
+ label?: string;
17
+ fieldType?: string;
18
+ };
19
+ options: {
20
+ value: string;
21
+ label: string;
22
+ }[];
23
+ req?: PayloadRequest;
24
+ }) => Promise<FieldAggregation>;
25
+ //#endregion
26
+ export { aggregateFromVotes };
27
+ //# sourceMappingURL=aggregateFromVotes.d.ts.map
@@ -0,0 +1,54 @@
1
+ import { POLL_VOTES_SLUG } from "./votesCollection.js";
2
+ //#region src/poll/votes/aggregateFromVotes.ts
3
+ const round1 = (n) => Math.round(n * 10) / 10;
4
+ /**
5
+ * Poll results from the tally store: one find over the form's rows for the field (O(options),
6
+ * no scan, no truncation), buckets zero-seeded from the effective options so order and labels
7
+ * match the scan-based shape. Counts sum across shard rows (see VOTE_SHARDS). `total` comes
8
+ * from the respondents rows, keeping the percentage denominator exact for multi-value answers.
9
+ */
10
+ const aggregateFromVotes = async (args) => {
11
+ const { payload, formId, field, meta, options, req } = args;
12
+ const { docs } = await payload.find({
13
+ collection: POLL_VOTES_SLUG,
14
+ where: { and: [{ form: { equals: String(formId) } }, { field: { equals: field } }] },
15
+ limit: 1e5,
16
+ pagination: false,
17
+ depth: 0,
18
+ overrideAccess: true,
19
+ req
20
+ });
21
+ const counts = /* @__PURE__ */ new Map();
22
+ let total = 0;
23
+ for (const doc of docs) {
24
+ const value = doc.value == null ? "" : String(doc.value);
25
+ const count = typeof doc.count === "number" ? doc.count : 0;
26
+ if (value === "") total += count;
27
+ else counts.set(value, (counts.get(value) ?? 0) + count);
28
+ }
29
+ total = Math.max(0, total);
30
+ const optionValues = new Set(options.map((option) => option.value));
31
+ const leftovers = [...counts.keys()].filter((value) => !optionValues.has(value)).sort((a, b) => (counts.get(b) ?? 0) - (counts.get(a) ?? 0));
32
+ const labelByValue = new Map(options.map((option) => [option.value, option.label]));
33
+ const buckets = [...options.map((option) => option.value), ...leftovers].map((value) => {
34
+ const count = Math.max(0, counts.get(value) ?? 0);
35
+ return {
36
+ value,
37
+ label: labelByValue.get(value) ?? value,
38
+ count,
39
+ percentage: total > 0 ? round1(count / total * 100) : 0
40
+ };
41
+ });
42
+ return {
43
+ field,
44
+ label: meta?.label,
45
+ fieldType: meta?.fieldType,
46
+ total,
47
+ buckets,
48
+ truncated: false
49
+ };
50
+ };
51
+ //#endregion
52
+ export { aggregateFromVotes };
53
+
54
+ //# sourceMappingURL=aggregateFromVotes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregateFromVotes.js","names":[],"sources":["../../../src/poll/votes/aggregateFromVotes.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport type { FieldAggregation } from '../../aggregation/types'\nimport { POLL_VOTES_SLUG, RESPONDENTS_VALUE } from './votesCollection'\n\ntype VoteRow = { field?: unknown; value?: unknown; count?: unknown }\n\nconst round1 = (n: number): number => Math.round(n * 10) / 10\n\n/**\n * Poll results from the tally store: one find over the form's rows for the field (O(options),\n * no scan, no truncation), buckets zero-seeded from the effective options so order and labels\n * match the scan-based shape. Counts sum across shard rows (see VOTE_SHARDS). `total` comes\n * from the respondents rows, keeping the percentage denominator exact for multi-value answers.\n */\nexport const aggregateFromVotes = async (args: {\n\tpayload: Payload\n\tformId: number | string\n\tfield: string\n\tmeta?: { label?: string; fieldType?: string }\n\toptions: { value: string; label: string }[]\n\treq?: PayloadRequest\n}): Promise<FieldAggregation> => {\n\tconst { payload, formId, field, meta, options, req } = args\n\tconst { docs } = await payload.find({\n\t\tcollection: POLL_VOTES_SLUG,\n\t\twhere: { and: [{ form: { equals: String(formId) } }, { field: { equals: field } }] },\n\t\tlimit: 100_000,\n\t\tpagination: false,\n\t\tdepth: 0,\n\t\toverrideAccess: true,\n\t\treq,\n\t})\n\n\tconst counts = new Map<string, number>()\n\tlet total = 0\n\tfor (const doc of docs as VoteRow[]) {\n\t\tconst value = doc.value == null ? RESPONDENTS_VALUE : String(doc.value)\n\t\tconst count = typeof doc.count === 'number' ? doc.count : 0\n\t\tif (value === RESPONDENTS_VALUE) {\n\t\t\ttotal += count\n\t\t} else {\n\t\t\tcounts.set(value, (counts.get(value) ?? 0) + count)\n\t\t}\n\t}\n\n\t// Clamp at read: a concurrent double-change can transiently double-decrement one value (see\n\t// the tally hook's drift note), and a negative bucket count must never surface to a reader.\n\ttotal = Math.max(0, total)\n\n\tconst optionValues = new Set(options.map((option) => option.value))\n\tconst leftovers = [...counts.keys()]\n\t\t.filter((value) => !optionValues.has(value))\n\t\t.sort((a, b) => (counts.get(b) ?? 0) - (counts.get(a) ?? 0))\n\tconst labelByValue = new Map(options.map((option) => [option.value, option.label]))\n\n\tconst buckets = [...options.map((option) => option.value), ...leftovers].map((value) => {\n\t\tconst count = Math.max(0, counts.get(value) ?? 0)\n\t\treturn {\n\t\t\tvalue,\n\t\t\tlabel: labelByValue.get(value) ?? value,\n\t\t\tcount,\n\t\t\tpercentage: total > 0 ? round1((count / total) * 100) : 0,\n\t\t}\n\t})\n\n\treturn {\n\t\tfield,\n\t\tlabel: meta?.label,\n\t\tfieldType: meta?.fieldType,\n\t\ttotal,\n\t\tbuckets,\n\t\ttruncated: false,\n\t}\n}\n"],"mappings":";;AAMA,MAAM,UAAU,MAAsB,KAAK,MAAM,IAAI,EAAE,IAAI;;;;;;;AAQ3D,MAAa,qBAAqB,OAAO,SAOR;CAChC,MAAM,EAAE,SAAS,QAAQ,OAAO,MAAM,SAAS,QAAQ;CACvD,MAAM,EAAE,SAAS,MAAM,QAAQ,KAAK;EACnC,YAAY;EACZ,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,OAAO,MAAM,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,MAAM,EAAE,CAAC,EAAE;EACnF,OAAO;EACP,YAAY;EACZ,OAAO;EACP,gBAAgB;EAChB;CACD,CAAC;CAED,MAAM,yBAAS,IAAI,IAAoB;CACvC,IAAI,QAAQ;CACZ,KAAK,MAAM,OAAO,MAAmB;EACpC,MAAM,QAAQ,IAAI,SAAS,OAAA,KAA2B,OAAO,IAAI,KAAK;EACtE,MAAM,QAAQ,OAAO,IAAI,UAAU,WAAW,IAAI,QAAQ;EAC1D,IAAI,UAAA,IACH,SAAS;OAET,OAAO,IAAI,QAAQ,OAAO,IAAI,KAAK,KAAK,KAAK,KAAK;CAEpD;CAIA,QAAQ,KAAK,IAAI,GAAG,KAAK;CAEzB,MAAM,eAAe,IAAI,IAAI,QAAQ,KAAK,WAAW,OAAO,KAAK,CAAC;CAClE,MAAM,YAAY,CAAC,GAAG,OAAO,KAAK,CAAC,EACjC,QAAQ,UAAU,CAAC,aAAa,IAAI,KAAK,CAAC,EAC1C,MAAM,GAAG,OAAO,OAAO,IAAI,CAAC,KAAK,MAAM,OAAO,IAAI,CAAC,KAAK,EAAE;CAC5D,MAAM,eAAe,IAAI,IAAI,QAAQ,KAAK,WAAW,CAAC,OAAO,OAAO,OAAO,KAAK,CAAC,CAAC;CAElF,MAAM,UAAU,CAAC,GAAG,QAAQ,KAAK,WAAW,OAAO,KAAK,GAAG,GAAG,SAAS,EAAE,KAAK,UAAU;EACvF,MAAM,QAAQ,KAAK,IAAI,GAAG,OAAO,IAAI,KAAK,KAAK,CAAC;EAChD,OAAO;GACN;GACA,OAAO,aAAa,IAAI,KAAK,KAAK;GAClC;GACA,YAAY,QAAQ,IAAI,OAAQ,QAAQ,QAAS,GAAG,IAAI;EACzD;CACD,CAAC;CAED,OAAO;EACN;EACA,OAAO,MAAM;EACb,WAAW,MAAM;EACjB;EACA;EACA,WAAW;CACZ;AACD"}
@@ -0,0 +1,16 @@
1
+ //#region src/poll/votes/answerValues.ts
2
+ /**
3
+ * A submission's answer(s) for one field as normalized non-empty strings: the shape both the vote
4
+ * tally and the voted-pick resolver count by (a multi-select answer contributes one entry per
5
+ * selected value).
6
+ */
7
+ const answerValues = (values, field) => {
8
+ if (!Array.isArray(values)) return [];
9
+ const entry = values.find((row) => row.field === field);
10
+ if (entry == null) return [];
11
+ return (Array.isArray(entry.value) ? entry.value : [entry.value]).filter((value) => value != null && value !== "").map((value) => String(value));
12
+ };
13
+ //#endregion
14
+ export { answerValues };
15
+
16
+ //# sourceMappingURL=answerValues.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"answerValues.js","names":[],"sources":["../../../src/poll/votes/answerValues.ts"],"sourcesContent":["import type { SubmissionValue } from '../../submissions/types'\n\n/**\n * A submission's answer(s) for one field as normalized non-empty strings: the shape both the vote\n * tally and the voted-pick resolver count by (a multi-select answer contributes one entry per\n * selected value).\n */\nexport const answerValues = (values: unknown, field: string): string[] => {\n\tif (!Array.isArray(values)) return []\n\tconst entry = (values as SubmissionValue[]).find((row) => row.field === field)\n\tif (entry == null) return []\n\tconst raw = Array.isArray(entry.value) ? entry.value : [entry.value]\n\treturn raw.filter((value) => value != null && value !== '').map((value) => String(value))\n}\n"],"mappings":";;;;;;AAOA,MAAa,gBAAgB,QAAiB,UAA4B;CACzE,IAAI,CAAC,MAAM,QAAQ,MAAM,GAAG,OAAO,CAAC;CACpC,MAAM,QAAS,OAA6B,MAAM,QAAQ,IAAI,UAAU,KAAK;CAC7E,IAAI,SAAS,MAAM,OAAO,CAAC;CAE3B,QADY,MAAM,QAAQ,MAAM,KAAK,IAAI,MAAM,QAAQ,CAAC,MAAM,KAAK,GACxD,QAAQ,UAAU,SAAS,QAAQ,UAAU,EAAE,EAAE,KAAK,UAAU,OAAO,KAAK,CAAC;AACzF"}
@@ -0,0 +1,88 @@
1
+ import { POLL_VOTES_SLUG } from "./votesCollection.js";
2
+ //#region src/poll/votes/bumpPollVote.ts
3
+ const PG_TABLE_KEY = "form_poll_votes";
4
+ /**
5
+ * Loads the optional Postgres peer without a literal specifier: bundlers (Turbopack, webpack,
6
+ * Vite) resolve literal dynamic imports at build time, which fails a Mongo host's build even
7
+ * though this branch is unreachable there. The function-built specifier is opaque to their
8
+ * analyzers, the ignore comments cover bundlers that would still warn, and the type-only
9
+ * reference above erases at compile time, so the package is touched only when the Postgres
10
+ * branch actually runs.
11
+ */
12
+ const importPostgresSql = () => {
13
+ return import(
14
+ /* webpackIgnore: true */
15
+ /* turbopackIgnore: true */
16
+ /* @vite-ignore */
17
+ ["@payloadcms", "db-postgres"].join("/")
18
+ );
19
+ };
20
+ /**
21
+ * Atomically bumps one (form, field, value) tally by `by` in a single upsert-increment
22
+ * statement (Mongo `$inc` + upsert, Postgres `INSERT ... ON CONFLICT DO UPDATE`); the unique
23
+ * compound index makes concurrent bumps for the same key safe without read-modify-write races.
24
+ * The shard column is internal: transactional Mongo bumps pick a random shard in
25
+ * [0, VOTE_SHARDS) so concurrent transactions rarely write the same document (see VOTE_SHARDS);
26
+ * Postgres and non-transactional Mongo always bump shard 0. Readers sum across shards.
27
+ *
28
+ * When `transactionID` names an open Payload transaction, the write joins it: a bump failure
29
+ * thrown from the submission hook rolls back the submission create (no undercount), and an
30
+ * aborted create rolls back the joined bump (no overcount). Residual risk on transactional
31
+ * Mongo: two concurrent bumps that land on the same shard still abort one transaction
32
+ * (WriteConflict, labelled TransientTransactionError); the losing submission rolls back whole,
33
+ * so counts stay consistent and the client can safely resubmit. Without a transaction (e.g.
34
+ * Mongo with transactions disabled) the write lands on the root handle immediately; a later
35
+ * recount from stored submissions is the healer for any drift that window allows.
36
+ */
37
+ async function bumpPollVote(payload, key, by, transactionID) {
38
+ if (payload.db.name === "mongoose") {
39
+ const db = payload.db;
40
+ const model = db.collections[POLL_VOTES_SLUG];
41
+ if (!model) throw new Error(`form-builder: mongoose collection "${POLL_VOTES_SLUG}" not found`);
42
+ const session = transactionID !== void 0 ? db.sessions?.[transactionID] : void 0;
43
+ const shard = session ? Math.floor(Math.random() * 8) : 0;
44
+ const shardedKey = {
45
+ ...key,
46
+ shard
47
+ };
48
+ const update = {
49
+ $inc: { count: by },
50
+ $setOnInsert: shardedKey
51
+ };
52
+ const options = session ? {
53
+ upsert: true,
54
+ session
55
+ } : { upsert: true };
56
+ try {
57
+ await model.collection.updateOne(shardedKey, update, options);
58
+ } catch (error) {
59
+ const duplicate = error?.code === 11e3;
60
+ if (session || !duplicate) throw error;
61
+ await model.collection.updateOne(shardedKey, update, options);
62
+ }
63
+ return;
64
+ }
65
+ const { sql } = await importPostgresSql();
66
+ const db = payload.db;
67
+ const tableName = db.tableNameMap.get(PG_TABLE_KEY);
68
+ if (!tableName) throw new Error(`form-builder: drizzle table "${PG_TABLE_KEY}" not found`);
69
+ const table = db.tables[tableName];
70
+ if (!table) throw new Error(`form-builder: drizzle table object for "${tableName}" not found`);
71
+ await ((transactionID !== void 0 ? db.sessions?.[transactionID]?.db : void 0) ?? db.drizzle).insert(table).values({
72
+ ...key,
73
+ shard: 0,
74
+ count: by
75
+ }).onConflictDoUpdate({
76
+ target: [
77
+ table.form,
78
+ table.field,
79
+ table.value,
80
+ table.shard
81
+ ],
82
+ set: { count: sql`${table.count} + ${by}` }
83
+ });
84
+ }
85
+ //#endregion
86
+ export { bumpPollVote };
87
+
88
+ //# sourceMappingURL=bumpPollVote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bumpPollVote.js","names":[],"sources":["../../../src/poll/votes/bumpPollVote.ts"],"sourcesContent":["import type { Payload } from 'payload'\nimport { POLL_VOTES_SLUG, VOTE_SHARDS } from './votesCollection'\n\nconst PG_TABLE_KEY = 'form_poll_votes'\n\ntype PostgresSqlModule = { sql: typeof import('@payloadcms/db-postgres')['sql'] }\n\n/**\n * Loads the optional Postgres peer without a literal specifier: bundlers (Turbopack, webpack,\n * Vite) resolve literal dynamic imports at build time, which fails a Mongo host's build even\n * though this branch is unreachable there. The function-built specifier is opaque to their\n * analyzers, the ignore comments cover bundlers that would still warn, and the type-only\n * reference above erases at compile time, so the package is touched only when the Postgres\n * branch actually runs.\n */\nconst importPostgresSql = (): Promise<PostgresSqlModule> => {\n\tconst specifier = ['@payloadcms', 'db-postgres'].join('/')\n\treturn import(\n\t\t/* webpackIgnore: true */ /* turbopackIgnore: true */ /* @vite-ignore */ specifier\n\t) as Promise<PostgresSqlModule>\n}\n\n// payload.db raw-access shapes are intentionally loose; these narrow casts reach the\n// Mongoose driver collection / Drizzle instance for atomic upserts (no public typed API).\ntype MongoDb = {\n\tname: 'mongoose'\n\tcollections: Record<\n\t\tstring,\n\t\t{ collection: { updateOne: (f: object, u: object, o: object) => Promise<unknown> } }\n\t>\n\tsessions?: Record<number | string, unknown>\n}\ntype PgInsert = {\n\tinsert: (t: unknown) => {\n\t\tvalues: (v: unknown) => { onConflictDoUpdate: (c: unknown) => Promise<unknown> }\n\t}\n}\ntype PgDb = {\n\tname: 'postgres'\n\tdrizzle: PgInsert\n\tsessions?: Record<number | string, { db: PgInsert }>\n\ttables: Record<string, Record<string, unknown>>\n\ttableNameMap: Map<string, string>\n}\n\n/**\n * Atomically bumps one (form, field, value) tally by `by` in a single upsert-increment\n * statement (Mongo `$inc` + upsert, Postgres `INSERT ... ON CONFLICT DO UPDATE`); the unique\n * compound index makes concurrent bumps for the same key safe without read-modify-write races.\n * The shard column is internal: transactional Mongo bumps pick a random shard in\n * [0, VOTE_SHARDS) so concurrent transactions rarely write the same document (see VOTE_SHARDS);\n * Postgres and non-transactional Mongo always bump shard 0. Readers sum across shards.\n *\n * When `transactionID` names an open Payload transaction, the write joins it: a bump failure\n * thrown from the submission hook rolls back the submission create (no undercount), and an\n * aborted create rolls back the joined bump (no overcount). Residual risk on transactional\n * Mongo: two concurrent bumps that land on the same shard still abort one transaction\n * (WriteConflict, labelled TransientTransactionError); the losing submission rolls back whole,\n * so counts stay consistent and the client can safely resubmit. Without a transaction (e.g.\n * Mongo with transactions disabled) the write lands on the root handle immediately; a later\n * recount from stored submissions is the healer for any drift that window allows.\n */\n// biome-ignore lint/complexity/useMaxParams: write primitive signature (payload, key, by, transactionID)\nexport async function bumpPollVote(\n\tpayload: Payload,\n\tkey: { form: string; field: string; value: string },\n\tby: number,\n\ttransactionID?: number | string\n): Promise<void> {\n\tif (payload.db.name === 'mongoose') {\n\t\tconst db = payload.db as unknown as MongoDb\n\t\tconst model = db.collections[POLL_VOTES_SLUG]\n\t\tif (!model) throw new Error(`form-builder: mongoose collection \"${POLL_VOTES_SLUG}\" not found`)\n\t\tconst session = transactionID !== undefined ? db.sessions?.[transactionID] : undefined\n\t\tconst shard = session ? Math.floor(Math.random() * VOTE_SHARDS) : 0\n\t\tconst shardedKey = { ...key, shard }\n\t\tconst update = { $inc: { count: by }, $setOnInsert: shardedKey }\n\t\tconst options = session ? { upsert: true, session } : { upsert: true }\n\t\ttry {\n\t\t\tawait model.collection.updateOne(shardedKey, update, options)\n\t\t} catch (error) {\n\t\t\t// Concurrent first inserts for a new key can race the upsert into E11000; outside a\n\t\t\t// transaction the row now exists, so one retry takes the $inc branch. Inside a\n\t\t\t// transaction the error propagates and Payload's rollback/retry semantics apply.\n\t\t\tconst duplicate = (error as { code?: unknown } | null)?.code === 11000\n\t\t\tif (session || !duplicate) throw error\n\t\t\tawait model.collection.updateOne(shardedKey, update, options)\n\t\t}\n\t\treturn\n\t}\n\tconst { sql } = await importPostgresSql()\n\tconst db = payload.db as unknown as PgDb\n\tconst tableName = db.tableNameMap.get(PG_TABLE_KEY)\n\tif (!tableName) throw new Error(`form-builder: drizzle table \"${PG_TABLE_KEY}\" not found`)\n\tconst table = db.tables[tableName]\n\tif (!table) throw new Error(`form-builder: drizzle table object for \"${tableName}\" not found`)\n\tconst txn = transactionID !== undefined ? db.sessions?.[transactionID]?.db : undefined\n\tawait (txn ?? db.drizzle)\n\t\t.insert(table)\n\t\t.values({ ...key, shard: 0, count: by })\n\t\t.onConflictDoUpdate({\n\t\t\ttarget: [table.form, table.field, table.value, table.shard],\n\t\t\tset: { count: sql`${table.count} + ${by}` },\n\t\t})\n}\n"],"mappings":";;AAGA,MAAM,eAAe;;;;;;;;;AAYrB,MAAM,0BAAsD;CAE3D,OAAO;;;;EADW,CAAC,eAAe,aAAa,EAAE,KAAK,GAE4B;;AAEnF;;;;;;;;;;;;;;;;;;AA2CA,eAAsB,aACrB,SACA,KACA,IACA,eACgB;CAChB,IAAI,QAAQ,GAAG,SAAS,YAAY;EACnC,MAAM,KAAK,QAAQ;EACnB,MAAM,QAAQ,GAAG,YAAY;EAC7B,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,sCAAsC,gBAAgB,YAAY;EAC9F,MAAM,UAAU,kBAAkB,KAAA,IAAY,GAAG,WAAW,iBAAiB,KAAA;EAC7E,MAAM,QAAQ,UAAU,KAAK,MAAM,KAAK,OAAO,IAAA,CAAe,IAAI;EAClE,MAAM,aAAa;GAAE,GAAG;GAAK;EAAM;EACnC,MAAM,SAAS;GAAE,MAAM,EAAE,OAAO,GAAG;GAAG,cAAc;EAAW;EAC/D,MAAM,UAAU,UAAU;GAAE,QAAQ;GAAM;EAAQ,IAAI,EAAE,QAAQ,KAAK;EACrE,IAAI;GACH,MAAM,MAAM,WAAW,UAAU,YAAY,QAAQ,OAAO;EAC7D,SAAS,OAAO;GAIf,MAAM,YAAa,OAAqC,SAAS;GACjE,IAAI,WAAW,CAAC,WAAW,MAAM;GACjC,MAAM,MAAM,WAAW,UAAU,YAAY,QAAQ,OAAO;EAC7D;EACA;CACD;CACA,MAAM,EAAE,QAAQ,MAAM,kBAAkB;CACxC,MAAM,KAAK,QAAQ;CACnB,MAAM,YAAY,GAAG,aAAa,IAAI,YAAY;CAClD,IAAI,CAAC,WAAW,MAAM,IAAI,MAAM,gCAAgC,aAAa,YAAY;CACzF,MAAM,QAAQ,GAAG,OAAO;CACxB,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,2CAA2C,UAAU,YAAY;CAE7F,QADY,kBAAkB,KAAA,IAAY,GAAG,WAAW,gBAAgB,KAAK,KAAA,MAC/D,GAAG,SACf,OAAO,KAAK,EACZ,OAAO;EAAE,GAAG;EAAK,OAAO;EAAG,OAAO;CAAG,CAAC,EACtC,mBAAmB;EACnB,QAAQ;GAAC,MAAM;GAAM,MAAM;GAAO,MAAM;GAAO,MAAM;EAAK;EAC1D,KAAK,EAAE,OAAO,GAAG,GAAG,MAAM,MAAM,KAAK,KAAK;CAC3C,CAAC;AACH"}
@@ -0,0 +1,20 @@
1
+ import { Payload, PayloadRequest } from "payload";
2
+
3
+ //#region src/poll/votes/recountPollVotes.d.ts
4
+ /**
5
+ * Rebuild a form's tally rows from persisted submissions (drift healing after out-of-band DB
6
+ * changes, or backfill after enabling a poll late). Scans first and refuses a truncated
7
+ * aggregation, then deletes the form's tally rows for the results field and replays the scan
8
+ * into fresh rows, so the store is never left holding a knowingly partial count. Writes join
9
+ * the request's transaction when one is open. Refuses a form with `persistSubmissions` off:
10
+ * its submissions are pruned, so the tally store is the only record of its votes and a rebuild
11
+ * would wipe them.
12
+ */
13
+ declare const recountPollVotes: (args: {
14
+ payload: Payload;
15
+ formId: number | string;
16
+ req?: PayloadRequest;
17
+ }) => Promise<void>;
18
+ //#endregion
19
+ export { recountPollVotes };
20
+ //# sourceMappingURL=recountPollVotes.d.ts.map
@@ -0,0 +1,63 @@
1
+ import { pollConfigOf } from "../../form/pollState.js";
2
+ import { aggregateFieldResponses } from "../../aggregation/aggregateResponses.js";
3
+ import { POLL_VOTES_SLUG } from "./votesCollection.js";
4
+ import { FORMS_SLUG } from "../../collections/forms.js";
5
+ import { bumpPollVote } from "./bumpPollVote.js";
6
+ import { transactionIDOf } from "./voteTallyHook.js";
7
+ //#region src/poll/votes/recountPollVotes.ts
8
+ const RECOUNT_MAX_SUBMISSIONS = 1e5;
9
+ /**
10
+ * Rebuild a form's tally rows from persisted submissions (drift healing after out-of-band DB
11
+ * changes, or backfill after enabling a poll late). Scans first and refuses a truncated
12
+ * aggregation, then deletes the form's tally rows for the results field and replays the scan
13
+ * into fresh rows, so the store is never left holding a knowingly partial count. Writes join
14
+ * the request's transaction when one is open. Refuses a form with `persistSubmissions` off:
15
+ * its submissions are pruned, so the tally store is the only record of its votes and a rebuild
16
+ * would wipe them.
17
+ */
18
+ const recountPollVotes = async (args) => {
19
+ const { payload, formId, req } = args;
20
+ const form = await payload.findByID({
21
+ collection: FORMS_SLUG,
22
+ id: formId,
23
+ depth: 0,
24
+ overrideAccess: true,
25
+ req
26
+ }).catch(() => null);
27
+ const poll = form ? pollConfigOf(form.poll) : void 0;
28
+ const resultsField = typeof poll?.resultsField === "string" && poll.resultsField.length > 0 ? poll.resultsField : void 0;
29
+ if (!resultsField) return;
30
+ if (form?.persistSubmissions === false) throw new Error(`form-builder: recountPollVotes cannot rebuild form "${String(formId)}": it does not persist submissions, so the tally store is the only record of its votes.`);
31
+ const aggregation = await aggregateFieldResponses({
32
+ payload,
33
+ formId,
34
+ field: resultsField,
35
+ req,
36
+ maxSubmissions: RECOUNT_MAX_SUBMISSIONS
37
+ });
38
+ if (aggregation?.truncated) throw new Error(`form-builder: recount for form ${String(formId)} aborted: more than ${RECOUNT_MAX_SUBMISSIONS} submissions, the scan was truncated and a rebuilt tally would be partial.`);
39
+ await payload.delete({
40
+ collection: POLL_VOTES_SLUG,
41
+ where: { and: [{ form: { equals: String(formId) } }, { field: { equals: resultsField } }] },
42
+ overrideAccess: true,
43
+ req
44
+ });
45
+ if (!aggregation) return;
46
+ const transactionID = transactionIDOf(req?.transactionID);
47
+ const base = {
48
+ form: String(formId),
49
+ field: resultsField
50
+ };
51
+ for (const bucket of aggregation.buckets) if (bucket.count > 0) await bumpPollVote(payload, {
52
+ ...base,
53
+ value: bucket.value
54
+ }, bucket.count, transactionID);
55
+ if (aggregation.total > 0) await bumpPollVote(payload, {
56
+ ...base,
57
+ value: ""
58
+ }, aggregation.total, transactionID);
59
+ };
60
+ //#endregion
61
+ export { recountPollVotes };
62
+
63
+ //# sourceMappingURL=recountPollVotes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recountPollVotes.js","names":[],"sources":["../../../src/poll/votes/recountPollVotes.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport { aggregateFieldResponses } from '../../aggregation/aggregateResponses'\nimport { FORMS_SLUG } from '../../collections/forms'\nimport { pollConfigOf } from '../../form/pollState'\nimport { bumpPollVote } from './bumpPollVote'\nimport { POLL_VOTES_SLUG, RESPONDENTS_VALUE } from './votesCollection'\nimport { transactionIDOf } from './voteTallyHook'\n\nconst RECOUNT_MAX_SUBMISSIONS = 100_000\n\n/**\n * Rebuild a form's tally rows from persisted submissions (drift healing after out-of-band DB\n * changes, or backfill after enabling a poll late). Scans first and refuses a truncated\n * aggregation, then deletes the form's tally rows for the results field and replays the scan\n * into fresh rows, so the store is never left holding a knowingly partial count. Writes join\n * the request's transaction when one is open. Refuses a form with `persistSubmissions` off:\n * its submissions are pruned, so the tally store is the only record of its votes and a rebuild\n * would wipe them.\n */\nexport const recountPollVotes = async (args: {\n\tpayload: Payload\n\tformId: number | string\n\treq?: PayloadRequest\n}): Promise<void> => {\n\tconst { payload, formId, req } = args\n\tconst form = await payload\n\t\t.findByID({ collection: FORMS_SLUG, id: formId, depth: 0, overrideAccess: true, req })\n\t\t.catch(() => null)\n\tconst poll = form ? pollConfigOf(form.poll) : undefined\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\tif (form?.persistSubmissions === false) {\n\t\tthrow new Error(\n\t\t\t`form-builder: recountPollVotes cannot rebuild form \"${String(formId)}\": it does not persist submissions, so the tally store is the only record of its votes.`\n\t\t)\n\t}\n\tconst aggregation = await aggregateFieldResponses({\n\t\tpayload,\n\t\tformId,\n\t\tfield: resultsField,\n\t\treq,\n\t\tmaxSubmissions: RECOUNT_MAX_SUBMISSIONS,\n\t})\n\tif (aggregation?.truncated) {\n\t\tthrow new Error(\n\t\t\t`form-builder: recount for form ${String(formId)} aborted: more than ${RECOUNT_MAX_SUBMISSIONS} submissions, the scan was truncated and a rebuilt tally would be partial.`\n\t\t)\n\t}\n\tawait payload.delete({\n\t\tcollection: POLL_VOTES_SLUG,\n\t\twhere: {\n\t\t\tand: [{ form: { equals: String(formId) } }, { field: { equals: resultsField } }],\n\t\t},\n\t\toverrideAccess: true,\n\t\treq,\n\t})\n\tif (!aggregation) {\n\t\treturn\n\t}\n\tconst transactionID = transactionIDOf(req?.transactionID)\n\tconst base = { form: String(formId), field: resultsField }\n\tfor (const bucket of aggregation.buckets) {\n\t\tif (bucket.count > 0) {\n\t\t\tawait bumpPollVote(payload, { ...base, value: bucket.value }, bucket.count, transactionID)\n\t\t}\n\t}\n\tif (aggregation.total > 0) {\n\t\tawait bumpPollVote(\n\t\t\tpayload,\n\t\t\t{ ...base, value: RESPONDENTS_VALUE },\n\t\t\taggregation.total,\n\t\t\ttransactionID\n\t\t)\n\t}\n}\n"],"mappings":";;;;;;;AAQA,MAAM,0BAA0B;;;;;;;;;;AAWhC,MAAa,mBAAmB,OAAO,SAIlB;CACpB,MAAM,EAAE,SAAS,QAAQ,QAAQ;CACjC,MAAM,OAAO,MAAM,QACjB,SAAS;EAAE,YAAY;EAAY,IAAI;EAAQ,OAAO;EAAG,gBAAgB;EAAM;CAAI,CAAC,EACpF,YAAY,IAAI;CAClB,MAAM,OAAO,OAAO,aAAa,KAAK,IAAI,IAAI,KAAA;CAC9C,MAAM,eACL,OAAO,MAAM,iBAAiB,YAAY,KAAK,aAAa,SAAS,IAClE,KAAK,eACL,KAAA;CACJ,IAAI,CAAC,cACJ;CAED,IAAI,MAAM,uBAAuB,OAChC,MAAM,IAAI,MACT,uDAAuD,OAAO,MAAM,EAAE,wFACvE;CAED,MAAM,cAAc,MAAM,wBAAwB;EACjD;EACA;EACA,OAAO;EACP;EACA,gBAAgB;CACjB,CAAC;CACD,IAAI,aAAa,WAChB,MAAM,IAAI,MACT,kCAAkC,OAAO,MAAM,EAAE,sBAAsB,wBAAwB,2EAChG;CAED,MAAM,QAAQ,OAAO;EACpB,YAAY;EACZ,OAAO,EACN,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,OAAO,MAAM,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,aAAa,EAAE,CAAC,EAChF;EACA,gBAAgB;EAChB;CACD,CAAC;CACD,IAAI,CAAC,aACJ;CAED,MAAM,gBAAgB,gBAAgB,KAAK,aAAa;CACxD,MAAM,OAAO;EAAE,MAAM,OAAO,MAAM;EAAG,OAAO;CAAa;CACzD,KAAK,MAAM,UAAU,YAAY,SAChC,IAAI,OAAO,QAAQ,GAClB,MAAM,aAAa,SAAS;EAAE,GAAG;EAAM,OAAO,OAAO;CAAM,GAAG,OAAO,OAAO,aAAa;CAG3F,IAAI,YAAY,QAAQ,GACvB,MAAM,aACL,SACA;EAAE,GAAG;EAAM,OAAA;CAAyB,GACpC,YAAY,OACZ,aACD;AAEF"}
@@ -0,0 +1,76 @@
1
+ import { pollConfigOf } from "../../form/pollState.js";
2
+ import "./votesCollection.js";
3
+ import { FORMS_SLUG } from "../../collections/forms.js";
4
+ import { formIdOf } from "../../submissions/formIdOf.js";
5
+ import { answerValues } from "./answerValues.js";
6
+ import { bumpPollVote } from "./bumpPollVote.js";
7
+ //#region src/poll/votes/voteTallyHook.ts
8
+ const isComplete = (doc) => doc.status == null || doc.status === "complete";
9
+ /** Narrow a request's loosely typed `transactionID` to the value `bumpPollVote` accepts. */
10
+ const transactionIDOf = (transactionID) => typeof transactionID === "number" || typeof transactionID === "string" ? transactionID : void 0;
11
+ /**
12
+ * Vote counting: a submission entering `complete` bumps one tally per answer value for the poll
13
+ * results field, plus the shared respondents row. Votes are permanent by default: no decrement on
14
+ * delete, edit, or prune, which is what lets a pruned submission's vote survive. The one
15
+ * exception is an `allowChange` poll, where an update of an already-complete submission moves the
16
+ * tally with the changed answer (decrement removed values, increment added ones, respondents
17
+ * untouched); delete and prune stay permanent even then. Two concurrent changes of the same
18
+ * submission can each read the same previous answer and double-adjust; the client's double-submit
19
+ * guard makes that rare and `recountPollVotes` is the healer, matching `bumpPollVote`'s residual
20
+ * drift note. Bump failures propagate so the enclosing transaction rolls the submission back
21
+ * rather than silently undercounting; registered before the swallow-all dispatch hook.
22
+ */
23
+ const makeVoteTallyHook = () => async ({ doc, previousDoc, operation, req }) => {
24
+ if (operation !== "create" && operation !== "update") return doc;
25
+ const entering = operation === "create" && isComplete(doc) || operation === "update" && isComplete(doc) && !isComplete(previousDoc ?? {});
26
+ const changing = operation === "update" && isComplete(doc) && isComplete(previousDoc ?? {}) && !entering;
27
+ if (!entering && !changing) return doc;
28
+ const formId = formIdOf(doc.form);
29
+ if (formId == null) return doc;
30
+ const form = await req.payload.findByID({
31
+ collection: FORMS_SLUG,
32
+ id: formId,
33
+ depth: 0,
34
+ overrideAccess: true,
35
+ req
36
+ }).catch(() => null);
37
+ const poll = form?.pollEnabled === true ? pollConfigOf(form.poll) : void 0;
38
+ const resultsField = typeof poll?.resultsField === "string" && poll.resultsField.length > 0 ? poll.resultsField : void 0;
39
+ if (!resultsField) return doc;
40
+ const transactionID = transactionIDOf(req.transactionID);
41
+ const base = {
42
+ form: String(formId),
43
+ field: resultsField
44
+ };
45
+ if (changing) {
46
+ if (poll?.allowChange !== true) return doc;
47
+ const previous = answerValues(previousDoc?.values, resultsField);
48
+ const next = answerValues(doc.values, resultsField);
49
+ const removed = previous.filter((value) => !next.includes(value));
50
+ const added = next.filter((value) => !previous.includes(value));
51
+ for (const value of removed) await bumpPollVote(req.payload, {
52
+ ...base,
53
+ value
54
+ }, -1, transactionID);
55
+ for (const value of added) await bumpPollVote(req.payload, {
56
+ ...base,
57
+ value
58
+ }, 1, transactionID);
59
+ return doc;
60
+ }
61
+ const votes = answerValues(doc.values, resultsField);
62
+ if (votes.length === 0) return doc;
63
+ for (const value of votes) await bumpPollVote(req.payload, {
64
+ ...base,
65
+ value
66
+ }, 1, transactionID);
67
+ await bumpPollVote(req.payload, {
68
+ ...base,
69
+ value: ""
70
+ }, 1, transactionID);
71
+ return doc;
72
+ };
73
+ //#endregion
74
+ export { makeVoteTallyHook, transactionIDOf };
75
+
76
+ //# sourceMappingURL=voteTallyHook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voteTallyHook.js","names":[],"sources":["../../../src/poll/votes/voteTallyHook.ts"],"sourcesContent":["import type { CollectionAfterChangeHook } from 'payload'\nimport { FORMS_SLUG } from '../../collections/forms'\nimport { pollConfigOf } from '../../form/pollState'\nimport { formIdOf } from '../../submissions/formIdOf'\nimport { answerValues } from './answerValues'\nimport { bumpPollVote } from './bumpPollVote'\nimport { RESPONDENTS_VALUE } from './votesCollection'\n\nconst isComplete = (doc: { status?: unknown }): boolean =>\n\tdoc.status == null || doc.status === 'complete'\n\n/** Narrow a request's loosely typed `transactionID` to the value `bumpPollVote` accepts. */\nexport const transactionIDOf = (transactionID: unknown): number | string | undefined =>\n\ttypeof transactionID === 'number' || typeof transactionID === 'string' ? transactionID : undefined\n\n/**\n * Vote counting: a submission entering `complete` bumps one tally per answer value for the poll\n * results field, plus the shared respondents row. Votes are permanent by default: no decrement on\n * delete, edit, or prune, which is what lets a pruned submission's vote survive. The one\n * exception is an `allowChange` poll, where an update of an already-complete submission moves the\n * tally with the changed answer (decrement removed values, increment added ones, respondents\n * untouched); delete and prune stay permanent even then. Two concurrent changes of the same\n * submission can each read the same previous answer and double-adjust; the client's double-submit\n * guard makes that rare and `recountPollVotes` is the healer, matching `bumpPollVote`'s residual\n * drift note. Bump failures propagate so the enclosing transaction rolls the submission back\n * rather than silently undercounting; registered before the swallow-all dispatch hook.\n */\nexport const makeVoteTallyHook =\n\t(): CollectionAfterChangeHook =>\n\tasync ({ doc, previousDoc, operation, req }) => {\n\t\tif (operation !== 'create' && operation !== 'update') return doc\n\t\tconst entering =\n\t\t\t(operation === 'create' && isComplete(doc)) ||\n\t\t\t(operation === 'update' && isComplete(doc) && !isComplete(previousDoc ?? {}))\n\t\tconst changing =\n\t\t\toperation === 'update' && isComplete(doc) && isComplete(previousDoc ?? {}) && !entering\n\t\tif (!entering && !changing) return doc\n\t\tconst formId = formIdOf(doc.form)\n\t\tif (formId == null) return doc\n\t\tconst form = await req.payload\n\t\t\t.findByID({ collection: FORMS_SLUG, id: formId, depth: 0, overrideAccess: true, req })\n\t\t\t.catch(() => null)\n\t\tconst poll = form?.pollEnabled === true ? pollConfigOf(form.poll) : undefined\n\t\tconst resultsField =\n\t\t\ttypeof poll?.resultsField === 'string' && poll.resultsField.length > 0\n\t\t\t\t? poll.resultsField\n\t\t\t\t: undefined\n\t\tif (!resultsField) return doc\n\t\tconst transactionID = transactionIDOf(req.transactionID)\n\t\tconst base = { form: String(formId), field: resultsField }\n\t\tif (changing) {\n\t\t\tif (poll?.allowChange !== true) return doc\n\t\t\tconst previous = answerValues(previousDoc?.values, resultsField)\n\t\t\tconst next = answerValues(doc.values, resultsField)\n\t\t\tconst removed = previous.filter((value) => !next.includes(value))\n\t\t\tconst added = next.filter((value) => !previous.includes(value))\n\t\t\tfor (const value of removed) {\n\t\t\t\tawait bumpPollVote(req.payload, { ...base, value }, -1, transactionID)\n\t\t\t}\n\t\t\tfor (const value of added) {\n\t\t\t\tawait bumpPollVote(req.payload, { ...base, value }, 1, transactionID)\n\t\t\t}\n\t\t\treturn doc\n\t\t}\n\t\tconst votes = answerValues(doc.values, resultsField)\n\t\tif (votes.length === 0) return doc\n\t\tfor (const value of votes) {\n\t\t\tawait bumpPollVote(req.payload, { ...base, value }, 1, transactionID)\n\t\t}\n\t\tawait bumpPollVote(req.payload, { ...base, value: RESPONDENTS_VALUE }, 1, transactionID)\n\t\treturn doc\n\t}\n"],"mappings":";;;;;;;AAQA,MAAM,cAAc,QACnB,IAAI,UAAU,QAAQ,IAAI,WAAW;;AAGtC,MAAa,mBAAmB,kBAC/B,OAAO,kBAAkB,YAAY,OAAO,kBAAkB,WAAW,gBAAgB,KAAA;;;;;;;;;;;;;AAc1F,MAAa,0BAEZ,OAAO,EAAE,KAAK,aAAa,WAAW,UAAU;CAC/C,IAAI,cAAc,YAAY,cAAc,UAAU,OAAO;CAC7D,MAAM,WACJ,cAAc,YAAY,WAAW,GAAG,KACxC,cAAc,YAAY,WAAW,GAAG,KAAK,CAAC,WAAW,eAAe,CAAC,CAAC;CAC5E,MAAM,WACL,cAAc,YAAY,WAAW,GAAG,KAAK,WAAW,eAAe,CAAC,CAAC,KAAK,CAAC;CAChF,IAAI,CAAC,YAAY,CAAC,UAAU,OAAO;CACnC,MAAM,SAAS,SAAS,IAAI,IAAI;CAChC,IAAI,UAAU,MAAM,OAAO;CAC3B,MAAM,OAAO,MAAM,IAAI,QACrB,SAAS;EAAE,YAAY;EAAY,IAAI;EAAQ,OAAO;EAAG,gBAAgB;EAAM;CAAI,CAAC,EACpF,YAAY,IAAI;CAClB,MAAM,OAAO,MAAM,gBAAgB,OAAO,aAAa,KAAK,IAAI,IAAI,KAAA;CACpE,MAAM,eACL,OAAO,MAAM,iBAAiB,YAAY,KAAK,aAAa,SAAS,IAClE,KAAK,eACL,KAAA;CACJ,IAAI,CAAC,cAAc,OAAO;CAC1B,MAAM,gBAAgB,gBAAgB,IAAI,aAAa;CACvD,MAAM,OAAO;EAAE,MAAM,OAAO,MAAM;EAAG,OAAO;CAAa;CACzD,IAAI,UAAU;EACb,IAAI,MAAM,gBAAgB,MAAM,OAAO;EACvC,MAAM,WAAW,aAAa,aAAa,QAAQ,YAAY;EAC/D,MAAM,OAAO,aAAa,IAAI,QAAQ,YAAY;EAClD,MAAM,UAAU,SAAS,QAAQ,UAAU,CAAC,KAAK,SAAS,KAAK,CAAC;EAChE,MAAM,QAAQ,KAAK,QAAQ,UAAU,CAAC,SAAS,SAAS,KAAK,CAAC;EAC9D,KAAK,MAAM,SAAS,SACnB,MAAM,aAAa,IAAI,SAAS;GAAE,GAAG;GAAM;EAAM,GAAG,IAAI,aAAa;EAEtE,KAAK,MAAM,SAAS,OACnB,MAAM,aAAa,IAAI,SAAS;GAAE,GAAG;GAAM;EAAM,GAAG,GAAG,aAAa;EAErE,OAAO;CACR;CACA,MAAM,QAAQ,aAAa,IAAI,QAAQ,YAAY;CACnD,IAAI,MAAM,WAAW,GAAG,OAAO;CAC/B,KAAK,MAAM,SAAS,OACnB,MAAM,aAAa,IAAI,SAAS;EAAE,GAAG;EAAM;CAAM,GAAG,GAAG,aAAa;CAErE,MAAM,aAAa,IAAI,SAAS;EAAE,GAAG;EAAM,OAAA;CAAyB,GAAG,GAAG,aAAa;CACvF,OAAO;AACR"}
@@ -0,0 +1,19 @@
1
+ import { CollectionConfig } from "payload";
2
+
3
+ //#region src/poll/votes/votesCollection.d.ts
4
+ declare const POLL_VOTES_SLUG = "form-poll-votes";
5
+ /** Reserved tally `value` marking the per-field respondents counter (empty answers are never counted, so '' is free). */
6
+ declare const RESPONDENTS_VALUE = "";
7
+ /**
8
+ * Shard count for hot tally counters. Mongo aborts one of two transactions that write the same
9
+ * document (WriteConflict/TransientTransactionError), so concurrent transactional votes for the
10
+ * same (form, field, value) would deterministically fail one submission. Spreading each
11
+ * transactional bump across VOTE_SHARDS rows makes concurrent bumps land on different shards
12
+ * with high probability, so transactions stop contending. Postgres row locks serialize
13
+ * ON CONFLICT updates without aborting and non-transactional Mongo $inc is conflict-free, so
14
+ * both always use shard 0. Reads sum counts across shards.
15
+ */
16
+ declare const VOTE_SHARDS = 8;
17
+ //#endregion
18
+ export { POLL_VOTES_SLUG, RESPONDENTS_VALUE, VOTE_SHARDS };
19
+ //# sourceMappingURL=votesCollection.d.ts.map