@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 @@
1
+ {"version":3,"file":"resolveConsentSourcesRequest.js","names":[],"sources":["../../src/consent/resolveConsentSourcesRequest.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport { FORMS_SLUG } from '../collections/forms'\nimport { resolveConsentEntries } from './resolveConsentEntries'\nimport type { ConsentSourcesResolver } from './types'\n\n/** One selectable consent source for the consent field's `source` select. */\nexport type ConsentSourceOption = { label: string; value: string }\n\nexport type ResolveConsentSourcesRequestArgs = {\n\tpayload: Payload\n\tformId: number | string | undefined\n\t/** Whether the caller is authenticated (an admin/user). */\n\tisAuthed: boolean\n\treq: PayloadRequest\n\tresolver: ConsentSourcesResolver\n}\n\nexport type ResolveConsentSourcesRequestResult = {\n\tstatus: number\n\tbody: { options: ConsentSourceOption[] } | { errors: { message: string }[] }\n}\n\n/**\n * Authorize and resolve the `GET /:id/consent-sources` request backing the consent `source` select:\n * authenticated callers get the host's sources for this request (a multi-tenant resolver scoping by\n * `req` therefore offers an author only their own tenant's), anonymous callers are always refused.\n * Statuses mirror the poll-options endpoint: 400 missing id, 403 unauthenticated, 404 unknown form,\n * 503 when the resolver throws (fail closed).\n */\nexport const resolveConsentSourcesRequest = async (\n\targs: ResolveConsentSourcesRequestArgs\n): Promise<ResolveConsentSourcesRequestResult> => {\n\tconst { payload, formId, isAuthed, req, resolver } = args\n\tif (!isAuthed) {\n\t\treturn { status: 403, body: { errors: [{ message: 'Forbidden' }] } }\n\t}\n\tif (formId == null) {\n\t\treturn { status: 400, body: { errors: [{ message: 'Missing form id' }] } }\n\t}\n\t// Load under the caller's own read access (no overrideAccess): a tenant-scoped `forms.access.read`\n\t// then refuses a cross-tenant form id (null -> 404), so an authed user can never enumerate another\n\t// tenant's consent source labels/ids.\n\tconst form = await payload\n\t\t.findByID({ collection: FORMS_SLUG, id: formId, depth: 0, req })\n\t\t.catch(() => null)\n\tif (!form) {\n\t\treturn { status: 404, body: { errors: [{ message: 'Not found' }] } }\n\t}\n\ttry {\n\t\tconst entries = await resolveConsentEntries({ payload, req, form, sources: resolver })\n\t\treturn {\n\t\t\tstatus: 200,\n\t\t\tbody: {\n\t\t\t\toptions: entries.map((entry) => ({ label: entry.name ?? entry.id, value: entry.id })),\n\t\t\t},\n\t\t}\n\t} catch {\n\t\treturn { status: 503, body: { errors: [{ message: 'Consent sources unavailable' }] } }\n\t}\n}\n"],"mappings":";;;;;;;;;;AA6BA,MAAa,+BAA+B,OAC3C,SACiD;CACjD,MAAM,EAAE,SAAS,QAAQ,UAAU,KAAK,aAAa;CACrD,IAAI,CAAC,UACJ,OAAO;EAAE,QAAQ;EAAK,MAAM,EAAE,QAAQ,CAAC,EAAE,SAAS,YAAY,CAAC,EAAE;CAAE;CAEpE,IAAI,UAAU,MACb,OAAO;EAAE,QAAQ;EAAK,MAAM,EAAE,QAAQ,CAAC,EAAE,SAAS,kBAAkB,CAAC,EAAE;CAAE;CAK1E,MAAM,OAAO,MAAM,QACjB,SAAS;EAAE,YAAY;EAAY,IAAI;EAAQ,OAAO;EAAG;CAAI,CAAC,EAC9D,YAAY,IAAI;CAClB,IAAI,CAAC,MACJ,OAAO;EAAE,QAAQ;EAAK,MAAM,EAAE,QAAQ,CAAC,EAAE,SAAS,YAAY,CAAC,EAAE;CAAE;CAEpE,IAAI;EAEH,OAAO;GACN,QAAQ;GACR,MAAM,EACL,UAAS,MAJW,sBAAsB;IAAE;IAAS;IAAK;IAAM,SAAS;GAAS,CAAC,GAIlE,KAAK,WAAW;IAAE,OAAO,MAAM,QAAQ,MAAM;IAAI,OAAO,MAAM;GAAG,EAAE,EACrF;EACD;CACD,QAAQ;EACP,OAAO;GAAE,QAAQ;GAAK,MAAM,EAAE,QAAQ,CAAC,EAAE,SAAS,8BAA8B,CAAC,EAAE;EAAE;CACtF;AACD"}
@@ -0,0 +1,45 @@
1
+ import { ConsentSourcesResolver } from "./types.js";
2
+ import { Payload, PayloadRequest } from "payload";
3
+
4
+ //#region src/consent/resolveConsentStatements.d.ts
5
+ /** What a consent field shows the visitor, resolved from its source at request time. */
6
+ type ConsentStatement = {
7
+ /** Rich text state (or whatever the host's resolver returned), for the request's locale. */statement?: unknown; /** The policy link beside the statement, present only when the source carries both a `url` and a `label`. */
8
+ link?: {
9
+ label: string;
10
+ url: string;
11
+ };
12
+ };
13
+ /** Resolved statements keyed by consent field name, as `toFormDocument`'s `consentStatements` takes them. */
14
+ type ConsentStatements = Record<string, ConsentStatement>;
15
+ type ResolveConsentStatementsArgs = {
16
+ payload: Payload;
17
+ req: PayloadRequest; /** A loaded forms document; its consent fields' `source` ids drive resolution. The whole doc is forwarded to the resolver. */
18
+ form: {
19
+ id: number | string;
20
+ title?: string | null;
21
+ fields?: {
22
+ blockType: string;
23
+ name?: string;
24
+ [key: string]: unknown;
25
+ }[] | null;
26
+ }; /** Resolver override for plugin-internal callers; defaults to the one the plugin stashed on the config. */
27
+ sources?: ConsentSourcesResolver;
28
+ };
29
+ /**
30
+ * Resolve every consent field's statement for this request, ready to hand to
31
+ * `toFormDocument(doc, { consentStatements })`.
32
+ *
33
+ * The statement is a live reference, not a copy: a form stores only its source id, and the
34
+ * sentence the visitor reads is resolved here, in their locale, from whatever the source says
35
+ * right now. So correcting a policy sentence corrects every form referencing it, with nothing to
36
+ * re-save, and a form rendered without this step shows no statement at all rather than a stale one.
37
+ *
38
+ * Call it server-side (a Server Component, a route handler) before rendering `<Form>`. Throws
39
+ * whatever the host's resolver throws, so a page can decide between failing and rendering without
40
+ * consent; a submit resolves the source again regardless, and the proof is built from that.
41
+ */
42
+ declare const resolveConsentStatements: (args: ResolveConsentStatementsArgs) => Promise<ConsentStatements>;
43
+ //#endregion
44
+ export { ConsentStatement, ConsentStatements, ResolveConsentStatementsArgs, resolveConsentStatements };
45
+ //# sourceMappingURL=resolveConsentStatements.d.ts.map
@@ -0,0 +1,46 @@
1
+ import { resolveConsentEntries } from "./resolveConsentEntries.js";
2
+ import { consentDisplayOf, effectiveConsentStatement } from "./effectiveStatement.js";
3
+ //#region src/consent/resolveConsentStatements.ts
4
+ /**
5
+ * Resolve every consent field's statement for this request, ready to hand to
6
+ * `toFormDocument(doc, { consentStatements })`.
7
+ *
8
+ * The statement is a live reference, not a copy: a form stores only its source id, and the
9
+ * sentence the visitor reads is resolved here, in their locale, from whatever the source says
10
+ * right now. So correcting a policy sentence corrects every form referencing it, with nothing to
11
+ * re-save, and a form rendered without this step shows no statement at all rather than a stale one.
12
+ *
13
+ * Call it server-side (a Server Component, a route handler) before rendering `<Form>`. Throws
14
+ * whatever the host's resolver throws, so a page can decide between failing and rendering without
15
+ * consent; a submit resolves the source again regardless, and the proof is built from that.
16
+ */
17
+ const resolveConsentStatements = async (args) => {
18
+ const { payload, req, form, sources } = args;
19
+ const consentFields = (form.fields ?? []).filter((field) => field.blockType === "consent" && typeof field.name === "string" && field.name.length > 0);
20
+ if (consentFields.length === 0) return {};
21
+ const entries = await resolveConsentEntries({
22
+ payload,
23
+ req,
24
+ form,
25
+ sources
26
+ });
27
+ const statements = {};
28
+ for (const field of consentFields) {
29
+ const entry = entries.find((candidate) => candidate.id === field.source);
30
+ if (!entry) continue;
31
+ const label = typeof entry.name === "string" && entry.name.trim() !== "" ? entry.name : "";
32
+ const wording = effectiveConsentStatement(entry, consentDisplayOf(field));
33
+ statements[field.name] = {
34
+ ...wording != null ? { statement: wording } : {},
35
+ ...label && typeof entry.url === "string" && entry.url.length > 0 ? { link: {
36
+ label,
37
+ url: entry.url
38
+ } } : {}
39
+ };
40
+ }
41
+ return statements;
42
+ };
43
+ //#endregion
44
+ export { resolveConsentStatements };
45
+
46
+ //# sourceMappingURL=resolveConsentStatements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveConsentStatements.js","names":[],"sources":["../../src/consent/resolveConsentStatements.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport { consentDisplayOf, effectiveConsentStatement } from './effectiveStatement'\nimport { resolveConsentEntries } from './resolveConsentEntries'\nimport type { ConsentSourcesResolver } from './types'\n\n/** What a consent field shows the visitor, resolved from its source at request time. */\nexport type ConsentStatement = {\n\t/** Rich text state (or whatever the host's resolver returned), for the request's locale. */\n\tstatement?: unknown\n\t/** The policy link beside the statement, present only when the source carries both a `url` and a `label`. */\n\tlink?: { label: string; url: string }\n}\n\n/** Resolved statements keyed by consent field name, as `toFormDocument`'s `consentStatements` takes them. */\nexport type ConsentStatements = Record<string, ConsentStatement>\n\nexport type ResolveConsentStatementsArgs = {\n\tpayload: Payload\n\treq: PayloadRequest\n\t/** A loaded forms document; its consent fields' `source` ids drive resolution. The whole doc is forwarded to the resolver. */\n\tform: {\n\t\tid: number | string\n\t\ttitle?: string | null\n\t\tfields?: { blockType: string; name?: string; [key: string]: unknown }[] | null\n\t}\n\t/** Resolver override for plugin-internal callers; defaults to the one the plugin stashed on the config. */\n\tsources?: ConsentSourcesResolver\n}\n\n/**\n * Resolve every consent field's statement for this request, ready to hand to\n * `toFormDocument(doc, { consentStatements })`.\n *\n * The statement is a live reference, not a copy: a form stores only its source id, and the\n * sentence the visitor reads is resolved here, in their locale, from whatever the source says\n * right now. So correcting a policy sentence corrects every form referencing it, with nothing to\n * re-save, and a form rendered without this step shows no statement at all rather than a stale one.\n *\n * Call it server-side (a Server Component, a route handler) before rendering `<Form>`. Throws\n * whatever the host's resolver throws, so a page can decide between failing and rendering without\n * consent; a submit resolves the source again regardless, and the proof is built from that.\n */\nexport const resolveConsentStatements = async (\n\targs: ResolveConsentStatementsArgs\n): Promise<ConsentStatements> => {\n\tconst { payload, req, form, sources } = args\n\tconst consentFields = (form.fields ?? []).filter(\n\t\t(field): field is { blockType: string; name: string; source?: unknown; display?: unknown } =>\n\t\t\tfield.blockType === 'consent' && typeof field.name === 'string' && field.name.length > 0\n\t)\n\tif (consentFields.length === 0) {\n\t\treturn {}\n\t}\n\tconst entries = await resolveConsentEntries({ payload, req, form, sources })\n\tconst statements: ConsentStatements = {}\n\tfor (const field of consentFields) {\n\t\tconst entry = entries.find((candidate) => candidate.id === field.source)\n\t\tif (!entry) {\n\t\t\tcontinue\n\t\t}\n\t\t// The link needs a name of its own, so it takes the source's `name` and never falls back to\n\t\t// the `id`: the id is a machine identifier, and showing it as the words a visitor clicks is\n\t\t// worse than no link at all. A source with a url and no name simply has no link.\n\t\tconst label = typeof entry.name === 'string' && entry.name.trim() !== '' ? entry.name : ''\n\t\t// The wording is selected per the field's display through the same helper the proof path\n\t\t// uses, so what renders is what a submit snapshots. The client only ever sees `statement`.\n\t\tconst wording = effectiveConsentStatement(entry, consentDisplayOf(field))\n\t\tstatements[field.name] = {\n\t\t\t...(wording != null ? { statement: wording } : {}),\n\t\t\t...(label && typeof entry.url === 'string' && entry.url.length > 0\n\t\t\t\t? { link: { label, url: entry.url } }\n\t\t\t\t: {}),\n\t\t}\n\t}\n\treturn statements\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA0CA,MAAa,2BAA2B,OACvC,SACgC;CAChC,MAAM,EAAE,SAAS,KAAK,MAAM,YAAY;CACxC,MAAM,iBAAiB,KAAK,UAAU,CAAC,GAAG,QACxC,UACA,MAAM,cAAc,aAAa,OAAO,MAAM,SAAS,YAAY,MAAM,KAAK,SAAS,CACzF;CACA,IAAI,cAAc,WAAW,GAC5B,OAAO,CAAC;CAET,MAAM,UAAU,MAAM,sBAAsB;EAAE;EAAS;EAAK;EAAM;CAAQ,CAAC;CAC3E,MAAM,aAAgC,CAAC;CACvC,KAAK,MAAM,SAAS,eAAe;EAClC,MAAM,QAAQ,QAAQ,MAAM,cAAc,UAAU,OAAO,MAAM,MAAM;EACvE,IAAI,CAAC,OACJ;EAKD,MAAM,QAAQ,OAAO,MAAM,SAAS,YAAY,MAAM,KAAK,KAAK,MAAM,KAAK,MAAM,OAAO;EAGxF,MAAM,UAAU,0BAA0B,OAAO,iBAAiB,KAAK,CAAC;EACxE,WAAW,MAAM,QAAQ;GACxB,GAAI,WAAW,OAAO,EAAE,WAAW,QAAQ,IAAI,CAAC;GAChD,GAAI,SAAS,OAAO,MAAM,QAAQ,YAAY,MAAM,IAAI,SAAS,IAC9D,EAAE,MAAM;IAAE;IAAO,KAAK,MAAM;GAAI,EAAE,IAClC,CAAC;EACL;CACD;CACA,OAAO;AACR"}
@@ -1,17 +1,23 @@
1
- import { Payload } from "payload";
1
+ import { Payload, PayloadRequest } from "payload";
2
2
 
3
3
  //#region src/consent/resolvePublishedVersionRef.d.ts
4
+ type ResolvePublishedVersionRefArgs = {
5
+ payload: Payload;
6
+ collection: string;
7
+ id: number | string; /** Threaded into `findVersions` so a version written earlier in the same transaction is visible. */
8
+ req?: PayloadRequest;
9
+ };
4
10
  /**
5
- * Returns the latest published version of a doc, or null when versions/drafts are off or nothing
6
- * is published. Never throws.
11
+ * The id of a document's latest published version, or null when nothing is published.
12
+ *
13
+ * Drafts-only, and deliberately not a versions check: `_status` exists only where
14
+ * `versions.drafts` is on (Payload adds the base version fields inside that branch when
15
+ * sanitizing), so on a collection with plain versions and no drafts this query matches nothing and
16
+ * returns null, which is indistinguishable from having no versions at all. Callers must decide the
17
+ * difference themselves with `hasDraftsEnabled` rather than reading null as "no versions" (see
18
+ * `captureConsent`). Never throws.
7
19
  */
8
- declare const resolvePublishedVersionRef: (payload: Payload, args: {
9
- collection: string;
10
- id: string | number;
11
- }) => Promise<{
12
- versionId: string;
13
- updatedAt: string;
14
- } | null>;
20
+ declare const resolvePublishedVersionRef: (args: ResolvePublishedVersionRefArgs) => Promise<string | null>;
15
21
  //#endregion
16
- export { resolvePublishedVersionRef };
22
+ export { ResolvePublishedVersionRefArgs, resolvePublishedVersionRef };
17
23
  //# sourceMappingURL=resolvePublishedVersionRef.d.ts.map
@@ -1,22 +1,25 @@
1
1
  //#region src/consent/resolvePublishedVersionRef.ts
2
2
  /**
3
- * Returns the latest published version of a doc, or null when versions/drafts are off or nothing
4
- * is published. Never throws.
3
+ * The id of a document's latest published version, or null when nothing is published.
4
+ *
5
+ * Drafts-only, and deliberately not a versions check: `_status` exists only where
6
+ * `versions.drafts` is on (Payload adds the base version fields inside that branch when
7
+ * sanitizing), so on a collection with plain versions and no drafts this query matches nothing and
8
+ * returns null, which is indistinguishable from having no versions at all. Callers must decide the
9
+ * difference themselves with `hasDraftsEnabled` rather than reading null as "no versions" (see
10
+ * `captureConsent`). Never throws.
5
11
  */
6
- const resolvePublishedVersionRef = async (payload, args) => {
12
+ const resolvePublishedVersionRef = async (args) => {
7
13
  try {
8
- const doc = (await payload.findVersions({
14
+ const doc = (await args.payload.findVersions({
9
15
  collection: args.collection,
10
16
  where: { and: [{ parent: { equals: args.id } }, { "version._status": { equals: "published" } }] },
11
17
  sort: "-updatedAt",
12
18
  limit: 1,
13
- depth: 0
19
+ depth: 0,
20
+ req: args.req
14
21
  }))?.docs?.[0];
15
- if (!doc) return null;
16
- return {
17
- versionId: String(doc.id),
18
- updatedAt: String(doc.updatedAt)
19
- };
22
+ return doc ? String(doc.id) : null;
20
23
  } catch {
21
24
  return null;
22
25
  }
@@ -1 +1 @@
1
- {"version":3,"file":"resolvePublishedVersionRef.js","names":[],"sources":["../../src/consent/resolvePublishedVersionRef.ts"],"sourcesContent":["import type { Payload } from 'payload'\n\n/**\n * Returns the latest published version of a doc, or null when versions/drafts are off or nothing\n * is published. Never throws.\n */\nexport const resolvePublishedVersionRef = async (\n\tpayload: Payload,\n\targs: { collection: string; id: string | number }\n): Promise<{ versionId: string; updatedAt: string } | null> => {\n\ttry {\n\t\tconst result = await payload.findVersions({\n\t\t\tcollection: args.collection as never,\n\t\t\twhere: {\n\t\t\t\tand: [{ parent: { equals: args.id } }, { 'version._status': { equals: 'published' } }],\n\t\t\t},\n\t\t\tsort: '-updatedAt',\n\t\t\tlimit: 1,\n\t\t\tdepth: 0,\n\t\t})\n\t\tconst doc = result?.docs?.[0]\n\t\tif (!doc) {\n\t\t\treturn null\n\t\t}\n\t\treturn { versionId: String(doc.id), updatedAt: String(doc.updatedAt) }\n\t} catch {\n\t\treturn null\n\t}\n}\n"],"mappings":";;;;;AAMA,MAAa,6BAA6B,OACzC,SACA,SAC8D;CAC9D,IAAI;EAUH,MAAM,OAAM,MATS,QAAQ,aAAa;GACzC,YAAY,KAAK;GACjB,OAAO,EACN,KAAK,CAAC,EAAE,QAAQ,EAAE,QAAQ,KAAK,GAAG,EAAE,GAAG,EAAE,mBAAmB,EAAE,QAAQ,YAAY,EAAE,CAAC,EACtF;GACA,MAAM;GACN,OAAO;GACP,OAAO;EACR,CAAC,IACmB,OAAO;EAC3B,IAAI,CAAC,KACJ,OAAO;EAER,OAAO;GAAE,WAAW,OAAO,IAAI,EAAE;GAAG,WAAW,OAAO,IAAI,SAAS;EAAE;CACtE,QAAQ;EACP,OAAO;CACR;AACD"}
1
+ {"version":3,"file":"resolvePublishedVersionRef.js","names":[],"sources":["../../src/consent/resolvePublishedVersionRef.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\n\nexport type ResolvePublishedVersionRefArgs = {\n\tpayload: Payload\n\tcollection: string\n\tid: number | string\n\t/** Threaded into `findVersions` so a version written earlier in the same transaction is visible. */\n\treq?: PayloadRequest\n}\n\n/**\n * The id of a document's latest published version, or null when nothing is published.\n *\n * Drafts-only, and deliberately not a versions check: `_status` exists only where\n * `versions.drafts` is on (Payload adds the base version fields inside that branch when\n * sanitizing), so on a collection with plain versions and no drafts this query matches nothing and\n * returns null, which is indistinguishable from having no versions at all. Callers must decide the\n * difference themselves with `hasDraftsEnabled` rather than reading null as \"no versions\" (see\n * `captureConsent`). Never throws.\n */\nexport const resolvePublishedVersionRef = async (\n\targs: ResolvePublishedVersionRefArgs\n): Promise<string | null> => {\n\ttry {\n\t\tconst result = await args.payload.findVersions({\n\t\t\tcollection: args.collection as never,\n\t\t\twhere: {\n\t\t\t\tand: [{ parent: { equals: args.id } }, { 'version._status': { equals: 'published' } }],\n\t\t\t},\n\t\t\tsort: '-updatedAt',\n\t\t\tlimit: 1,\n\t\t\tdepth: 0,\n\t\t\treq: args.req,\n\t\t})\n\t\tconst doc = result?.docs?.[0]\n\t\treturn doc ? String(doc.id) : null\n\t} catch {\n\t\treturn null\n\t}\n}\n"],"mappings":";;;;;;;;;;;AAoBA,MAAa,6BAA6B,OACzC,SAC4B;CAC5B,IAAI;EAWH,MAAM,OAAM,MAVS,KAAK,QAAQ,aAAa;GAC9C,YAAY,KAAK;GACjB,OAAO,EACN,KAAK,CAAC,EAAE,QAAQ,EAAE,QAAQ,KAAK,GAAG,EAAE,GAAG,EAAE,mBAAmB,EAAE,QAAQ,YAAY,EAAE,CAAC,EACtF;GACA,MAAM;GACN,OAAO;GACP,OAAO;GACP,KAAK,KAAK;EACX,CAAC,IACmB,OAAO;EAC3B,OAAO,MAAM,OAAO,IAAI,EAAE,IAAI;CAC/B,QAAQ;EACP,OAAO;CACR;AACD"}
@@ -0,0 +1,69 @@
1
+ import { PayloadRequest } from "payload";
2
+
3
+ //#region src/consent/types.d.ts
4
+ /** A document reference, kept id-based so renaming or re-slugging the page never breaks it. */
5
+ type ConsentSourcePage = {
6
+ relationTo: string;
7
+ id: number | string;
8
+ };
9
+ /**
10
+ * One consent source a form author can reference by `id`: the statement the visitor agrees to, and
11
+ * optionally the policy document that statement belongs to. Produced by the host's
12
+ * {@link ConsentSourcesResolver}, normally by mapping the rows of a placed `consentSourcesField()`.
13
+ *
14
+ * `id` is the array row's own auto-assigned identifier, so it survives name edits and reordering
15
+ * and is the stable reference stored on the consent field and in every proof. `statement` is rich
16
+ * text state (the visitor-facing sentence), already resolved for the request's locale by virtue of
17
+ * the resolver reading its own localized field under `req.locale`. `page` drives proof: an id and
18
+ * its collection, never a URL, so the document can be renamed, re-slugged, or re-routed without
19
+ * stranding past proofs. `url` is the visitor-facing link the renderer shows beside the statement;
20
+ * only the host knows how a document id maps to a public route, so the plugin never derives one.
21
+ */
22
+ type ConsentSourceEntry = {
23
+ id: string;
24
+ name?: string;
25
+ statement?: unknown;
26
+ /**
27
+ * The wording a `display: 'notice'` consent field shows instead of `statement` ("By
28
+ * subscribing, you agree..." rather than "I accept..."). Same consent, same policy, same id in
29
+ * every proof; only the sentence differs. Absent or empty falls back to `statement`. A resolver
30
+ * mapping `consentSourcesField()` rows must forward the row's `noticeStatement` for notice
31
+ * displays to pick it up.
32
+ */
33
+ noticeStatement?: unknown;
34
+ page?: ConsentSourcePage;
35
+ url?: string;
36
+ };
37
+ /**
38
+ * Host seam resolving the consent sources a form's authors can reference (plugin option
39
+ * `consent.sources`). Without it there are no sources, so the `consent` field type is not
40
+ * registered at all.
41
+ *
42
+ * The intended shape: place `consentSourcesField()` on a collection or global you own, then read it
43
+ * back here. Multi-tenant hosts scope the read by the tenant derived from `req` (host header,
44
+ * cookie, or auth context) and return only that tenant's sources, so one tenant's statements are
45
+ * never selectable, resolvable, or provable under another's. Read under `req.locale` so the
46
+ * statement the visitor agrees to is the one they were shown.
47
+ *
48
+ * Do not thread the save's `req` into a `find({ locale: 'all' })` from within a form's save
49
+ * validation: it flips `req.locale` to `all` and corrupts the localized write of the document being
50
+ * saved. Read the settings document without the save's `req` (already-committed data, no transaction).
51
+ */
52
+ type ConsentSourcesResolver = (args: {
53
+ req: PayloadRequest;
54
+ /**
55
+ * The form document being read: `id`, `title`, and (at runtime) every other field. Derive
56
+ * multi-tenant scoping from a field on it, casting for non-standard fields (e.g.
57
+ * `(form as { tenant?: string }).tenant`), rather than reading the form back: reading the same
58
+ * `forms` collection from within this resolver re-enters the form's afterRead hook. The type is
59
+ * kept index-signature-free so a generated `Form` interface assigns without a cast. Treat `form`
60
+ * as read-only.
61
+ */
62
+ form: {
63
+ id: number | string;
64
+ title?: string | null;
65
+ };
66
+ }) => ConsentSourceEntry[] | Promise<ConsentSourceEntry[]>;
67
+ //#endregion
68
+ export { ConsentSourceEntry, ConsentSourcePage, ConsentSourcesResolver };
69
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,30 @@
1
+ import { Payload } from "payload";
2
+
3
+ //#region src/context/formContext.d.ts
4
+ /** A verified reference to the document a form was rendered for. Never a secret: the id is already public. */
5
+ type FormContextReference = {
6
+ relationTo: string;
7
+ value: string | number;
8
+ };
9
+ /**
10
+ * Sign a `{ relationTo, value }` reference into a compact `v1.<base64url(payload)>.<hmac>` token: a
11
+ * rendering surface mints one per document it renders a form for, the client carries it, and the plugin
12
+ * verifies it on submit. Signed (HMAC-SHA256 over the payload with `secret ?? payload.secret`), not
13
+ * encrypted: the reference is not a secret, the signature only stops rewriting. `expiresIn` (seconds,
14
+ * default one day) bounds replay.
15
+ */
16
+ declare const signFormContext: (args: {
17
+ payload: Payload;
18
+ relationTo: string;
19
+ value: string | number;
20
+ expiresIn?: number;
21
+ secret?: string;
22
+ }) => string;
23
+ /**
24
+ * Verify a token minted by `signFormContext` and return its reference, or null on any failure (wrong
25
+ * version, malformed, bad signature, or expired). Never throws, and the signature check is constant-time.
26
+ */
27
+ declare const verifyFormContext: (token: string, secret: string, nowMs?: number) => FormContextReference | null;
28
+ //#endregion
29
+ export { FormContextReference, signFormContext, verifyFormContext };
30
+ //# sourceMappingURL=formContext.d.ts.map
@@ -0,0 +1,54 @@
1
+ import { createHmac, timingSafeEqual } from "node:crypto";
2
+ //#region src/context/formContext.ts
3
+ const VERSION = "v1";
4
+ const DEFAULT_EXPIRES_IN_SECONDS = 86400;
5
+ const hmacHex = (body, secret) => createHmac("sha256", secret).update(body).digest("hex");
6
+ /**
7
+ * Sign a `{ relationTo, value }` reference into a compact `v1.<base64url(payload)>.<hmac>` token: a
8
+ * rendering surface mints one per document it renders a form for, the client carries it, and the plugin
9
+ * verifies it on submit. Signed (HMAC-SHA256 over the payload with `secret ?? payload.secret`), not
10
+ * encrypted: the reference is not a secret, the signature only stops rewriting. `expiresIn` (seconds,
11
+ * default one day) bounds replay.
12
+ */
13
+ const signFormContext = (args) => {
14
+ const { payload, relationTo, value, expiresIn, secret } = args;
15
+ const exp = Math.floor(Date.now() / 1e3) + (expiresIn ?? DEFAULT_EXPIRES_IN_SECONDS);
16
+ const body = Buffer.from(JSON.stringify({
17
+ relationTo,
18
+ value,
19
+ exp
20
+ })).toString("base64url");
21
+ return `${VERSION}.${body}.${hmacHex(body, secret ?? payload.secret)}`;
22
+ };
23
+ /**
24
+ * Verify a token minted by `signFormContext` and return its reference, or null on any failure (wrong
25
+ * version, malformed, bad signature, or expired). Never throws, and the signature check is constant-time.
26
+ */
27
+ const verifyFormContext = (token, secret, nowMs = Date.now()) => {
28
+ const parts = token.split(".");
29
+ if (parts.length !== 3 || parts[0] !== VERSION) return null;
30
+ const [, body, signature] = parts;
31
+ const expected = hmacHex(body ?? "", secret);
32
+ const given = Buffer.from(signature ?? "");
33
+ const want = Buffer.from(expected);
34
+ if (given.byteLength !== want.byteLength || !timingSafeEqual(given, want)) return null;
35
+ let parsed;
36
+ try {
37
+ parsed = JSON.parse(Buffer.from(body ?? "", "base64url").toString("utf8"));
38
+ } catch {
39
+ return null;
40
+ }
41
+ if (!parsed || typeof parsed !== "object") return null;
42
+ const { relationTo, value, exp } = parsed;
43
+ if (typeof relationTo !== "string" || relationTo.length === 0) return null;
44
+ if (typeof value !== "string" && typeof value !== "number") return null;
45
+ if (typeof exp !== "number" || exp * 1e3 <= nowMs) return null;
46
+ return {
47
+ relationTo,
48
+ value
49
+ };
50
+ };
51
+ //#endregion
52
+ export { signFormContext, verifyFormContext };
53
+
54
+ //# sourceMappingURL=formContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formContext.js","names":[],"sources":["../../src/context/formContext.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from 'node:crypto'\nimport type { Payload } from 'payload'\n\n/** A verified reference to the document a form was rendered for. Never a secret: the id is already public. */\nexport type FormContextReference = { relationTo: string; value: string | number }\n\nconst VERSION = 'v1'\nconst DEFAULT_EXPIRES_IN_SECONDS = 86_400\n\nconst hmacHex = (body: string, secret: string): string =>\n\tcreateHmac('sha256', secret).update(body).digest('hex')\n\n/**\n * Sign a `{ relationTo, value }` reference into a compact `v1.<base64url(payload)>.<hmac>` token: a\n * rendering surface mints one per document it renders a form for, the client carries it, and the plugin\n * verifies it on submit. Signed (HMAC-SHA256 over the payload with `secret ?? payload.secret`), not\n * encrypted: the reference is not a secret, the signature only stops rewriting. `expiresIn` (seconds,\n * default one day) bounds replay.\n */\nexport const signFormContext = (args: {\n\tpayload: Payload\n\trelationTo: string\n\tvalue: string | number\n\texpiresIn?: number\n\tsecret?: string\n}): string => {\n\tconst { payload, relationTo, value, expiresIn, secret } = args\n\tconst exp = Math.floor(Date.now() / 1000) + (expiresIn ?? DEFAULT_EXPIRES_IN_SECONDS)\n\tconst body = Buffer.from(JSON.stringify({ relationTo, value, exp })).toString('base64url')\n\treturn `${VERSION}.${body}.${hmacHex(body, secret ?? payload.secret)}`\n}\n\n/**\n * Verify a token minted by `signFormContext` and return its reference, or null on any failure (wrong\n * version, malformed, bad signature, or expired). Never throws, and the signature check is constant-time.\n */\nexport const verifyFormContext = (\n\ttoken: string,\n\tsecret: string,\n\tnowMs: number = Date.now()\n): FormContextReference | null => {\n\tconst parts = token.split('.')\n\tif (parts.length !== 3 || parts[0] !== VERSION) {\n\t\treturn null\n\t}\n\tconst [, body, signature] = parts\n\tconst expected = hmacHex(body ?? '', secret)\n\tconst given = Buffer.from(signature ?? '')\n\tconst want = Buffer.from(expected)\n\tif (given.byteLength !== want.byteLength || !timingSafeEqual(given, want)) {\n\t\treturn null\n\t}\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(Buffer.from(body ?? '', 'base64url').toString('utf8'))\n\t} catch {\n\t\treturn null\n\t}\n\tif (!parsed || typeof parsed !== 'object') {\n\t\treturn null\n\t}\n\tconst { relationTo, value, exp } = parsed as Record<string, unknown>\n\tif (typeof relationTo !== 'string' || relationTo.length === 0) {\n\t\treturn null\n\t}\n\tif (typeof value !== 'string' && typeof value !== 'number') {\n\t\treturn null\n\t}\n\tif (typeof exp !== 'number' || exp * 1000 <= nowMs) {\n\t\treturn null\n\t}\n\treturn { relationTo, value }\n}\n"],"mappings":";;AAMA,MAAM,UAAU;AAChB,MAAM,6BAA6B;AAEnC,MAAM,WAAW,MAAc,WAC9B,WAAW,UAAU,MAAM,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;;;;;;;;AASvD,MAAa,mBAAmB,SAMlB;CACb,MAAM,EAAE,SAAS,YAAY,OAAO,WAAW,WAAW;CAC1D,MAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,KAAK,aAAa;CAC1D,MAAM,OAAO,OAAO,KAAK,KAAK,UAAU;EAAE;EAAY;EAAO;CAAI,CAAC,CAAC,EAAE,SAAS,WAAW;CACzF,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,QAAQ,MAAM,UAAU,QAAQ,MAAM;AACpE;;;;;AAMA,MAAa,qBACZ,OACA,QACA,QAAgB,KAAK,IAAI,MACQ;CACjC,MAAM,QAAQ,MAAM,MAAM,GAAG;CAC7B,IAAI,MAAM,WAAW,KAAK,MAAM,OAAO,SACtC,OAAO;CAER,MAAM,GAAG,MAAM,aAAa;CAC5B,MAAM,WAAW,QAAQ,QAAQ,IAAI,MAAM;CAC3C,MAAM,QAAQ,OAAO,KAAK,aAAa,EAAE;CACzC,MAAM,OAAO,OAAO,KAAK,QAAQ;CACjC,IAAI,MAAM,eAAe,KAAK,cAAc,CAAC,gBAAgB,OAAO,IAAI,GACvE,OAAO;CAER,IAAI;CACJ,IAAI;EACH,SAAS,KAAK,MAAM,OAAO,KAAK,QAAQ,IAAI,WAAW,EAAE,SAAS,MAAM,CAAC;CAC1E,QAAQ;EACP,OAAO;CACR;CACA,IAAI,CAAC,UAAU,OAAO,WAAW,UAChC,OAAO;CAER,MAAM,EAAE,YAAY,OAAO,QAAQ;CACnC,IAAI,OAAO,eAAe,YAAY,WAAW,WAAW,GAC3D,OAAO;CAER,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UACjD,OAAO;CAER,IAAI,OAAO,QAAQ,YAAY,MAAM,OAAQ,OAC5C,OAAO;CAER,OAAO;EAAE;EAAY;CAAM;AAC5B"}
@@ -0,0 +1,59 @@
1
+ import { Payload, PayloadRequest } from "payload";
2
+
3
+ //#region src/email/departments.d.ts
4
+ /** One selectable department for the `emailTeam` recipients; `value` is the email address routed to. */
5
+ type DepartmentOption = {
6
+ label: string;
7
+ value: string;
8
+ };
9
+ /**
10
+ * Host seam resolving the selectable departments for `emailTeam`'s recipient fields (plugin option
11
+ * `email.departments`). Req-scoped, so multi-tenant hosts derive tenant scoping from `req` (host
12
+ * header, cookie, or auth context) and return only that tenant's departments. `value` is the literal
13
+ * string `payload.sendEmail` accepts (an email address); `label` is how the department is named in the
14
+ * admin select. Mirrors {@link FromAddressesResolver}. {@link resolveDepartmentOptions} builds this
15
+ * from a `departmentsField()`-bearing document with the reusable locale fallback, so a host does not
16
+ * reimplement it.
17
+ *
18
+ * Do not thread the save's `req` into a `find({ locale: 'all' })` from within a form's save
19
+ * validation: it flips `req.locale` to `all` and corrupts the localized write of the document being
20
+ * saved. Read the settings document without the save's `req` (already-committed data, no transaction).
21
+ */
22
+ type DepartmentEmailsResolver = (args: {
23
+ req: PayloadRequest;
24
+ }) => Promise<DepartmentOption[]> | DepartmentOption[];
25
+ type ResolveDepartmentsRequestArgs = {
26
+ /** Whether the caller is authenticated (an admin/user). */isAuthed: boolean;
27
+ req: PayloadRequest;
28
+ resolver: DepartmentEmailsResolver;
29
+ };
30
+ type ResolveDepartmentsRequestResult = {
31
+ status: number;
32
+ body: {
33
+ options: DepartmentOption[];
34
+ } | {
35
+ errors: {
36
+ message: string;
37
+ }[];
38
+ };
39
+ };
40
+ type ResolveDepartmentOptionsArgs = {
41
+ payload: Payload;
42
+ req: PayloadRequest; /** A document fetched at `locale: 'all'`, so each localized subfield is a `{ locale: value }` map. */
43
+ doc: Record<string, unknown>; /** The array field name the departments live under; defaults to `departmentEmails`. */
44
+ field?: string;
45
+ };
46
+ /**
47
+ * Build the `emailTeam` recipient options from a `departmentsField()`-bearing document (a settings
48
+ * global, a tenants row, wherever the host placed it), fetched at `locale: 'all'`. Each row's `label`
49
+ * and `email` are resolved for `req.locale` via the current -> default -> next-available chain (see
50
+ * {@link pickLocaleValue}), reading `defaultLocale`/`localeCodes` from `payload.config.localization`
51
+ * (a bare string is used as-is when localization is disabled). Rows with no resolvable email are
52
+ * dropped; a row's label is resolved independently of its email and falls back to the email when
53
+ * absent. This is the reusable seam a host's `email.departments` resolver returns, so tenant scoping
54
+ * (which document is read) stays the host's call while the locale fallback stays consistent.
55
+ */
56
+ declare const resolveDepartmentOptions: (args: ResolveDepartmentOptionsArgs) => DepartmentOption[];
57
+ //#endregion
58
+ export { DepartmentEmailsResolver, DepartmentOption, ResolveDepartmentOptionsArgs, ResolveDepartmentsRequestArgs, ResolveDepartmentsRequestResult, resolveDepartmentOptions };
59
+ //# sourceMappingURL=departments.d.ts.map
@@ -0,0 +1,87 @@
1
+ //#region src/email/departments.ts
2
+ /**
3
+ * Authorize and resolve the `GET /:id/departments` request backing the recipient selects: authenticated
4
+ * callers get the host resolver's current options for this request; anonymous callers are always
5
+ * refused. The route id is unused (the option set is request-scoped, not per-form). Statuses mirror
6
+ * `resolveFromAddressesRequest`: 403 unauthenticated, 503 when the resolver throws (fail closed).
7
+ */
8
+ const resolveDepartmentsRequest = async (args) => {
9
+ const { isAuthed, req, resolver } = args;
10
+ if (!isAuthed) return {
11
+ status: 403,
12
+ body: { errors: [{ message: "Forbidden" }] }
13
+ };
14
+ try {
15
+ return {
16
+ status: 200,
17
+ body: { options: await resolver({ req }) }
18
+ };
19
+ } catch {
20
+ return {
21
+ status: 503,
22
+ body: { errors: [{ message: "Departments unavailable" }] }
23
+ };
24
+ }
25
+ };
26
+ /**
27
+ * Resolve one localized subfield value for `reqLocale` with the current -> default -> next-available
28
+ * chain. A non-localized subfield (fetched at `locale: 'all'` on a host without localization) is a
29
+ * bare string, used as-is; a localized one is a `{ [locale]: value }` map, from which `reqLocale`,
30
+ * then `defaultLocale`, then the first non-empty configured locale wins. Empty strings are skipped
31
+ * (they fall through the chain), and undefined is returned when nothing resolves.
32
+ */
33
+ const pickLocaleValue = (value, reqLocale, defaultLocale, localeCodes) => {
34
+ if (typeof value === "string") return value.length > 0 ? value : void 0;
35
+ if (value == null || typeof value !== "object") return;
36
+ const map = value;
37
+ const nonEmpty = (entry) => typeof entry === "string" && entry.length > 0 ? entry : void 0;
38
+ const ordered = [
39
+ reqLocale,
40
+ defaultLocale,
41
+ ...localeCodes
42
+ ].filter((code) => typeof code === "string" && code.length > 0);
43
+ for (const code of ordered) {
44
+ const hit = nonEmpty(map[code]);
45
+ if (hit !== void 0) return hit;
46
+ }
47
+ for (const key of Object.keys(map)) {
48
+ const hit = nonEmpty(map[key]);
49
+ if (hit !== void 0) return hit;
50
+ }
51
+ };
52
+ /**
53
+ * Build the `emailTeam` recipient options from a `departmentsField()`-bearing document (a settings
54
+ * global, a tenants row, wherever the host placed it), fetched at `locale: 'all'`. Each row's `label`
55
+ * and `email` are resolved for `req.locale` via the current -> default -> next-available chain (see
56
+ * {@link pickLocaleValue}), reading `defaultLocale`/`localeCodes` from `payload.config.localization`
57
+ * (a bare string is used as-is when localization is disabled). Rows with no resolvable email are
58
+ * dropped; a row's label is resolved independently of its email and falls back to the email when
59
+ * absent. This is the reusable seam a host's `email.departments` resolver returns, so tenant scoping
60
+ * (which document is read) stays the host's call while the locale fallback stays consistent.
61
+ */
62
+ const resolveDepartmentOptions = (args) => {
63
+ const { payload, req, doc, field } = args;
64
+ const rows = doc[field ?? "departmentEmails"];
65
+ if (!Array.isArray(rows)) return [];
66
+ const localization = payload.config.localization;
67
+ const localeCodes = localization ? localization.localeCodes : [];
68
+ const defaultLocale = localization ? localization.defaultLocale : void 0;
69
+ const reqLocale = typeof req.locale === "string" ? req.locale : void 0;
70
+ const options = [];
71
+ for (const row of rows) {
72
+ if (row == null || typeof row !== "object") continue;
73
+ const entry = row;
74
+ const email = pickLocaleValue(entry.email, reqLocale, defaultLocale, localeCodes);
75
+ if (email === void 0) continue;
76
+ const label = pickLocaleValue(entry.label, reqLocale, defaultLocale, localeCodes) ?? email;
77
+ options.push({
78
+ label,
79
+ value: email
80
+ });
81
+ }
82
+ return options;
83
+ };
84
+ //#endregion
85
+ export { resolveDepartmentOptions, resolveDepartmentsRequest };
86
+
87
+ //# sourceMappingURL=departments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"departments.js","names":[],"sources":["../../src/email/departments.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\n\n/** One selectable department for the `emailTeam` recipients; `value` is the email address routed to. */\nexport type DepartmentOption = { label: string; value: string }\n\n/**\n * Host seam resolving the selectable departments for `emailTeam`'s recipient fields (plugin option\n * `email.departments`). Req-scoped, so multi-tenant hosts derive tenant scoping from `req` (host\n * header, cookie, or auth context) and return only that tenant's departments. `value` is the literal\n * string `payload.sendEmail` accepts (an email address); `label` is how the department is named in the\n * admin select. Mirrors {@link FromAddressesResolver}. {@link resolveDepartmentOptions} builds this\n * from a `departmentsField()`-bearing document with the reusable locale fallback, so a host does not\n * reimplement it.\n *\n * Do not thread the save's `req` into a `find({ locale: 'all' })` from within a form's save\n * validation: it flips `req.locale` to `all` and corrupts the localized write of the document being\n * saved. Read the settings document without the save's `req` (already-committed data, no transaction).\n */\nexport type DepartmentEmailsResolver = (args: {\n\treq: PayloadRequest\n}) => Promise<DepartmentOption[]> | DepartmentOption[]\n\nexport type ResolveDepartmentsRequestArgs = {\n\t/** Whether the caller is authenticated (an admin/user). */\n\tisAuthed: boolean\n\treq: PayloadRequest\n\tresolver: DepartmentEmailsResolver\n}\n\nexport type ResolveDepartmentsRequestResult = {\n\tstatus: number\n\tbody: { options: DepartmentOption[] } | { errors: { message: string }[] }\n}\n\n/**\n * Authorize and resolve the `GET /:id/departments` request backing the recipient selects: authenticated\n * callers get the host resolver's current options for this request; anonymous callers are always\n * refused. The route id is unused (the option set is request-scoped, not per-form). Statuses mirror\n * `resolveFromAddressesRequest`: 403 unauthenticated, 503 when the resolver throws (fail closed).\n */\nexport const resolveDepartmentsRequest = async (\n\targs: ResolveDepartmentsRequestArgs\n): Promise<ResolveDepartmentsRequestResult> => {\n\tconst { isAuthed, req, resolver } = args\n\tif (!isAuthed) {\n\t\treturn { status: 403, body: { errors: [{ message: 'Forbidden' }] } }\n\t}\n\ttry {\n\t\tconst options = await resolver({ req })\n\t\treturn { status: 200, body: { options } }\n\t} catch {\n\t\treturn { status: 503, body: { errors: [{ message: 'Departments unavailable' }] } }\n\t}\n}\n\n/**\n * Resolve one localized subfield value for `reqLocale` with the current -> default -> next-available\n * chain. A non-localized subfield (fetched at `locale: 'all'` on a host without localization) is a\n * bare string, used as-is; a localized one is a `{ [locale]: value }` map, from which `reqLocale`,\n * then `defaultLocale`, then the first non-empty configured locale wins. Empty strings are skipped\n * (they fall through the chain), and undefined is returned when nothing resolves.\n */\n// biome-ignore lint/complexity/useMaxParams: the locale-fallback inputs are the minimal surface (value, reqLocale, defaultLocale, localeCodes)\nexport const pickLocaleValue = (\n\tvalue: unknown,\n\treqLocale: string | undefined,\n\tdefaultLocale: string | undefined,\n\tlocaleCodes: string[]\n): string | undefined => {\n\tif (typeof value === 'string') {\n\t\treturn value.length > 0 ? value : undefined\n\t}\n\tif (value == null || typeof value !== 'object') {\n\t\treturn undefined\n\t}\n\tconst map = value as Record<string, unknown>\n\tconst nonEmpty = (entry: unknown): string | undefined =>\n\t\ttypeof entry === 'string' && entry.length > 0 ? entry : undefined\n\tconst ordered = [reqLocale, defaultLocale, ...localeCodes].filter(\n\t\t(code): code is string => typeof code === 'string' && code.length > 0\n\t)\n\tfor (const code of ordered) {\n\t\tconst hit = nonEmpty(map[code])\n\t\tif (hit !== undefined) {\n\t\t\treturn hit\n\t\t}\n\t}\n\t// Defensive: a `locale: 'all'` fetch keys by configured locales, but a key outside them still\n\t// counts toward \"next available\" so a value is never silently dropped.\n\tfor (const key of Object.keys(map)) {\n\t\tconst hit = nonEmpty(map[key])\n\t\tif (hit !== undefined) {\n\t\t\treturn hit\n\t\t}\n\t}\n\treturn undefined\n}\n\nexport type ResolveDepartmentOptionsArgs = {\n\tpayload: Payload\n\treq: PayloadRequest\n\t/** A document fetched at `locale: 'all'`, so each localized subfield is a `{ locale: value }` map. */\n\tdoc: Record<string, unknown>\n\t/** The array field name the departments live under; defaults to `departmentEmails`. */\n\tfield?: string\n}\n\n/**\n * Build the `emailTeam` recipient options from a `departmentsField()`-bearing document (a settings\n * global, a tenants row, wherever the host placed it), fetched at `locale: 'all'`. Each row's `label`\n * and `email` are resolved for `req.locale` via the current -> default -> next-available chain (see\n * {@link pickLocaleValue}), reading `defaultLocale`/`localeCodes` from `payload.config.localization`\n * (a bare string is used as-is when localization is disabled). Rows with no resolvable email are\n * dropped; a row's label is resolved independently of its email and falls back to the email when\n * absent. This is the reusable seam a host's `email.departments` resolver returns, so tenant scoping\n * (which document is read) stays the host's call while the locale fallback stays consistent.\n */\nexport const resolveDepartmentOptions = (\n\targs: ResolveDepartmentOptionsArgs\n): DepartmentOption[] => {\n\tconst { payload, req, doc, field } = args\n\tconst rows = doc[field ?? 'departmentEmails']\n\tif (!Array.isArray(rows)) {\n\t\treturn []\n\t}\n\tconst localization = payload.config.localization\n\tconst localeCodes = localization ? localization.localeCodes : []\n\tconst defaultLocale = localization ? localization.defaultLocale : undefined\n\tconst reqLocale = typeof req.locale === 'string' ? req.locale : undefined\n\tconst options: DepartmentOption[] = []\n\tfor (const row of rows) {\n\t\tif (row == null || typeof row !== 'object') {\n\t\t\tcontinue\n\t\t}\n\t\tconst entry = row as Record<string, unknown>\n\t\tconst email = pickLocaleValue(entry.email, reqLocale, defaultLocale, localeCodes)\n\t\tif (email === undefined) {\n\t\t\tcontinue\n\t\t}\n\t\tconst label = pickLocaleValue(entry.label, reqLocale, defaultLocale, localeCodes) ?? email\n\t\toptions.push({ label, value: email })\n\t}\n\treturn options\n}\n"],"mappings":";;;;;;;AAwCA,MAAa,4BAA4B,OACxC,SAC8C;CAC9C,MAAM,EAAE,UAAU,KAAK,aAAa;CACpC,IAAI,CAAC,UACJ,OAAO;EAAE,QAAQ;EAAK,MAAM,EAAE,QAAQ,CAAC,EAAE,SAAS,YAAY,CAAC,EAAE;CAAE;CAEpE,IAAI;EAEH,OAAO;GAAE,QAAQ;GAAK,MAAM,EAAE,SAAA,MADR,SAAS,EAAE,IAAI,CAAC,EACA;EAAE;CACzC,QAAQ;EACP,OAAO;GAAE,QAAQ;GAAK,MAAM,EAAE,QAAQ,CAAC,EAAE,SAAS,0BAA0B,CAAC,EAAE;EAAE;CAClF;AACD;;;;;;;;AAUA,MAAa,mBACZ,OACA,WACA,eACA,gBACwB;CACxB,IAAI,OAAO,UAAU,UACpB,OAAO,MAAM,SAAS,IAAI,QAAQ,KAAA;CAEnC,IAAI,SAAS,QAAQ,OAAO,UAAU,UACrC;CAED,MAAM,MAAM;CACZ,MAAM,YAAY,UACjB,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ,KAAA;CACzD,MAAM,UAAU;EAAC;EAAW;EAAe,GAAG;CAAW,EAAE,QACzD,SAAyB,OAAO,SAAS,YAAY,KAAK,SAAS,CACrE;CACA,KAAK,MAAM,QAAQ,SAAS;EAC3B,MAAM,MAAM,SAAS,IAAI,KAAK;EAC9B,IAAI,QAAQ,KAAA,GACX,OAAO;CAET;CAGA,KAAK,MAAM,OAAO,OAAO,KAAK,GAAG,GAAG;EACnC,MAAM,MAAM,SAAS,IAAI,IAAI;EAC7B,IAAI,QAAQ,KAAA,GACX,OAAO;CAET;AAED;;;;;;;;;;;AAqBA,MAAa,4BACZ,SACwB;CACxB,MAAM,EAAE,SAAS,KAAK,KAAK,UAAU;CACrC,MAAM,OAAO,IAAI,SAAS;CAC1B,IAAI,CAAC,MAAM,QAAQ,IAAI,GACtB,OAAO,CAAC;CAET,MAAM,eAAe,QAAQ,OAAO;CACpC,MAAM,cAAc,eAAe,aAAa,cAAc,CAAC;CAC/D,MAAM,gBAAgB,eAAe,aAAa,gBAAgB,KAAA;CAClE,MAAM,YAAY,OAAO,IAAI,WAAW,WAAW,IAAI,SAAS,KAAA;CAChE,MAAM,UAA8B,CAAC;CACrC,KAAK,MAAM,OAAO,MAAM;EACvB,IAAI,OAAO,QAAQ,OAAO,QAAQ,UACjC;EAED,MAAM,QAAQ;EACd,MAAM,QAAQ,gBAAgB,MAAM,OAAO,WAAW,eAAe,WAAW;EAChF,IAAI,UAAU,KAAA,GACb;EAED,MAAM,QAAQ,gBAAgB,MAAM,OAAO,WAAW,eAAe,WAAW,KAAK;EACrF,QAAQ,KAAK;GAAE;GAAO,OAAO;EAAM,CAAC;CACrC;CACA,OAAO;AACR"}
@@ -0,0 +1,53 @@
1
+ import { ArrayField } from "payload";
2
+
3
+ //#region src/email/departmentsField.d.ts
4
+ type DepartmentsFieldOptions = {
5
+ /** Field name, i.e. what the row array is stored under. Defaults to `departmentEmails`. */name?: string; /** Field label. Defaults to the plugin's translated one. */
6
+ label?: ArrayField['label'];
7
+ /**
8
+ * Whether each row's `email` (and its `label`) carry `localized: true`, so the same field can
9
+ * hold a different address per locale. Default `true`; Payload strips the flag on hosts without
10
+ * `localization`, so it is safe either way.
11
+ */
12
+ localized?: boolean;
13
+ };
14
+ /**
15
+ * The department-emails array, for the host to place on any collection or global they own: a
16
+ * settings global, a tenants collection, a departments parent, wherever the addresses belong. The
17
+ * plugin never registers it and never guesses where it lives; the `email.departments` resolver reads
18
+ * it back, which is also where multi-tenant scoping goes: place this on the tenant-scoped document
19
+ * and have the resolver return only that tenant's addresses, derived from `req`.
20
+ *
21
+ * A row is a `label` (how the address is named when routing a submission to it) and the `email` it
22
+ * resolves to. Because `email` is `localized` by default, a host can hold a different address per
23
+ * locale without changing the stored shape. The array itself is not localized, so per-locale edits
24
+ * must address existing rows by their own auto-assigned `id` rather than re-sending id-less rows.
25
+ *
26
+ * It renders condensed: `admin.components.Field` is {@link CondensedArray}, which lays each row out
27
+ * flat as `[label, email]` at 50% each with no per-row collapsible, while keeping Payload's native
28
+ * per-locale storage and row ids.
29
+ *
30
+ * ```ts
31
+ * // The host's own collection or global:
32
+ * fields: [departmentsField()]
33
+ *
34
+ * // The plugin, reading it back to route a submission. `resolveDepartmentOptions` reads the array
35
+ * // (fetched at `locale: 'all'`) and resolves each row's address for the requesting locale:
36
+ * formBuilder({
37
+ * email: {
38
+ * departments: async ({ req }) => {
39
+ * const settings = await req.payload.findGlobal({ slug: 'settings', depth: 0, locale: 'all' })
40
+ * return resolveDepartmentOptions({ payload: req.payload, req, doc: settings })
41
+ * },
42
+ * },
43
+ * })
44
+ * ```
45
+ *
46
+ * The read above omits the save's `req` on purpose: threading it into a `locale: 'all'` read from
47
+ * within a form's save validation flips `req.locale` to `all` and corrupts the localized write of the
48
+ * document being saved. It reads already-committed data, so no transaction (`req`) is needed.
49
+ */
50
+ declare const departmentsField: (options?: DepartmentsFieldOptions) => ArrayField;
51
+ //#endregion
52
+ export { DepartmentsFieldOptions, departmentsField };
53
+ //# sourceMappingURL=departmentsField.d.ts.map