@10x-media/form-builder 0.1.0-beta.3 → 0.1.0-beta.5

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 (406) hide show
  1. package/CHANGELOG.md +125 -0
  2. package/README.md +4 -4
  3. package/dist/actions/body/serializeBody.d.ts +22 -4
  4. package/dist/actions/body/serializeBody.js +3 -1
  5. package/dist/actions/body/serializeBody.js.map +1 -1
  6. package/dist/actions/body/textOfBody.d.ts +13 -0
  7. package/dist/actions/body/textOfBody.js +35 -0
  8. package/dist/actions/body/textOfBody.js.map +1 -0
  9. package/dist/actions/builtin/confirmation.js +51 -12
  10. package/dist/actions/builtin/confirmation.js.map +1 -1
  11. package/dist/actions/builtin/emailTeam.js +82 -34
  12. package/dist/actions/builtin/emailTeam.js.map +1 -1
  13. package/dist/actions/builtin/index.d.ts +18 -1
  14. package/dist/actions/builtin/index.js +21 -7
  15. package/dist/actions/builtin/index.js.map +1 -1
  16. package/dist/actions/builtin/signedWebhook.js +2 -16
  17. package/dist/actions/builtin/signedWebhook.js.map +1 -1
  18. package/dist/actions/defineAction.d.ts +1 -1
  19. package/dist/actions/fromAddresses.d.ts +21 -0
  20. package/dist/actions/fromAddresses.js +74 -0
  21. package/dist/actions/fromAddresses.js.map +1 -0
  22. package/dist/actions/runActions.js +2 -0
  23. package/dist/actions/runActions.js.map +1 -1
  24. package/dist/actions/task.js +9 -8
  25. package/dist/actions/task.js.map +1 -1
  26. package/dist/aggregation/aggregateResponses.d.ts +9 -0
  27. package/dist/aggregation/aggregateResponses.js +8 -14
  28. package/dist/aggregation/aggregateResponses.js.map +1 -1
  29. package/dist/aggregation/resolveResultsRequest.d.ts +35 -6
  30. package/dist/aggregation/resolveResultsRequest.js +68 -11
  31. package/dist/aggregation/resolveResultsRequest.js.map +1 -1
  32. package/dist/calc/computeCalcFields.js +2 -1
  33. package/dist/calc/computeCalcFields.js.map +1 -1
  34. package/dist/client/ByteSizeField.d.ts +8 -0
  35. package/dist/client/ByteSizeField.js +3 -1
  36. package/dist/client/ByteSizeField.js.map +1 -1
  37. package/dist/client/ClosePollButton.d.ts +15 -0
  38. package/dist/client/ClosePollButton.js +80 -0
  39. package/dist/client/ClosePollButton.js.map +1 -0
  40. package/dist/client/CondensedArray.d.ts +15 -0
  41. package/dist/client/CondensedArray.js +97 -0
  42. package/dist/client/CondensedArray.js.map +1 -0
  43. package/dist/client/EndpointOptionsSelect.d.ts +45 -0
  44. package/dist/client/EndpointOptionsSelect.js +103 -0
  45. package/dist/client/EndpointOptionsSelect.js.map +1 -0
  46. package/dist/client/FieldBlockLabel.d.ts +18 -0
  47. package/dist/client/FieldBlockLabel.js +34 -0
  48. package/dist/client/FieldBlockLabel.js.map +1 -0
  49. package/dist/client/FieldNameSelect.d.ts +20 -4
  50. package/dist/client/FieldNameSelect.js +25 -14
  51. package/dist/client/FieldNameSelect.js.map +1 -1
  52. package/dist/client/FlowBuilder.d.ts +2 -1
  53. package/dist/client/FlowBuilder.js +134 -83
  54. package/dist/client/FlowBuilder.js.map +1 -1
  55. package/dist/client/RuleDescription.d.ts +21 -0
  56. package/dist/client/RuleDescription.js +19 -0
  57. package/dist/client/RuleDescription.js.map +1 -0
  58. package/dist/client/admin.css +83 -5
  59. package/dist/client/endpointOptions.js +29 -0
  60. package/dist/client/endpointOptions.js.map +1 -0
  61. package/dist/client/flowAuthoring.js +142 -0
  62. package/dist/client/flowAuthoring.js.map +1 -0
  63. package/dist/client/synthesizeClientField.js +21 -10
  64. package/dist/client/synthesizeClientField.js.map +1 -1
  65. package/dist/collections/buttonFields.d.ts +34 -0
  66. package/dist/collections/buttonFields.js +35 -0
  67. package/dist/collections/buttonFields.js.map +1 -0
  68. package/dist/collections/cells/FieldCountCell.d.ts +15 -0
  69. package/dist/collections/cells/FieldCountCell.js +18 -0
  70. package/dist/collections/cells/FieldCountCell.js.map +1 -0
  71. package/dist/collections/cells/FlowStepsCell.d.ts +14 -0
  72. package/dist/collections/cells/FlowStepsCell.js +17 -0
  73. package/dist/collections/cells/FlowStepsCell.js.map +1 -0
  74. package/dist/collections/formSubmissions.js +55 -12
  75. package/dist/collections/formSubmissions.js.map +1 -1
  76. package/dist/collections/forms.js +376 -57
  77. package/dist/collections/forms.js.map +1 -1
  78. package/dist/collections/redirectFields.d.ts +29 -0
  79. package/dist/conditions/conditionType.js +9 -2
  80. package/dist/conditions/conditionType.js.map +1 -1
  81. package/dist/conditions/normalizeConditions.js +2 -1
  82. package/dist/conditions/normalizeConditions.js.map +1 -1
  83. package/dist/consent/applyConsentStatements.d.ts +14 -0
  84. package/dist/consent/applyConsentStatements.js +17 -0
  85. package/dist/consent/applyConsentStatements.js.map +1 -0
  86. package/dist/consent/captureConsent.d.ts +19 -8
  87. package/dist/consent/captureConsent.js +34 -11
  88. package/dist/consent/captureConsent.js.map +1 -1
  89. package/dist/consent/consentSourcesField.d.ts +58 -0
  90. package/dist/consent/consentSourcesField.js +79 -0
  91. package/dist/consent/consentSourcesField.js.map +1 -0
  92. package/dist/consent/resolveConsentEntries.d.ts +24 -0
  93. package/dist/consent/resolveConsentEntries.js +43 -0
  94. package/dist/consent/resolveConsentEntries.js.map +1 -0
  95. package/dist/consent/resolveConsentSourcesRequest.d.ts +37 -0
  96. package/dist/consent/resolveConsentSourcesRequest.js +55 -0
  97. package/dist/consent/resolveConsentSourcesRequest.js.map +1 -0
  98. package/dist/consent/resolveConsentStatements.d.ts +45 -0
  99. package/dist/consent/resolveConsentStatements.js +44 -0
  100. package/dist/consent/resolveConsentStatements.js.map +1 -0
  101. package/dist/consent/resolvePublishedVersionRef.d.ts +17 -11
  102. package/dist/consent/resolvePublishedVersionRef.js +13 -10
  103. package/dist/consent/resolvePublishedVersionRef.js.map +1 -1
  104. package/dist/consent/types.d.ts +49 -0
  105. package/dist/email/departments.d.ts +55 -0
  106. package/dist/email/departments.js +130 -0
  107. package/dist/email/departments.js.map +1 -0
  108. package/dist/email/departmentsField.d.ts +49 -0
  109. package/dist/email/departmentsField.js +69 -0
  110. package/dist/email/departmentsField.js.map +1 -0
  111. package/dist/exports/client.d.ts +8 -1
  112. package/dist/exports/client.js +8 -1
  113. package/dist/exports/react.d.ts +15 -6
  114. package/dist/exports/react.js +11 -3
  115. package/dist/exports/rsc.d.ts +8 -1
  116. package/dist/exports/rsc.js +8 -1
  117. package/dist/exports/types.d.ts +18 -10
  118. package/dist/fields/buildFieldBlocks.js +43 -19
  119. package/dist/fields/buildFieldBlocks.js.map +1 -1
  120. package/dist/fields/builtin/calculation.js +7 -0
  121. package/dist/fields/builtin/calculation.js.map +1 -1
  122. package/dist/fields/builtin/checkbox.js +8 -0
  123. package/dist/fields/builtin/checkbox.js.map +1 -1
  124. package/dist/fields/builtin/consent.js +25 -15
  125. package/dist/fields/builtin/consent.js.map +1 -1
  126. package/dist/fields/builtin/country.d.ts +13 -0
  127. package/dist/fields/builtin/country.js +30 -0
  128. package/dist/fields/builtin/country.js.map +1 -0
  129. package/dist/fields/builtin/date.js +2 -0
  130. package/dist/fields/builtin/date.js.map +1 -1
  131. package/dist/fields/builtin/file.js +13 -8
  132. package/dist/fields/builtin/file.js.map +1 -1
  133. package/dist/fields/builtin/index.d.ts +13 -1
  134. package/dist/fields/builtin/index.js +20 -7
  135. package/dist/fields/builtin/index.js.map +1 -1
  136. package/dist/fields/builtin/message.js +30 -0
  137. package/dist/fields/builtin/message.js.map +1 -0
  138. package/dist/fields/builtin/repeater.js +8 -3
  139. package/dist/fields/builtin/repeater.js.map +1 -1
  140. package/dist/fields/builtin/select.js +8 -3
  141. package/dist/fields/builtin/select.js.map +1 -1
  142. package/dist/fields/builtin/state.d.ts +13 -0
  143. package/dist/fields/builtin/state.js +30 -0
  144. package/dist/fields/builtin/state.js.map +1 -0
  145. package/dist/fields/data/regions.d.ts +21 -0
  146. package/dist/fields/data/regions.js +1238 -0
  147. package/dist/fields/data/regions.js.map +1 -0
  148. package/dist/fields/fieldKey.d.ts +15 -0
  149. package/dist/fields/fieldKey.js +16 -0
  150. package/dist/fields/fieldKey.js.map +1 -0
  151. package/dist/fields/fieldNamesOfType.js +13 -8
  152. package/dist/fields/fieldNamesOfType.js.map +1 -1
  153. package/dist/fields/instanceOptions.js +20 -0
  154. package/dist/fields/instanceOptions.js.map +1 -0
  155. package/dist/fields/localizedIf.d.ts +13 -0
  156. package/dist/fields/localizedIf.js +12 -0
  157. package/dist/fields/localizedIf.js.map +1 -0
  158. package/dist/fields/registry.d.ts +1 -0
  159. package/dist/fields/registry.js +21 -2
  160. package/dist/fields/registry.js.map +1 -1
  161. package/dist/fields/sharedConfig.js +101 -51
  162. package/dist/fields/sharedConfig.js.map +1 -1
  163. package/dist/fields/types.d.ts +61 -5
  164. package/dist/flow/engine.d.ts +6 -4
  165. package/dist/flow/engine.js +11 -6
  166. package/dist/flow/engine.js.map +1 -1
  167. package/dist/flow/normalizeFlow.js +22 -7
  168. package/dist/flow/normalizeFlow.js.map +1 -1
  169. package/dist/flow/stepLabel.js +15 -0
  170. package/dist/flow/stepLabel.js.map +1 -0
  171. package/dist/flow/types.d.ts +12 -3
  172. package/dist/flow/types.js +11 -0
  173. package/dist/flow/types.js.map +1 -0
  174. package/dist/form/pollState.d.ts +22 -0
  175. package/dist/form/pollState.js +17 -0
  176. package/dist/form/pollState.js.map +1 -0
  177. package/dist/form/toFormDocument.d.ts +85 -0
  178. package/dist/form/toFormDocument.js +75 -0
  179. package/dist/form/toFormDocument.js.map +1 -0
  180. package/dist/form/types.d.ts +65 -0
  181. package/dist/index.d.ts +193 -27
  182. package/dist/index.js +59 -19
  183. package/dist/index.js.map +1 -1
  184. package/dist/plugin/customState.js +26 -0
  185. package/dist/plugin/customState.js.map +1 -0
  186. package/dist/plugin/registerCollections.js +33 -44
  187. package/dist/plugin/registerCollections.js.map +1 -1
  188. package/dist/plugin/uploadsCollection.d.ts +13 -0
  189. package/dist/plugin/uploadsCollection.js +44 -0
  190. package/dist/plugin/uploadsCollection.js.map +1 -0
  191. package/dist/poll/applyPollOptions.js +18 -0
  192. package/dist/poll/applyPollOptions.js.map +1 -0
  193. package/dist/poll/buildPollOptionSourceFields.js +50 -0
  194. package/dist/poll/buildPollOptionSourceFields.js.map +1 -0
  195. package/dist/poll/closeJob.d.ts +52 -0
  196. package/dist/poll/closeJob.js +119 -0
  197. package/dist/poll/closeJob.js.map +1 -0
  198. package/dist/poll/definePollOptionSource.d.ts +44 -0
  199. package/dist/poll/definePollOptionSource.js +6 -0
  200. package/dist/poll/definePollOptionSource.js.map +1 -0
  201. package/dist/poll/definePollType.d.ts +34 -0
  202. package/dist/poll/definePollType.js +6 -0
  203. package/dist/poll/definePollType.js.map +1 -0
  204. package/dist/poll/effectivePollOptions.d.ts +38 -0
  205. package/dist/poll/effectivePollOptions.js +64 -0
  206. package/dist/poll/effectivePollOptions.js.map +1 -0
  207. package/dist/poll/mostVoted.d.ts +21 -0
  208. package/dist/poll/mostVoted.js +32 -0
  209. package/dist/poll/mostVoted.js.map +1 -0
  210. package/dist/poll/outcomeBeforeChange.js +91 -0
  211. package/dist/poll/outcomeBeforeChange.js.map +1 -0
  212. package/dist/poll/outcomeFields.d.ts +38 -0
  213. package/dist/poll/outcomeFields.js +52 -0
  214. package/dist/poll/outcomeFields.js.map +1 -0
  215. package/dist/poll/pollTypeRegistry.d.ts +34 -0
  216. package/dist/poll/pollTypeRegistry.js +71 -0
  217. package/dist/poll/pollTypeRegistry.js.map +1 -0
  218. package/dist/poll/registry.d.ts +16 -0
  219. package/dist/poll/registry.js +16 -0
  220. package/dist/poll/registry.js.map +1 -0
  221. package/dist/poll/resolvePollCloseRequest.js +105 -0
  222. package/dist/poll/resolvePollCloseRequest.js.map +1 -0
  223. package/dist/poll/resolvePollOptions.d.ts +29 -0
  224. package/dist/poll/resolvePollOptions.js +52 -0
  225. package/dist/poll/resolvePollOptions.js.map +1 -0
  226. package/dist/poll/resolvePollOptionsRequest.js +51 -0
  227. package/dist/poll/resolvePollOptionsRequest.js.map +1 -0
  228. package/dist/poll/resolvePollOutcome.d.ts +34 -0
  229. package/dist/poll/resolvePollOutcome.js +76 -0
  230. package/dist/poll/resolvePollOutcome.js.map +1 -0
  231. package/dist/poll/resultsField.js +21 -0
  232. package/dist/poll/resultsField.js.map +1 -0
  233. package/dist/prefill/valuesFromSearchParams.js +3 -2
  234. package/dist/prefill/valuesFromSearchParams.js.map +1 -1
  235. package/dist/presentations/types.d.ts +3 -2
  236. package/dist/react/Form.d.ts +15 -33
  237. package/dist/react/Form.js +144 -119
  238. package/dist/react/Form.js.map +1 -1
  239. package/dist/react/FormContext.d.ts +37 -1
  240. package/dist/react/FormContext.js.map +1 -1
  241. package/dist/react/FormControls.d.ts +55 -0
  242. package/dist/react/FormControls.js +56 -0
  243. package/dist/react/FormControls.js.map +1 -0
  244. package/dist/react/FormResults.d.ts +6 -3
  245. package/dist/react/FormResults.js +37 -29
  246. package/dist/react/FormResults.js.map +1 -1
  247. package/dist/react/FormSteps.d.ts +17 -0
  248. package/dist/react/FormSteps.js +35 -0
  249. package/dist/react/FormSteps.js.map +1 -0
  250. package/dist/react/Poll.d.ts +17 -6
  251. package/dist/react/Poll.js +66 -18
  252. package/dist/react/Poll.js.map +1 -1
  253. package/dist/react/contract.d.ts +2 -3
  254. package/dist/react/contract.js.map +1 -1
  255. package/dist/react/fetchResults.d.ts +1 -1
  256. package/dist/react/fetchResults.js +1 -1
  257. package/dist/react/fetchResults.js.map +1 -1
  258. package/dist/react/primitives/Checkbox.d.ts +4 -2
  259. package/dist/react/primitives/Checkbox.js +2 -1
  260. package/dist/react/primitives/Checkbox.js.map +1 -1
  261. package/dist/react/primitives/FieldShell.d.ts +2 -4
  262. package/dist/react/primitives/FieldShell.js +12 -19
  263. package/dist/react/primitives/FieldShell.js.map +1 -1
  264. package/dist/react/renderers/calculation.js +1 -2
  265. package/dist/react/renderers/calculation.js.map +1 -1
  266. package/dist/react/renderers/checkbox.js +1 -2
  267. package/dist/react/renderers/checkbox.js.map +1 -1
  268. package/dist/react/renderers/consent.js +78 -34
  269. package/dist/react/renderers/consent.js.map +1 -1
  270. package/dist/react/renderers/date.js +1 -3
  271. package/dist/react/renderers/date.js.map +1 -1
  272. package/dist/react/renderers/email.js +1 -2
  273. package/dist/react/renderers/email.js.map +1 -1
  274. package/dist/react/renderers/file.js +6 -3
  275. package/dist/react/renderers/file.js.map +1 -1
  276. package/dist/react/renderers/index.js +5 -0
  277. package/dist/react/renderers/index.js.map +1 -1
  278. package/dist/react/renderers/message.js +35 -0
  279. package/dist/react/renderers/message.js.map +1 -0
  280. package/dist/react/renderers/number.js +1 -2
  281. package/dist/react/renderers/number.js.map +1 -1
  282. package/dist/react/renderers/region.js +45 -0
  283. package/dist/react/renderers/region.js.map +1 -0
  284. package/dist/react/renderers/repeater.js +7 -10
  285. package/dist/react/renderers/repeater.js.map +1 -1
  286. package/dist/react/renderers/select.js +1 -2
  287. package/dist/react/renderers/select.js.map +1 -1
  288. package/dist/react/renderers/text.js +1 -2
  289. package/dist/react/renderers/text.js.map +1 -1
  290. package/dist/react/renderers/textarea.js +1 -2
  291. package/dist/react/renderers/textarea.js.map +1 -1
  292. package/dist/react/state.d.ts +23 -2
  293. package/dist/react/state.js +16 -7
  294. package/dist/react/state.js.map +1 -1
  295. package/dist/react/uploadFile.d.ts +2 -2
  296. package/dist/react/uploadFile.js +1 -1
  297. package/dist/react/uploadFile.js.map +1 -1
  298. package/dist/react/useField.d.ts +0 -1
  299. package/dist/react/useField.js +0 -1
  300. package/dist/react/useField.js.map +1 -1
  301. package/dist/react/useFormState.d.ts +1 -1
  302. package/dist/react/useFormState.js +1 -1
  303. package/dist/react/useFormState.js.map +1 -1
  304. package/dist/react/validateField.js +3 -6
  305. package/dist/react/validateField.js.map +1 -1
  306. package/dist/recall/interpolate.d.ts +4 -3
  307. package/dist/recall/interpolate.js +5 -4
  308. package/dist/recall/interpolate.js.map +1 -1
  309. package/dist/recall/resolver.d.ts +1 -1
  310. package/dist/recall/resolver.js +14 -2
  311. package/dist/recall/resolver.js.map +1 -1
  312. package/dist/spam/captcha.d.ts +1 -1
  313. package/dist/spam/captcha.js +1 -1
  314. package/dist/spam/captcha.js.map +1 -1
  315. package/dist/submissions/SubmissionAnswers.js +6 -2
  316. package/dist/submissions/SubmissionAnswers.js.map +1 -1
  317. package/dist/submissions/SubmissionAnswersClient.js +91 -62
  318. package/dist/submissions/SubmissionAnswersClient.js.map +1 -1
  319. package/dist/submissions/createSubmission.d.ts +36 -0
  320. package/dist/submissions/createSubmission.js +26 -0
  321. package/dist/submissions/createSubmission.js.map +1 -0
  322. package/dist/submissions/runSubmission.d.ts +10 -0
  323. package/dist/submissions/runSubmission.js +92 -44
  324. package/dist/submissions/runSubmission.js.map +1 -1
  325. package/dist/submissions/types.d.ts +11 -4
  326. package/dist/submissions/validateSubmission.js +59 -10
  327. package/dist/submissions/validateSubmission.js.map +1 -1
  328. package/dist/submissions/votedCookie.d.ts +13 -0
  329. package/dist/submissions/votedCookie.js +27 -0
  330. package/dist/submissions/votedCookie.js.map +1 -0
  331. package/dist/translations/de.js +295 -0
  332. package/dist/translations/de.js.map +1 -0
  333. package/dist/translations/en.js +138 -23
  334. package/dist/translations/en.js.map +1 -1
  335. package/dist/translations/index.d.ts +2 -1
  336. package/dist/translations/index.js +6 -2
  337. package/dist/translations/index.js.map +1 -1
  338. package/dist/translations/keys.d.ts +134 -19
  339. package/dist/translations/keys.js +135 -20
  340. package/dist/translations/keys.js.map +1 -1
  341. package/dist/uploads/types.d.ts +1 -2
  342. package/dist/validation/buildRuleBlocks.js +18 -17
  343. package/dist/validation/buildRuleBlocks.js.map +1 -1
  344. package/dist/validation/builtin/email.js +1 -0
  345. package/dist/validation/builtin/email.js.map +1 -1
  346. package/dist/validation/builtin/matchesField.js +3 -7
  347. package/dist/validation/builtin/matchesField.js.map +1 -1
  348. package/dist/validation/builtin/max.js +1 -0
  349. package/dist/validation/builtin/max.js.map +1 -1
  350. package/dist/validation/builtin/maxDate.js +1 -0
  351. package/dist/validation/builtin/maxDate.js.map +1 -1
  352. package/dist/validation/builtin/maxLength.js +1 -0
  353. package/dist/validation/builtin/maxLength.js.map +1 -1
  354. package/dist/validation/builtin/min.js +1 -0
  355. package/dist/validation/builtin/min.js.map +1 -1
  356. package/dist/validation/builtin/minDate.js +1 -0
  357. package/dist/validation/builtin/minDate.js.map +1 -1
  358. package/dist/validation/builtin/minLength.js +1 -0
  359. package/dist/validation/builtin/minLength.js.map +1 -1
  360. package/dist/validation/builtin/notAlreadySubmitted.js +1 -0
  361. package/dist/validation/builtin/notAlreadySubmitted.js.map +1 -1
  362. package/dist/validation/builtin/oneOf.js +1 -0
  363. package/dist/validation/builtin/oneOf.js.map +1 -1
  364. package/dist/validation/builtin/pattern.js +11 -2
  365. package/dist/validation/builtin/pattern.js.map +1 -1
  366. package/dist/validation/builtin/url.js +1 -0
  367. package/dist/validation/builtin/url.js.map +1 -1
  368. package/dist/validation/builtin/validateRegexParams.js +44 -0
  369. package/dist/validation/builtin/validateRegexParams.js.map +1 -0
  370. package/dist/validation/fieldTargetParam.d.ts +22 -0
  371. package/dist/validation/fieldTargetParam.js +38 -0
  372. package/dist/validation/fieldTargetParam.js.map +1 -0
  373. package/dist/validation/runValidation.js +8 -29
  374. package/dist/validation/runValidation.js.map +1 -1
  375. package/dist/validation/types.d.ts +5 -11
  376. package/dist/validation/validateUrl.js +22 -0
  377. package/dist/validation/validateUrl.js.map +1 -0
  378. package/package.json +7 -6
  379. package/styles/form-builder.css +10 -0
  380. package/dist/collections/uploads.d.ts +0 -30
  381. package/dist/collections/uploads.js +0 -55
  382. package/dist/collections/uploads.js.map +0 -1
  383. package/dist/consent/buildConsentSourceConfig.js +0 -49
  384. package/dist/consent/buildConsentSourceConfig.js.map +0 -1
  385. package/dist/consent/builtin/index.d.ts +0 -7
  386. package/dist/consent/builtin/index.js +0 -11
  387. package/dist/consent/builtin/index.js.map +0 -1
  388. package/dist/consent/builtin/pageReference.js +0 -75
  389. package/dist/consent/builtin/pageReference.js.map +0 -1
  390. package/dist/consent/builtin/static.js +0 -44
  391. package/dist/consent/builtin/static.js.map +0 -1
  392. package/dist/consent/defineConsentSource.d.ts +0 -35
  393. package/dist/consent/defineConsentSource.js +0 -6
  394. package/dist/consent/defineConsentSource.js.map +0 -1
  395. package/dist/consent/registry.d.ts +0 -16
  396. package/dist/consent/registry.js +0 -16
  397. package/dist/consent/registry.js.map +0 -1
  398. package/dist/consent/resolveConsentLinks.d.ts +0 -19
  399. package/dist/consent/resolveConsentLinks.js +0 -26
  400. package/dist/consent/resolveConsentLinks.js.map +0 -1
  401. package/dist/presentations/registry.d.ts +0 -15
  402. package/dist/presentations/registry.js +0 -15
  403. package/dist/presentations/registry.js.map +0 -1
  404. package/dist/react/toFormDocument.d.ts +0 -24
  405. package/dist/react/toFormDocument.js +0 -22
  406. package/dist/react/toFormDocument.js.map +0 -1
@@ -1,65 +1,54 @@
1
+ import { registerPollCloseTask } from "../poll/closeJob.js";
1
2
  import { buildFormsCollection } from "../collections/forms.js";
2
3
  import { buildSubmissionsCollection } from "../collections/formSubmissions.js";
3
4
  import { registerActionsTask } from "../actions/task.js";
4
- import { buildUploadOwnerStamp, buildUploadRateLimit } from "../spam/uploadHooks.js";
5
+ import { attachUploadsCollection } from "./uploadsCollection.js";
5
6
  //#region src/plugin/registerCollections.ts
6
- const registerCollections = ({ config, registry, ruleRegistry, consentRegistry, presentationRegistry, actionRegistry, richText, hasJobsPlugin, events, uploads, spam, showSubmissionRawFields, overrides }) => {
7
+ const registerCollections = ({ config, registry, ruleRegistry, consentSources, actionRegistry, richText, hasJobsPlugin, events, uploads, spam, showSubmissionRawFields, localizeContent, resultsAccess, votedCookie, pollSourceRegistry, pollTypeRegistry, outcomeFields, buttons, response, fromAddresses, departments, redirectRelationships, overrides }) => {
7
8
  registerActionsTask(config, actionRegistry, richText);
9
+ registerPollCloseTask(config);
8
10
  const hasRunner = Boolean(config.jobs?.autoRun) || hasJobsPlugin;
9
- let uploadsCollection = null;
10
- if (uploads.enabled && uploads.collection) {
11
- const col = uploads.collection;
12
- if (spam) {
13
- col.hooks = col.hooks ?? {};
14
- col.hooks.beforeOperation = [...col.hooks.beforeOperation ?? [], buildUploadRateLimit(spam)];
15
- col.hooks.beforeValidate = [...col.hooks.beforeValidate ?? [], buildUploadOwnerStamp(spam)];
16
- }
17
- const ov = overrides?.uploads;
18
- if (ov) {
19
- const defaultFields = col.fields ?? [];
20
- uploadsCollection = {
21
- ...col,
22
- ...ov ?? {},
23
- access: {
24
- ...col.access ?? {},
25
- ...ov.access ?? {}
26
- },
27
- admin: {
28
- ...col.admin ?? {},
29
- ...ov.admin ?? {}
30
- },
31
- hooks: {
32
- ...ov.hooks ?? {},
33
- beforeOperation: [...col.hooks?.beforeOperation ?? [], ...ov.hooks?.beforeOperation ?? []],
34
- beforeValidate: [...col.hooks?.beforeValidate ?? [], ...ov.hooks?.beforeValidate ?? []]
35
- },
36
- fields: ov.fields ? ov.fields({ defaultFields }) : defaultFields
37
- };
38
- } else uploadsCollection = col;
39
- }
11
+ const uploadSlug = uploads === false ? void 0 : uploads.collection;
12
+ if (uploads !== false) attachUploadsCollection({
13
+ config,
14
+ slug: uploads.collection,
15
+ spam
16
+ });
40
17
  config.collections = [
41
18
  ...config.collections ?? [],
42
- ...uploadsCollection ? [uploadsCollection] : [],
43
- buildFormsCollection({
44
- registry,
45
- ruleRegistry,
46
- consentRegistry,
47
- presentationRegistry,
48
- actionRegistry,
49
- overrides: overrides?.forms
50
- }),
51
19
  buildSubmissionsCollection({
52
20
  registry,
53
21
  ruleRegistry,
54
- consentRegistry,
22
+ consentSources,
55
23
  actionRegistry,
56
24
  richText,
57
25
  events,
58
26
  hasRunner,
59
- uploadSlug: uploads.slug,
27
+ uploadSlug,
60
28
  spam,
29
+ votedCookie,
30
+ pollSourceRegistry,
61
31
  showRawFields: showSubmissionRawFields,
62
32
  overrides: overrides?.formSubmissions
33
+ }),
34
+ buildFormsCollection({
35
+ registry,
36
+ ruleRegistry,
37
+ consentSources,
38
+ actionRegistry,
39
+ localizeContent,
40
+ richText,
41
+ uploadsCollectionSlug: uploadSlug,
42
+ resultsAccess,
43
+ pollSourceRegistry,
44
+ pollTypeRegistry,
45
+ outcomeFields,
46
+ buttons,
47
+ response,
48
+ fromAddresses,
49
+ departments,
50
+ redirectRelationships,
51
+ overrides: overrides?.forms
63
52
  })
64
53
  ];
65
54
  };
@@ -1 +1 @@
1
- {"version":3,"file":"registerCollections.js","names":[],"sources":["../../src/plugin/registerCollections.ts"],"sourcesContent":["import type { CollectionConfig, Config, Field } from 'payload'\nimport type { RichTextBodyOption } from '../actions/body/serializeBody'\nimport type { ActionRegistry } from '../actions/registry'\nimport { registerActionsTask } from '../actions/task'\nimport { buildSubmissionsCollection } from '../collections/formSubmissions'\nimport { buildFormsCollection } from '../collections/forms'\nimport type { ConsentSourceRegistry } from '../consent/registry'\nimport type { FormEventSink } from '../events/types'\nimport type { FieldTypeRegistry } from '../fields/registry'\nimport type { PresentationDescriptorRegistry } from '../presentations/registry'\nimport type { ResolvedSpamConfig } from '../spam/types'\nimport { buildUploadOwnerStamp, buildUploadRateLimit } from '../spam/uploadHooks'\nimport type { ValidationRuleRegistry } from '../validation/registry'\nimport type { CollectionOverrides } from './collectionOverrides'\n\ntype RegisterCollectionsArgs = {\n\tconfig: Config\n\tregistry: FieldTypeRegistry\n\truleRegistry: ValidationRuleRegistry\n\tconsentRegistry: ConsentSourceRegistry\n\tpresentationRegistry: PresentationDescriptorRegistry\n\tactionRegistry: ActionRegistry\n\trichText?: RichTextBodyOption\n\thasJobsPlugin: boolean\n\tevents?: FormEventSink\n\tuploads: { enabled: boolean; slug: string; collection?: CollectionConfig }\n\tspam: ResolvedSpamConfig | false\n\tshowSubmissionRawFields: boolean\n\toverrides?: {\n\t\tforms?: CollectionOverrides\n\t\tformSubmissions?: CollectionOverrides\n\t\tuploads?: CollectionOverrides\n\t}\n}\n\nexport const registerCollections = ({\n\tconfig,\n\tregistry,\n\truleRegistry,\n\tconsentRegistry,\n\tpresentationRegistry,\n\tactionRegistry,\n\trichText,\n\thasJobsPlugin,\n\tevents,\n\tuploads,\n\tspam,\n\tshowSubmissionRawFields,\n\toverrides,\n}: RegisterCollectionsArgs): void => {\n\tregisterActionsTask(config, actionRegistry, richText)\n\tconst hasRunner = Boolean(config.jobs?.autoRun) || hasJobsPlugin\n\n\tlet uploadsCollection: CollectionConfig | null = null\n\tif (uploads.enabled && uploads.collection) {\n\t\tconst col = uploads.collection\n\n\t\t// Spam hooks are added first (plugin-owned invariant)\n\t\tif (spam) {\n\t\t\tcol.hooks = col.hooks ?? {}\n\t\t\tcol.hooks.beforeOperation = [...(col.hooks.beforeOperation ?? []), buildUploadRateLimit(spam)]\n\t\t\tcol.hooks.beforeValidate = [...(col.hooks.beforeValidate ?? []), buildUploadOwnerStamp(spam)]\n\t\t}\n\n\t\t// Apply CollectionOverrides using explicit spreads after spam hooks are in place.\n\t\t// ...col first so the plugin defaults are the base, ...(ov ?? {}) second so the consumer\n\t\t// wins for any top-level key not explicitly re-set below (consistent with forms/formSubmissions).\n\t\t// The locked keys (hooks array ordering, fields function) are spread last.\n\t\tconst ov = overrides?.uploads\n\t\tif (ov) {\n\t\t\tconst defaultFields = (col.fields ?? []) as Field[]\n\t\t\tuploadsCollection = {\n\t\t\t\t...col,\n\t\t\t\t...(ov ?? {}),\n\t\t\t\taccess: { ...(col.access ?? {}), ...(ov.access ?? {}) },\n\t\t\t\tadmin: { ...(col.admin ?? {}), ...(ov.admin ?? {}) },\n\t\t\t\thooks: {\n\t\t\t\t\t...(ov.hooks ?? {}),\n\t\t\t\t\t// Spam hooks stay first in each array; consumer hooks appended\n\t\t\t\t\tbeforeOperation: [\n\t\t\t\t\t\t...(col.hooks?.beforeOperation ?? []),\n\t\t\t\t\t\t...(ov.hooks?.beforeOperation ?? []),\n\t\t\t\t\t],\n\t\t\t\t\tbeforeValidate: [\n\t\t\t\t\t\t...(col.hooks?.beforeValidate ?? []),\n\t\t\t\t\t\t...(ov.hooks?.beforeValidate ?? []),\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\tfields: ov.fields ? ov.fields({ defaultFields }) : defaultFields,\n\t\t\t}\n\t\t} else {\n\t\t\tuploadsCollection = col\n\t\t}\n\t}\n\n\tconfig.collections = [\n\t\t...(config.collections ?? []),\n\t\t...(uploadsCollection ? [uploadsCollection] : []),\n\t\tbuildFormsCollection({\n\t\t\tregistry,\n\t\t\truleRegistry,\n\t\t\tconsentRegistry,\n\t\t\tpresentationRegistry,\n\t\t\tactionRegistry,\n\t\t\toverrides: overrides?.forms,\n\t\t}),\n\t\tbuildSubmissionsCollection({\n\t\t\tregistry,\n\t\t\truleRegistry,\n\t\t\tconsentRegistry,\n\t\t\tactionRegistry,\n\t\t\trichText,\n\t\t\tevents,\n\t\t\thasRunner,\n\t\t\tuploadSlug: uploads.slug,\n\t\t\tspam,\n\t\t\tshowRawFields: showSubmissionRawFields,\n\t\t\toverrides: overrides?.formSubmissions,\n\t\t}),\n\t]\n}\n"],"mappings":";;;;;AAmCA,MAAa,uBAAuB,EACnC,QACA,UACA,cACA,iBACA,sBACA,gBACA,UACA,eACA,QACA,SACA,MACA,yBACA,gBACoC;CACpC,oBAAoB,QAAQ,gBAAgB,QAAQ;CACpD,MAAM,YAAY,QAAQ,OAAO,MAAM,OAAO,KAAK;CAEnD,IAAI,oBAA6C;CACjD,IAAI,QAAQ,WAAW,QAAQ,YAAY;EAC1C,MAAM,MAAM,QAAQ;EAGpB,IAAI,MAAM;GACT,IAAI,QAAQ,IAAI,SAAS,CAAC;GAC1B,IAAI,MAAM,kBAAkB,CAAC,GAAI,IAAI,MAAM,mBAAmB,CAAC,GAAI,qBAAqB,IAAI,CAAC;GAC7F,IAAI,MAAM,iBAAiB,CAAC,GAAI,IAAI,MAAM,kBAAkB,CAAC,GAAI,sBAAsB,IAAI,CAAC;EAC7F;EAMA,MAAM,KAAK,WAAW;EACtB,IAAI,IAAI;GACP,MAAM,gBAAiB,IAAI,UAAU,CAAC;GACtC,oBAAoB;IACnB,GAAG;IACH,GAAI,MAAM,CAAC;IACX,QAAQ;KAAE,GAAI,IAAI,UAAU,CAAC;KAAI,GAAI,GAAG,UAAU,CAAC;IAAG;IACtD,OAAO;KAAE,GAAI,IAAI,SAAS,CAAC;KAAI,GAAI,GAAG,SAAS,CAAC;IAAG;IACnD,OAAO;KACN,GAAI,GAAG,SAAS,CAAC;KAEjB,iBAAiB,CAChB,GAAI,IAAI,OAAO,mBAAmB,CAAC,GACnC,GAAI,GAAG,OAAO,mBAAmB,CAAC,CACnC;KACA,gBAAgB,CACf,GAAI,IAAI,OAAO,kBAAkB,CAAC,GAClC,GAAI,GAAG,OAAO,kBAAkB,CAAC,CAClC;IACD;IACA,QAAQ,GAAG,SAAS,GAAG,OAAO,EAAE,cAAc,CAAC,IAAI;GACpD;EACD,OACC,oBAAoB;CAEtB;CAEA,OAAO,cAAc;EACpB,GAAI,OAAO,eAAe,CAAC;EAC3B,GAAI,oBAAoB,CAAC,iBAAiB,IAAI,CAAC;EAC/C,qBAAqB;GACpB;GACA;GACA;GACA;GACA;GACA,WAAW,WAAW;EACvB,CAAC;EACD,2BAA2B;GAC1B;GACA;GACA;GACA;GACA;GACA;GACA;GACA,YAAY,QAAQ;GACpB;GACA,eAAe;GACf,WAAW,WAAW;EACvB,CAAC;CACF;AACD"}
1
+ {"version":3,"file":"registerCollections.js","names":[],"sources":["../../src/plugin/registerCollections.ts"],"sourcesContent":["import type { CollectionSlug, Config } from 'payload'\nimport type { RichTextBodyOption } from '../actions/body/serializeBody'\nimport type { FromAddressesResolver } from '../actions/fromAddresses'\nimport type { ActionRegistry } from '../actions/registry'\nimport { registerActionsTask } from '../actions/task'\nimport type { FormResultsAccess } from '../aggregation/resolveResultsRequest'\nimport type { ButtonsOption } from '../collections/buttonFields'\nimport { buildSubmissionsCollection } from '../collections/formSubmissions'\nimport { buildFormsCollection } from '../collections/forms'\nimport type { ResponseOption } from '../collections/redirectFields'\nimport type { ConsentSourcesResolver } from '../consent/types'\nimport type { DepartmentEmailsResolver } from '../email/departments'\nimport type { FormEventSink } from '../events/types'\nimport type { FieldTypeRegistry } from '../fields/registry'\nimport { registerPollCloseTask } from '../poll/closeJob'\nimport type { OutcomeFieldsOverride } from '../poll/outcomeFields'\nimport type { PollTypeRegistry } from '../poll/pollTypeRegistry'\nimport type { PollOptionSourceRegistry } from '../poll/registry'\nimport type { ResolvedSpamConfig } from '../spam/types'\nimport type { ValidationRuleRegistry } from '../validation/registry'\nimport type { CollectionOverrides } from './collectionOverrides'\nimport { attachUploadsCollection, type UploadsOption } from './uploadsCollection'\n\ntype RegisterCollectionsArgs = {\n\tconfig: Config\n\tregistry: FieldTypeRegistry\n\truleRegistry: ValidationRuleRegistry\n\tconsentSources?: ConsentSourcesResolver\n\tactionRegistry: ActionRegistry\n\trichText?: RichTextBodyOption\n\thasJobsPlugin: boolean\n\tevents?: FormEventSink\n\tuploads: UploadsOption\n\tspam: ResolvedSpamConfig | false\n\tshowSubmissionRawFields: boolean\n\tlocalizeContent: boolean\n\tresultsAccess?: FormResultsAccess\n\tvotedCookie: boolean\n\tpollSourceRegistry: PollOptionSourceRegistry\n\tpollTypeRegistry: PollTypeRegistry\n\toutcomeFields?: OutcomeFieldsOverride\n\tbuttons?: ButtonsOption\n\tresponse?: ResponseOption\n\tfromAddresses?: FromAddressesResolver\n\tdepartments?: DepartmentEmailsResolver\n\tredirectRelationships?: CollectionSlug[]\n\toverrides?: {\n\t\tforms?: CollectionOverrides\n\t\tformSubmissions?: CollectionOverrides\n\t}\n}\n\nexport const registerCollections = ({\n\tconfig,\n\tregistry,\n\truleRegistry,\n\tconsentSources,\n\tactionRegistry,\n\trichText,\n\thasJobsPlugin,\n\tevents,\n\tuploads,\n\tspam,\n\tshowSubmissionRawFields,\n\tlocalizeContent,\n\tresultsAccess,\n\tvotedCookie,\n\tpollSourceRegistry,\n\tpollTypeRegistry,\n\toutcomeFields,\n\tbuttons,\n\tresponse,\n\tfromAddresses,\n\tdepartments,\n\tredirectRelationships,\n\toverrides,\n}: RegisterCollectionsArgs): void => {\n\tregisterActionsTask(config, actionRegistry, richText)\n\tregisterPollCloseTask(config)\n\tconst hasRunner = Boolean(config.jobs?.autoRun) || hasJobsPlugin\n\n\tconst uploadSlug = uploads === false ? undefined : uploads.collection\n\tif (uploads !== false) {\n\t\tattachUploadsCollection({ config, slug: uploads.collection, spam })\n\t}\n\n\t// Payload has no nav-sort option, so it lists collections in registration order. Register\n\t// `form-submissions` before `forms` so the \"Forms\" nav group reads alphabetically\n\t// (`form-submissions` < `forms`); ordering plugin nav groups against a host's own collections\n\t// stays a host concern.\n\tconfig.collections = [\n\t\t...(config.collections ?? []),\n\t\tbuildSubmissionsCollection({\n\t\t\tregistry,\n\t\t\truleRegistry,\n\t\t\tconsentSources,\n\t\t\tactionRegistry,\n\t\t\trichText,\n\t\t\tevents,\n\t\t\thasRunner,\n\t\t\tuploadSlug,\n\t\t\tspam,\n\t\t\tvotedCookie,\n\t\t\tpollSourceRegistry,\n\t\t\tshowRawFields: showSubmissionRawFields,\n\t\t\toverrides: overrides?.formSubmissions,\n\t\t}),\n\t\tbuildFormsCollection({\n\t\t\tregistry,\n\t\t\truleRegistry,\n\t\t\tconsentSources,\n\t\t\tactionRegistry,\n\t\t\tlocalizeContent,\n\t\t\trichText,\n\t\t\tuploadsCollectionSlug: uploadSlug,\n\t\t\tresultsAccess,\n\t\t\tpollSourceRegistry,\n\t\t\tpollTypeRegistry,\n\t\t\toutcomeFields,\n\t\t\tbuttons,\n\t\t\tresponse,\n\t\t\tfromAddresses,\n\t\t\tdepartments,\n\t\t\tredirectRelationships,\n\t\t\toverrides: overrides?.forms,\n\t\t}),\n\t]\n}\n"],"mappings":";;;;;;AAoDA,MAAa,uBAAuB,EACnC,QACA,UACA,cACA,gBACA,gBACA,UACA,eACA,QACA,SACA,MACA,yBACA,iBACA,eACA,aACA,oBACA,kBACA,eACA,SACA,UACA,eACA,aACA,uBACA,gBACoC;CACpC,oBAAoB,QAAQ,gBAAgB,QAAQ;CACpD,sBAAsB,MAAM;CAC5B,MAAM,YAAY,QAAQ,OAAO,MAAM,OAAO,KAAK;CAEnD,MAAM,aAAa,YAAY,QAAQ,KAAA,IAAY,QAAQ;CAC3D,IAAI,YAAY,OACf,wBAAwB;EAAE;EAAQ,MAAM,QAAQ;EAAY;CAAK,CAAC;CAOnE,OAAO,cAAc;EACpB,GAAI,OAAO,eAAe,CAAC;EAC3B,2BAA2B;GAC1B;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,eAAe;GACf,WAAW,WAAW;EACvB,CAAC;EACD,qBAAqB;GACpB;GACA;GACA;GACA;GACA;GACA;GACA,uBAAuB;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,WAAW,WAAW;EACvB,CAAC;CACF;AACD"}
@@ -0,0 +1,13 @@
1
+ import { Config } from "payload";
2
+
3
+ //#region src/plugin/uploadsCollection.d.ts
4
+ /**
5
+ * Uploads are bring-your-own: `false` (the default) disables file fields entirely, an object points
6
+ * at a host-owned upload collection (created by the app with its storage adapter of choice).
7
+ */
8
+ type UploadsOption = false | {
9
+ collection: string;
10
+ };
11
+ //#endregion
12
+ export { UploadsOption };
13
+ //# sourceMappingURL=uploadsCollection.d.ts.map
@@ -0,0 +1,44 @@
1
+ import { buildUploadOwnerStamp, buildUploadRateLimit } from "../spam/uploadHooks.js";
2
+ //#region src/plugin/uploadsCollection.ts
3
+ /** Descends presentational rows only: an `owner` field nested in a group/tabs/collapsible is not detected, so a top-level one gets appended alongside it. */
4
+ const hasFieldNamed = (fields, name) => fields.some((field) => {
5
+ if ("name" in field && field.name === name) return true;
6
+ return field.type === "row" && hasFieldNamed(field.fields, name);
7
+ });
8
+ /**
9
+ * Wire the plugin's upload concerns into the host-owned upload collection named by
10
+ * `uploads.collection`. The collection must already exist in `config.collections` and carry an
11
+ * `upload` config; anything else is a plugin misconfiguration and throws at boot. The plugin
12
+ * appends its hidden `owner` text field unless the host already defines a field named `owner`
13
+ * (then that field is reused and must stay a text-compatible identity slot), and prepends the
14
+ * spam upload hooks (rate limit, owner stamp) ahead of the host's own hooks so abuse checks run
15
+ * first. The host entry is replaced with a new object; the host's config is never mutated.
16
+ */
17
+ const attachUploadsCollection = ({ config, slug, spam }) => {
18
+ const collections = config.collections ?? [];
19
+ const target = collections.find((collection) => collection.slug === slug);
20
+ if (!target) throw new Error(`@10x-media/form-builder: uploads.collection "${slug}" was not found in config.collections. Create an upload-enabled collection (with your storage adapter) and pass its slug, e.g. formBuilder({ uploads: { collection: 'media' } }).`);
21
+ if (!target.upload) throw new Error(`@10x-media/form-builder: uploads.collection "${slug}" has no \`upload\` config. File fields store Payload uploads, so the collection must be an upload collection (set \`upload: true\` or an upload options object on it).`);
22
+ const fields = hasFieldNamed(target.fields, "owner") ? target.fields : [...target.fields, {
23
+ name: "owner",
24
+ type: "text",
25
+ admin: {
26
+ readOnly: true,
27
+ hidden: true
28
+ }
29
+ }];
30
+ const attached = {
31
+ ...target,
32
+ fields,
33
+ ...spam ? { hooks: {
34
+ ...target.hooks ?? {},
35
+ beforeOperation: [buildUploadRateLimit(spam), ...target.hooks?.beforeOperation ?? []],
36
+ beforeValidate: [buildUploadOwnerStamp(spam), ...target.hooks?.beforeValidate ?? []]
37
+ } } : {}
38
+ };
39
+ config.collections = collections.map((collection) => collection.slug === slug ? attached : collection);
40
+ };
41
+ //#endregion
42
+ export { attachUploadsCollection };
43
+
44
+ //# sourceMappingURL=uploadsCollection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploadsCollection.js","names":[],"sources":["../../src/plugin/uploadsCollection.ts"],"sourcesContent":["import type { CollectionConfig, Config, Field } from 'payload'\nimport type { ResolvedSpamConfig } from '../spam/types'\nimport { buildUploadOwnerStamp, buildUploadRateLimit } from '../spam/uploadHooks'\n\n/**\n * Uploads are bring-your-own: `false` (the default) disables file fields entirely, an object points\n * at a host-owned upload collection (created by the app with its storage adapter of choice).\n */\nexport type UploadsOption = false | { collection: string }\n\n/** Descends presentational rows only: an `owner` field nested in a group/tabs/collapsible is not detected, so a top-level one gets appended alongside it. */\nconst hasFieldNamed = (fields: Field[], name: string): boolean =>\n\tfields.some((field) => {\n\t\tif ('name' in field && field.name === name) {\n\t\t\treturn true\n\t\t}\n\t\treturn field.type === 'row' && hasFieldNamed(field.fields, name)\n\t})\n\ntype AttachUploadsCollectionArgs = {\n\tconfig: Config\n\tslug: string\n\tspam: ResolvedSpamConfig | false\n}\n\n/**\n * Wire the plugin's upload concerns into the host-owned upload collection named by\n * `uploads.collection`. The collection must already exist in `config.collections` and carry an\n * `upload` config; anything else is a plugin misconfiguration and throws at boot. The plugin\n * appends its hidden `owner` text field unless the host already defines a field named `owner`\n * (then that field is reused and must stay a text-compatible identity slot), and prepends the\n * spam upload hooks (rate limit, owner stamp) ahead of the host's own hooks so abuse checks run\n * first. The host entry is replaced with a new object; the host's config is never mutated.\n */\nexport const attachUploadsCollection = ({\n\tconfig,\n\tslug,\n\tspam,\n}: AttachUploadsCollectionArgs): void => {\n\tconst collections = config.collections ?? []\n\tconst target = collections.find((collection) => collection.slug === slug)\n\tif (!target) {\n\t\tthrow new Error(\n\t\t\t`@10x-media/form-builder: uploads.collection \"${slug}\" was not found in config.collections. ` +\n\t\t\t\t`Create an upload-enabled collection (with your storage adapter) and pass its slug, ` +\n\t\t\t\t`e.g. formBuilder({ uploads: { collection: 'media' } }).`\n\t\t)\n\t}\n\tif (!target.upload) {\n\t\tthrow new Error(\n\t\t\t`@10x-media/form-builder: uploads.collection \"${slug}\" has no \\`upload\\` config. ` +\n\t\t\t\t`File fields store Payload uploads, so the collection must be an upload collection ` +\n\t\t\t\t`(set \\`upload: true\\` or an upload options object on it).`\n\t\t)\n\t}\n\n\tconst fields = hasFieldNamed(target.fields, 'owner')\n\t\t? target.fields\n\t\t: [\n\t\t\t\t...target.fields,\n\t\t\t\t{ name: 'owner', type: 'text', admin: { readOnly: true, hidden: true } } as Field,\n\t\t\t]\n\n\tconst attached: CollectionConfig = {\n\t\t...target,\n\t\tfields,\n\t\t...(spam\n\t\t\t? {\n\t\t\t\t\thooks: {\n\t\t\t\t\t\t...(target.hooks ?? {}),\n\t\t\t\t\t\tbeforeOperation: [buildUploadRateLimit(spam), ...(target.hooks?.beforeOperation ?? [])],\n\t\t\t\t\t\tbeforeValidate: [buildUploadOwnerStamp(spam), ...(target.hooks?.beforeValidate ?? [])],\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: {}),\n\t}\n\n\tconfig.collections = collections.map((collection) =>\n\t\tcollection.slug === slug ? attached : collection\n\t)\n}\n"],"mappings":";;;AAWA,MAAM,iBAAiB,QAAiB,SACvC,OAAO,MAAM,UAAU;CACtB,IAAI,UAAU,SAAS,MAAM,SAAS,MACrC,OAAO;CAER,OAAO,MAAM,SAAS,SAAS,cAAc,MAAM,QAAQ,IAAI;AAChE,CAAC;;;;;;;;;;AAiBF,MAAa,2BAA2B,EACvC,QACA,MACA,WACwC;CACxC,MAAM,cAAc,OAAO,eAAe,CAAC;CAC3C,MAAM,SAAS,YAAY,MAAM,eAAe,WAAW,SAAS,IAAI;CACxE,IAAI,CAAC,QACJ,MAAM,IAAI,MACT,gDAAgD,KAAK,kLAGtD;CAED,IAAI,CAAC,OAAO,QACX,MAAM,IAAI,MACT,gDAAgD,KAAK,wKAGtD;CAGD,MAAM,SAAS,cAAc,OAAO,QAAQ,OAAO,IAChD,OAAO,SACP,CACA,GAAG,OAAO,QACV;EAAE,MAAM;EAAS,MAAM;EAAQ,OAAO;GAAE,UAAU;GAAM,QAAQ;EAAK;CAAE,CACxE;CAEF,MAAM,WAA6B;EAClC,GAAG;EACH;EACA,GAAI,OACD,EACA,OAAO;GACN,GAAI,OAAO,SAAS,CAAC;GACrB,iBAAiB,CAAC,qBAAqB,IAAI,GAAG,GAAI,OAAO,OAAO,mBAAmB,CAAC,CAAE;GACtF,gBAAgB,CAAC,sBAAsB,IAAI,GAAG,GAAI,OAAO,OAAO,kBAAkB,CAAC,CAAE;EACtF,EACD,IACC,CAAC;CACL;CAEA,OAAO,cAAc,YAAY,KAAK,eACrC,WAAW,SAAS,OAAO,WAAW,UACvC;AACD"}
@@ -0,0 +1,18 @@
1
+ //#region src/poll/applyPollOptions.ts
2
+ /**
3
+ * Replace the options of the field instance named `resultsField` with source-resolved options,
4
+ * without mutating the input. No-op when `resultsField` is unset or matches no instance
5
+ * (nameless bare rows never match), so hand-authored options stay untouched on forms without
6
+ * an option source.
7
+ */
8
+ const applyPollOptions = (fields, resultsField, options) => {
9
+ if (typeof resultsField !== "string" || resultsField.length === 0) return fields;
10
+ return fields.map((instance) => instance.name === resultsField ? {
11
+ ...instance,
12
+ options
13
+ } : instance);
14
+ };
15
+ //#endregion
16
+ export { applyPollOptions };
17
+
18
+ //# sourceMappingURL=applyPollOptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"applyPollOptions.js","names":[],"sources":["../../src/poll/applyPollOptions.ts"],"sourcesContent":["import type { PollOption } from './definePollOptionSource'\n\n/**\n * Replace the options of the field instance named `resultsField` with source-resolved options,\n * without mutating the input. No-op when `resultsField` is unset or matches no instance\n * (nameless bare rows never match), so hand-authored options stay untouched on forms without\n * an option source.\n */\nexport const applyPollOptions = <T extends { name?: string }>(\n\tfields: T[],\n\tresultsField: string | null | undefined,\n\toptions: PollOption[]\n): T[] => {\n\tif (typeof resultsField !== 'string' || resultsField.length === 0) {\n\t\treturn fields\n\t}\n\treturn fields.map((instance) =>\n\t\tinstance.name === resultsField ? { ...instance, options } : instance\n\t)\n}\n"],"mappings":";;;;;;;AAQA,MAAa,oBACZ,QACA,cACA,YACS;CACT,IAAI,OAAO,iBAAiB,YAAY,aAAa,WAAW,GAC/D,OAAO;CAER,OAAO,OAAO,KAAK,aAClB,SAAS,SAAS,eAAe;EAAE,GAAG;EAAU;CAAQ,IAAI,QAC7D;AACD"}
@@ -0,0 +1,50 @@
1
+ import { keys } from "../translations/keys.js";
2
+ import { labelFor, labelForKey } 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: labelFor(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 { labelFor, labelForKey } 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) => ({ label: labelFor(source.label), value: source.type })),\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;GAAE,OAAO,SAAS,OAAO,KAAK;GAAG,OAAO,OAAO;EAAK,EAAE;CACzF,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,52 @@
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;
28
+ req?: PayloadRequest;
29
+ }) => Promise<void>;
30
+ /** Native Payload jobs task that resolves a poll's outcome once it closes. */
31
+ declare const buildPollCloseTask: () => TaskConfig;
32
+ /** Register the poll-close task on `config.jobs.tasks`, creating the jobs config if absent. */
33
+ declare const registerPollCloseTask: (config: Config) => void;
34
+ /**
35
+ * Schedule (or reschedule) the auto-close for a poll after a save. Enqueues a `waitUntil: closesAt`
36
+ * job when the poll is enabled, has a `closesAt`, is still unresolved, and its strategy is not
37
+ * `manual`, and only when a job runner is present (`payload.jobs.queue`); otherwise the resolve-on-read
38
+ * fallback is the safety net. Any prior pending close job for the form is deleted first so the newest
39
+ * `closesAt` wins. Never throws; a queue failure is logged and swallowed.
40
+ */
41
+ declare const enqueuePollClose: (args: {
42
+ payload: Payload;
43
+ form: {
44
+ id: number | string;
45
+ pollEnabled?: boolean | null;
46
+ poll?: unknown;
47
+ };
48
+ req?: PayloadRequest;
49
+ }) => Promise<void>;
50
+ //#endregion
51
+ export { POLL_CLOSE_TASK_SLUG, PollCloseTaskInput, buildPollCloseTask, enqueuePollClose, registerPollCloseTask, runPollClose, shouldAutoResolvePoll };
52
+ //# sourceMappingURL=closeJob.d.ts.map
@@ -0,0 +1,119 @@
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, 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
+ req
41
+ });
42
+ };
43
+ /** Native Payload jobs task that resolves a poll's outcome once it closes. */
44
+ const buildPollCloseTask = () => ({
45
+ slug: POLL_CLOSE_TASK_SLUG,
46
+ inputSchema: [{
47
+ name: "formId",
48
+ type: "text",
49
+ required: true
50
+ }],
51
+ handler: async ({ input, req }) => {
52
+ await runPollClose({
53
+ payload: req.payload,
54
+ input,
55
+ req
56
+ });
57
+ return { output: {} };
58
+ }
59
+ });
60
+ /** Register the poll-close task on `config.jobs.tasks`, creating the jobs config if absent. */
61
+ const registerPollCloseTask = (config) => {
62
+ config.jobs ??= {};
63
+ config.jobs.tasks ??= [];
64
+ config.jobs.tasks.push(buildPollCloseTask());
65
+ };
66
+ const canQueue = (payload) => typeof payload.jobs?.queue === "function";
67
+ /**
68
+ * Delete this form's pending (never-run) close jobs so a re-enqueue supersedes the prior one. Payload
69
+ * 3.85's `jobs.queue` has no concurrency key, so this mirrors core's `deleteScheduledPublishJobs`:
70
+ * a direct `db.deleteMany` on the jobs collection, scoped by task slug and the JSON `input.formId`.
71
+ * Best-effort; a failure here only risks a duplicate future job, which the idempotent handler absorbs.
72
+ */
73
+ const deletePendingCloseJobs = async (payload, formId, req) => {
74
+ try {
75
+ await payload.db.deleteMany({
76
+ collection: JOBS_SLUG,
77
+ req,
78
+ where: { and: [
79
+ { completedAt: { exists: false } },
80
+ { processing: { equals: false } },
81
+ { taskSlug: { equals: POLL_CLOSE_TASK_SLUG } },
82
+ { "input.formId": { equals: String(formId) } }
83
+ ] }
84
+ });
85
+ } catch (error) {
86
+ 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)}`);
87
+ }
88
+ };
89
+ /**
90
+ * Schedule (or reschedule) the auto-close for a poll after a save. Enqueues a `waitUntil: closesAt`
91
+ * job when the poll is enabled, has a `closesAt`, is still unresolved, and its strategy is not
92
+ * `manual`, and only when a job runner is present (`payload.jobs.queue`); otherwise the resolve-on-read
93
+ * fallback is the safety net. Any prior pending close job for the form is deleted first so the newest
94
+ * `closesAt` wins. Never throws; a queue failure is logged and swallowed.
95
+ */
96
+ const enqueuePollClose = async (args) => {
97
+ const { payload, form, req } = args;
98
+ const poll = pollConfigOf(form.poll);
99
+ if (form.pollEnabled !== true || !poll) return;
100
+ const closesAt = typeof poll.closesAt === "string" && poll.closesAt.length > 0 ? poll.closesAt : void 0;
101
+ if (!closesAt || !hasEmptyWinners(poll) || strategyOf(poll) === "manual" || !canQueue(payload)) return;
102
+ const waitUntil = new Date(closesAt);
103
+ if (Number.isNaN(waitUntil.getTime())) return;
104
+ await deletePendingCloseJobs(payload, form.id, req);
105
+ try {
106
+ await payload.jobs.queue({
107
+ task: POLL_CLOSE_TASK_SLUG,
108
+ input: { formId: String(form.id) },
109
+ waitUntil,
110
+ req
111
+ });
112
+ } catch (error) {
113
+ payload.logger?.error(`@10x-media/form-builder: failed to enqueue poll-close for form ${String(form.id)}: ${error instanceof Error ? error.message : String(error)}`);
114
+ }
115
+ };
116
+ //#endregion
117
+ export { POLL_CLOSE_TASK_SLUG, buildPollCloseTask, enqueuePollClose, registerPollCloseTask, runPollClose, shouldAutoResolvePoll };
118
+
119
+ //# 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\treq?: PayloadRequest\n}): Promise<void> => {\n\tconst { payload, input, 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, req })\n}\n\n/** Native Payload jobs task that resolves a poll's outcome once it closes. */\nexport const buildPollCloseTask = (): 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({ payload: req.payload, input: input as PollCloseTaskInput, req })\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): void => {\n\tconfig.jobs ??= {}\n\tconfig.jobs.tasks ??= []\n\tconfig.jobs.tasks.push(buildPollCloseTask())\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,SAId;CACpB,MAAM,EAAE,SAAS,OAAO,QAAQ;CAChC,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;CAAI,CAAC;AAChE;;AAGA,MAAa,4BACX;CACA,MAAM;CACN,aAAa,CAAC;EAAE,MAAM;EAAU,MAAM;EAAQ,UAAU;CAAK,CAAC;CAC9D,SAAS,OAAO,EAAE,OAAO,UAAU;EAClC,MAAM,aAAa;GAAE,SAAS,IAAI;GAAgB;GAA6B;EAAI,CAAC;EACpF,OAAO,EAAE,QAAQ,CAAC,EAAE;CACrB;AACD;;AAGD,MAAa,yBAAyB,WAAyB;CAC9D,OAAO,SAAS,CAAC;CACjB,OAAO,KAAK,UAAU,CAAC;CACvB,OAAO,KAAK,MAAM,KAAK,mBAAmB,CAAC;AAC5C;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;
26
+ label: 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\tlabel: 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":";AAyCA,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