@10x-media/form-builder 0.1.0-beta.4 → 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 (356) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/README.md +3 -3
  3. package/dist/actions/body/serializeBody.d.ts +8 -5
  4. package/dist/actions/body/serializeBody.js.map +1 -1
  5. package/dist/actions/body/textOfBody.d.ts +13 -0
  6. package/dist/actions/body/textOfBody.js +35 -0
  7. package/dist/actions/body/textOfBody.js.map +1 -0
  8. package/dist/actions/builtin/confirmation.js +41 -11
  9. package/dist/actions/builtin/confirmation.js.map +1 -1
  10. package/dist/actions/builtin/emailTeam.js +77 -38
  11. package/dist/actions/builtin/emailTeam.js.map +1 -1
  12. package/dist/actions/builtin/index.d.ts +8 -3
  13. package/dist/actions/builtin/index.js +8 -5
  14. package/dist/actions/builtin/index.js.map +1 -1
  15. package/dist/actions/defineAction.d.ts +1 -1
  16. package/dist/actions/fromAddresses.d.ts +21 -0
  17. package/dist/actions/fromAddresses.js +74 -0
  18. package/dist/actions/fromAddresses.js.map +1 -0
  19. package/dist/actions/task.js +9 -8
  20. package/dist/actions/task.js.map +1 -1
  21. package/dist/aggregation/aggregateResponses.d.ts +9 -0
  22. package/dist/aggregation/aggregateResponses.js +8 -14
  23. package/dist/aggregation/aggregateResponses.js.map +1 -1
  24. package/dist/aggregation/resolveResultsRequest.d.ts +19 -6
  25. package/dist/aggregation/resolveResultsRequest.js +58 -12
  26. package/dist/aggregation/resolveResultsRequest.js.map +1 -1
  27. package/dist/calc/computeCalcFields.js +2 -1
  28. package/dist/calc/computeCalcFields.js.map +1 -1
  29. package/dist/client/ByteSizeField.d.ts +8 -0
  30. package/dist/client/ByteSizeField.js +3 -1
  31. package/dist/client/ByteSizeField.js.map +1 -1
  32. package/dist/client/ClosePollButton.d.ts +15 -0
  33. package/dist/client/ClosePollButton.js +80 -0
  34. package/dist/client/ClosePollButton.js.map +1 -0
  35. package/dist/client/CondensedArray.d.ts +15 -0
  36. package/dist/client/CondensedArray.js +97 -0
  37. package/dist/client/CondensedArray.js.map +1 -0
  38. package/dist/client/EndpointOptionsSelect.d.ts +45 -0
  39. package/dist/client/EndpointOptionsSelect.js +103 -0
  40. package/dist/client/EndpointOptionsSelect.js.map +1 -0
  41. package/dist/client/FieldBlockLabel.d.ts +18 -0
  42. package/dist/client/FieldBlockLabel.js +34 -0
  43. package/dist/client/FieldBlockLabel.js.map +1 -0
  44. package/dist/client/FieldNameSelect.d.ts +20 -4
  45. package/dist/client/FieldNameSelect.js +25 -14
  46. package/dist/client/FieldNameSelect.js.map +1 -1
  47. package/dist/client/FlowBuilder.d.ts +2 -1
  48. package/dist/client/FlowBuilder.js +63 -57
  49. package/dist/client/FlowBuilder.js.map +1 -1
  50. package/dist/client/RuleDescription.d.ts +21 -0
  51. package/dist/client/RuleDescription.js +19 -0
  52. package/dist/client/RuleDescription.js.map +1 -0
  53. package/dist/client/admin.css +36 -0
  54. package/dist/client/endpointOptions.js +29 -0
  55. package/dist/client/endpointOptions.js.map +1 -0
  56. package/dist/client/flowAuthoring.js +114 -7
  57. package/dist/client/flowAuthoring.js.map +1 -1
  58. package/dist/client/synthesizeClientField.js +15 -8
  59. package/dist/client/synthesizeClientField.js.map +1 -1
  60. package/dist/collections/buttonFields.d.ts +34 -0
  61. package/dist/collections/buttonFields.js +35 -0
  62. package/dist/collections/buttonFields.js.map +1 -0
  63. package/dist/collections/cells/FieldCountCell.d.ts +15 -0
  64. package/dist/collections/cells/FieldCountCell.js +18 -0
  65. package/dist/collections/cells/FieldCountCell.js.map +1 -0
  66. package/dist/collections/cells/FlowStepsCell.d.ts +14 -0
  67. package/dist/collections/cells/FlowStepsCell.js +17 -0
  68. package/dist/collections/cells/FlowStepsCell.js.map +1 -0
  69. package/dist/collections/formSubmissions.js +22 -17
  70. package/dist/collections/formSubmissions.js.map +1 -1
  71. package/dist/collections/forms.js +310 -137
  72. package/dist/collections/forms.js.map +1 -1
  73. package/dist/collections/redirectFields.d.ts +29 -0
  74. package/dist/consent/applyConsentStatements.d.ts +14 -0
  75. package/dist/consent/applyConsentStatements.js +17 -0
  76. package/dist/consent/applyConsentStatements.js.map +1 -0
  77. package/dist/consent/captureConsent.d.ts +19 -8
  78. package/dist/consent/captureConsent.js +34 -11
  79. package/dist/consent/captureConsent.js.map +1 -1
  80. package/dist/consent/consentSourcesField.d.ts +58 -0
  81. package/dist/consent/consentSourcesField.js +79 -0
  82. package/dist/consent/consentSourcesField.js.map +1 -0
  83. package/dist/consent/resolveConsentEntries.d.ts +24 -0
  84. package/dist/consent/resolveConsentEntries.js +43 -0
  85. package/dist/consent/resolveConsentEntries.js.map +1 -0
  86. package/dist/consent/resolveConsentSourcesRequest.d.ts +37 -0
  87. package/dist/consent/resolveConsentSourcesRequest.js +55 -0
  88. package/dist/consent/resolveConsentSourcesRequest.js.map +1 -0
  89. package/dist/consent/resolveConsentStatements.d.ts +45 -0
  90. package/dist/consent/resolveConsentStatements.js +44 -0
  91. package/dist/consent/resolveConsentStatements.js.map +1 -0
  92. package/dist/consent/resolvePublishedVersionRef.d.ts +17 -11
  93. package/dist/consent/resolvePublishedVersionRef.js +13 -10
  94. package/dist/consent/resolvePublishedVersionRef.js.map +1 -1
  95. package/dist/consent/types.d.ts +49 -0
  96. package/dist/email/departments.d.ts +55 -0
  97. package/dist/email/departments.js +130 -0
  98. package/dist/email/departments.js.map +1 -0
  99. package/dist/email/departmentsField.d.ts +49 -0
  100. package/dist/email/departmentsField.js +69 -0
  101. package/dist/email/departmentsField.js.map +1 -0
  102. package/dist/exports/client.d.ts +8 -1
  103. package/dist/exports/client.js +8 -1
  104. package/dist/exports/react.d.ts +8 -3
  105. package/dist/exports/react.js +6 -2
  106. package/dist/exports/rsc.d.ts +3 -1
  107. package/dist/exports/rsc.js +4 -2
  108. package/dist/exports/types.d.ts +16 -8
  109. package/dist/fields/buildFieldBlocks.js +28 -13
  110. package/dist/fields/buildFieldBlocks.js.map +1 -1
  111. package/dist/fields/builtin/calculation.js +7 -0
  112. package/dist/fields/builtin/calculation.js.map +1 -1
  113. package/dist/fields/builtin/checkbox.js +8 -0
  114. package/dist/fields/builtin/checkbox.js.map +1 -1
  115. package/dist/fields/builtin/consent.js +27 -21
  116. package/dist/fields/builtin/consent.js.map +1 -1
  117. package/dist/fields/builtin/country.d.ts +13 -0
  118. package/dist/fields/builtin/country.js +30 -0
  119. package/dist/fields/builtin/country.js.map +1 -0
  120. package/dist/fields/builtin/date.js +2 -0
  121. package/dist/fields/builtin/date.js.map +1 -1
  122. package/dist/fields/builtin/file.js +5 -4
  123. package/dist/fields/builtin/file.js.map +1 -1
  124. package/dist/fields/builtin/index.d.ts +8 -6
  125. package/dist/fields/builtin/index.js +10 -9
  126. package/dist/fields/builtin/index.js.map +1 -1
  127. package/dist/fields/builtin/message.js +8 -4
  128. package/dist/fields/builtin/message.js.map +1 -1
  129. package/dist/fields/builtin/repeater.js +1 -0
  130. package/dist/fields/builtin/repeater.js.map +1 -1
  131. package/dist/fields/builtin/select.js +1 -0
  132. package/dist/fields/builtin/select.js.map +1 -1
  133. package/dist/fields/builtin/state.d.ts +13 -0
  134. package/dist/fields/builtin/state.js +30 -0
  135. package/dist/fields/builtin/state.js.map +1 -0
  136. package/dist/fields/data/regions.d.ts +21 -0
  137. package/dist/fields/data/regions.js +1238 -0
  138. package/dist/fields/data/regions.js.map +1 -0
  139. package/dist/fields/fieldKey.d.ts +15 -0
  140. package/dist/fields/fieldKey.js +16 -0
  141. package/dist/fields/fieldKey.js.map +1 -0
  142. package/dist/fields/fieldNamesOfType.js +13 -8
  143. package/dist/fields/fieldNamesOfType.js.map +1 -1
  144. package/dist/fields/instanceOptions.js +20 -0
  145. package/dist/fields/instanceOptions.js.map +1 -0
  146. package/dist/fields/registry.d.ts +1 -0
  147. package/dist/fields/registry.js +21 -2
  148. package/dist/fields/registry.js.map +1 -1
  149. package/dist/fields/sharedConfig.js +46 -9
  150. package/dist/fields/sharedConfig.js.map +1 -1
  151. package/dist/fields/types.d.ts +54 -3
  152. package/dist/flow/engine.d.ts +6 -4
  153. package/dist/flow/engine.js +11 -6
  154. package/dist/flow/engine.js.map +1 -1
  155. package/dist/flow/normalizeFlow.js +16 -7
  156. package/dist/flow/normalizeFlow.js.map +1 -1
  157. package/dist/flow/types.d.ts +12 -3
  158. package/dist/flow/types.js +11 -0
  159. package/dist/flow/types.js.map +1 -0
  160. package/dist/form/pollState.d.ts +4 -4
  161. package/dist/form/pollState.js.map +1 -1
  162. package/dist/form/toFormDocument.d.ts +37 -12
  163. package/dist/form/toFormDocument.js +43 -7
  164. package/dist/form/toFormDocument.js.map +1 -1
  165. package/dist/form/types.d.ts +31 -14
  166. package/dist/index.d.ts +143 -28
  167. package/dist/index.js +43 -14
  168. package/dist/index.js.map +1 -1
  169. package/dist/plugin/customState.js +26 -0
  170. package/dist/plugin/customState.js.map +1 -0
  171. package/dist/plugin/registerCollections.js +23 -13
  172. package/dist/plugin/registerCollections.js.map +1 -1
  173. package/dist/plugin/uploadsCollection.d.ts +2 -0
  174. package/dist/poll/applyPollOptions.js +3 -2
  175. package/dist/poll/applyPollOptions.js.map +1 -1
  176. package/dist/poll/buildPollOptionSourceFields.js +2 -5
  177. package/dist/poll/buildPollOptionSourceFields.js.map +1 -1
  178. package/dist/poll/closeJob.d.ts +52 -0
  179. package/dist/poll/closeJob.js +119 -0
  180. package/dist/poll/closeJob.js.map +1 -0
  181. package/dist/poll/definePollOptionSource.d.ts +10 -1
  182. package/dist/poll/definePollOptionSource.js.map +1 -1
  183. package/dist/poll/definePollType.d.ts +34 -0
  184. package/dist/poll/definePollType.js +6 -0
  185. package/dist/poll/definePollType.js.map +1 -0
  186. package/dist/poll/effectivePollOptions.d.ts +38 -0
  187. package/dist/poll/effectivePollOptions.js +64 -0
  188. package/dist/poll/effectivePollOptions.js.map +1 -0
  189. package/dist/poll/mostVoted.d.ts +21 -0
  190. package/dist/poll/mostVoted.js +32 -0
  191. package/dist/poll/mostVoted.js.map +1 -0
  192. package/dist/poll/outcomeBeforeChange.js +91 -0
  193. package/dist/poll/outcomeBeforeChange.js.map +1 -0
  194. package/dist/poll/outcomeFields.d.ts +38 -0
  195. package/dist/poll/outcomeFields.js +52 -0
  196. package/dist/poll/outcomeFields.js.map +1 -0
  197. package/dist/poll/pollTypeRegistry.d.ts +34 -0
  198. package/dist/poll/pollTypeRegistry.js +71 -0
  199. package/dist/poll/pollTypeRegistry.js.map +1 -0
  200. package/dist/poll/resolvePollCloseRequest.js +105 -0
  201. package/dist/poll/resolvePollCloseRequest.js.map +1 -0
  202. package/dist/poll/resolvePollOptions.d.ts +2 -1
  203. package/dist/poll/resolvePollOptions.js +5 -17
  204. package/dist/poll/resolvePollOptions.js.map +1 -1
  205. package/dist/poll/resolvePollOptionsRequest.js +51 -0
  206. package/dist/poll/resolvePollOptionsRequest.js.map +1 -0
  207. package/dist/poll/resolvePollOutcome.d.ts +22 -9
  208. package/dist/poll/resolvePollOutcome.js +54 -14
  209. package/dist/poll/resolvePollOutcome.js.map +1 -1
  210. package/dist/poll/resultsField.js +21 -0
  211. package/dist/poll/resultsField.js.map +1 -0
  212. package/dist/prefill/valuesFromSearchParams.js +2 -1
  213. package/dist/prefill/valuesFromSearchParams.js.map +1 -1
  214. package/dist/react/Form.d.ts +8 -8
  215. package/dist/react/Form.js +74 -62
  216. package/dist/react/Form.js.map +1 -1
  217. package/dist/react/FormContext.d.ts +6 -4
  218. package/dist/react/FormContext.js.map +1 -1
  219. package/dist/react/FormControls.d.ts +3 -3
  220. package/dist/react/FormControls.js +3 -3
  221. package/dist/react/FormControls.js.map +1 -1
  222. package/dist/react/FormResults.d.ts +5 -5
  223. package/dist/react/FormResults.js +6 -6
  224. package/dist/react/FormResults.js.map +1 -1
  225. package/dist/react/Poll.d.ts +2 -2
  226. package/dist/react/Poll.js +5 -5
  227. package/dist/react/Poll.js.map +1 -1
  228. package/dist/react/contract.d.ts +2 -3
  229. package/dist/react/contract.js.map +1 -1
  230. package/dist/react/primitives/Checkbox.d.ts +4 -2
  231. package/dist/react/primitives/Checkbox.js +2 -1
  232. package/dist/react/primitives/Checkbox.js.map +1 -1
  233. package/dist/react/primitives/FieldShell.d.ts +2 -4
  234. package/dist/react/primitives/FieldShell.js +12 -19
  235. package/dist/react/primitives/FieldShell.js.map +1 -1
  236. package/dist/react/renderers/calculation.js +1 -2
  237. package/dist/react/renderers/calculation.js.map +1 -1
  238. package/dist/react/renderers/checkbox.js +1 -2
  239. package/dist/react/renderers/checkbox.js.map +1 -1
  240. package/dist/react/renderers/consent.js +78 -34
  241. package/dist/react/renderers/consent.js.map +1 -1
  242. package/dist/react/renderers/date.js +1 -3
  243. package/dist/react/renderers/date.js.map +1 -1
  244. package/dist/react/renderers/email.js +1 -2
  245. package/dist/react/renderers/email.js.map +1 -1
  246. package/dist/react/renderers/file.js +1 -2
  247. package/dist/react/renderers/file.js.map +1 -1
  248. package/dist/react/renderers/index.js +3 -0
  249. package/dist/react/renderers/index.js.map +1 -1
  250. package/dist/react/renderers/number.js +1 -2
  251. package/dist/react/renderers/number.js.map +1 -1
  252. package/dist/react/renderers/region.js +45 -0
  253. package/dist/react/renderers/region.js.map +1 -0
  254. package/dist/react/renderers/repeater.js +7 -10
  255. package/dist/react/renderers/repeater.js.map +1 -1
  256. package/dist/react/renderers/select.js +1 -2
  257. package/dist/react/renderers/select.js.map +1 -1
  258. package/dist/react/renderers/text.js +1 -2
  259. package/dist/react/renderers/text.js.map +1 -1
  260. package/dist/react/renderers/textarea.js +1 -2
  261. package/dist/react/renderers/textarea.js.map +1 -1
  262. package/dist/react/state.d.ts +0 -3
  263. package/dist/react/state.js +7 -11
  264. package/dist/react/state.js.map +1 -1
  265. package/dist/react/useField.d.ts +0 -1
  266. package/dist/react/useField.js +0 -1
  267. package/dist/react/useField.js.map +1 -1
  268. package/dist/react/useFormState.d.ts +1 -1
  269. package/dist/react/useFormState.js +1 -1
  270. package/dist/react/useFormState.js.map +1 -1
  271. package/dist/react/validateField.js +3 -6
  272. package/dist/react/validateField.js.map +1 -1
  273. package/dist/recall/interpolate.d.ts +4 -3
  274. package/dist/recall/interpolate.js +5 -4
  275. package/dist/recall/interpolate.js.map +1 -1
  276. package/dist/recall/resolver.d.ts +1 -1
  277. package/dist/recall/resolver.js +14 -2
  278. package/dist/recall/resolver.js.map +1 -1
  279. package/dist/spam/captcha.d.ts +1 -1
  280. package/dist/spam/captcha.js +1 -1
  281. package/dist/spam/captcha.js.map +1 -1
  282. package/dist/submissions/SubmissionAnswers.js +6 -2
  283. package/dist/submissions/SubmissionAnswers.js.map +1 -1
  284. package/dist/submissions/SubmissionAnswersClient.js +91 -62
  285. package/dist/submissions/SubmissionAnswersClient.js.map +1 -1
  286. package/dist/submissions/createSubmission.d.ts +36 -0
  287. package/dist/submissions/createSubmission.js +26 -0
  288. package/dist/submissions/createSubmission.js.map +1 -0
  289. package/dist/submissions/runSubmission.d.ts +10 -0
  290. package/dist/submissions/runSubmission.js +29 -18
  291. package/dist/submissions/runSubmission.js.map +1 -1
  292. package/dist/submissions/types.d.ts +11 -4
  293. package/dist/submissions/validateSubmission.js +38 -23
  294. package/dist/submissions/validateSubmission.js.map +1 -1
  295. package/dist/submissions/votedCookie.js +3 -3
  296. package/dist/submissions/votedCookie.js.map +1 -1
  297. package/dist/translations/de.js +102 -26
  298. package/dist/translations/de.js.map +1 -1
  299. package/dist/translations/en.js +103 -27
  300. package/dist/translations/en.js.map +1 -1
  301. package/dist/translations/keys.d.ts +97 -21
  302. package/dist/translations/keys.js +98 -22
  303. package/dist/translations/keys.js.map +1 -1
  304. package/dist/validation/buildRuleBlocks.js +18 -17
  305. package/dist/validation/buildRuleBlocks.js.map +1 -1
  306. package/dist/validation/builtin/email.js +1 -0
  307. package/dist/validation/builtin/email.js.map +1 -1
  308. package/dist/validation/builtin/matchesField.js +3 -7
  309. package/dist/validation/builtin/matchesField.js.map +1 -1
  310. package/dist/validation/builtin/max.js +1 -0
  311. package/dist/validation/builtin/max.js.map +1 -1
  312. package/dist/validation/builtin/maxDate.js +1 -0
  313. package/dist/validation/builtin/maxDate.js.map +1 -1
  314. package/dist/validation/builtin/maxLength.js +1 -0
  315. package/dist/validation/builtin/maxLength.js.map +1 -1
  316. package/dist/validation/builtin/min.js +1 -0
  317. package/dist/validation/builtin/min.js.map +1 -1
  318. package/dist/validation/builtin/minDate.js +1 -0
  319. package/dist/validation/builtin/minDate.js.map +1 -1
  320. package/dist/validation/builtin/minLength.js +1 -0
  321. package/dist/validation/builtin/minLength.js.map +1 -1
  322. package/dist/validation/builtin/notAlreadySubmitted.js +1 -0
  323. package/dist/validation/builtin/notAlreadySubmitted.js.map +1 -1
  324. package/dist/validation/builtin/oneOf.js +1 -0
  325. package/dist/validation/builtin/oneOf.js.map +1 -1
  326. package/dist/validation/builtin/pattern.js +11 -2
  327. package/dist/validation/builtin/pattern.js.map +1 -1
  328. package/dist/validation/builtin/url.js +1 -0
  329. package/dist/validation/builtin/url.js.map +1 -1
  330. package/dist/validation/builtin/validateRegexParams.js +44 -0
  331. package/dist/validation/builtin/validateRegexParams.js.map +1 -0
  332. package/dist/validation/fieldTargetParam.d.ts +22 -0
  333. package/dist/validation/fieldTargetParam.js +38 -0
  334. package/dist/validation/fieldTargetParam.js.map +1 -0
  335. package/dist/validation/runValidation.js +8 -29
  336. package/dist/validation/runValidation.js.map +1 -1
  337. package/dist/validation/types.d.ts +5 -11
  338. package/package.json +6 -6
  339. package/dist/consent/buildConsentSourceConfig.js +0 -49
  340. package/dist/consent/buildConsentSourceConfig.js.map +0 -1
  341. package/dist/consent/builtin/index.d.ts +0 -17
  342. package/dist/consent/builtin/index.js +0 -21
  343. package/dist/consent/builtin/index.js.map +0 -1
  344. package/dist/consent/builtin/pageReference.js +0 -75
  345. package/dist/consent/builtin/pageReference.js.map +0 -1
  346. package/dist/consent/builtin/static.js +0 -48
  347. package/dist/consent/builtin/static.js.map +0 -1
  348. package/dist/consent/defineConsentSource.d.ts +0 -35
  349. package/dist/consent/defineConsentSource.js +0 -6
  350. package/dist/consent/defineConsentSource.js.map +0 -1
  351. package/dist/consent/registry.d.ts +0 -16
  352. package/dist/consent/registry.js +0 -16
  353. package/dist/consent/registry.js.map +0 -1
  354. package/dist/consent/resolveConsentLinks.d.ts +0 -19
  355. package/dist/consent/resolveConsentLinks.js +0 -26
  356. package/dist/consent/resolveConsentLinks.js.map +0 -1
@@ -3,19 +3,32 @@ import { Payload, PayloadRequest } from "payload";
3
3
  //#region src/poll/resolvePollOutcome.d.ts
4
4
  type ResolvePollOutcomeArgs = {
5
5
  payload: Payload;
6
- formId: number | string; /** A stable option value, matching one of the poll's choice values (the host's contract with `PollOption.value`). */
7
- winningValue: string;
6
+ formId: number | string;
7
+ /**
8
+ * The winning set: stable option values, each matching one of the poll's choice values (the
9
+ * host's contract with `PollOption.value`). Pass more than one for a tie, an empty array to clear
10
+ * the outcome. Omit to auto-resolve via the poll's selected outcome strategy (`poll.type`).
11
+ */
12
+ winningValues?: string[];
8
13
  req?: PayloadRequest;
9
14
  };
10
15
  /**
11
- * Record a poll's final outcome from host domain logic (e.g. after the race is decided): writes
12
- * `poll.outcome.winningValue` and stamps `resolvedAt` with the current time. The outcome fields are
13
- * admin read-only with field-level `update: () => false`, so this server-side call (with
14
- * `overrideAccess`) is the only write path. Throws when the form does not exist or is not
15
- * poll-enabled. `<Poll>` treats a present `winningValue` as the final state and highlights the
16
- * matching result bucket.
16
+ * Record a poll's final outcome server-side, in one of two modes:
17
+ * - explicit: pass `winningValues` when host code already knows the winner(s); more than one is a
18
+ * tie, and an empty array clears the outcome;
19
+ * - auto: omit it and the poll's selected outcome strategy (`poll.type`, from the `poll.types`
20
+ * registry, defaulting to `manual`) resolves the winners. `manual` is a no-op; `mostVoted` reads
21
+ * the aggregated results field; `source` delegates to the option source. A strategy that is not
22
+ * yet decidable (or `manual`) writes nothing and this returns an empty set, so auto mode is safe
23
+ * to call repeatedly from the close job and the read fallback.
24
+ *
25
+ * Every write runs through `payload.update` with `overrideAccess`, so the forms collection hook
26
+ * validates each value against the poll's effective options and owns the `poll.outcome.resolvedAt`
27
+ * stamp in both modes (auto-resolved winners are validated exactly as hand-picked ones). Throws only
28
+ * when the form does not exist or is not poll-enabled. Returns the recorded winning set (empty when
29
+ * auto mode did not decide).
17
30
  */
18
- declare const resolvePollOutcome: (args: ResolvePollOutcomeArgs) => Promise<void>;
31
+ declare const resolvePollOutcome: (args: ResolvePollOutcomeArgs) => Promise<string[]>;
19
32
  //#endregion
20
33
  export { ResolvePollOutcomeArgs, resolvePollOutcome };
21
34
  //# sourceMappingURL=resolvePollOutcome.d.ts.map
@@ -1,34 +1,74 @@
1
1
  import { pollConfigOf } from "../form/pollState.js";
2
+ import { aggregateFieldResponses } from "../aggregation/aggregateResponses.js";
3
+ import { pollTypesOf } from "./pollTypeRegistry.js";
2
4
  import { FORMS_SLUG } from "../collections/forms.js";
3
5
  //#region src/poll/resolvePollOutcome.ts
6
+ /** A strategy result narrowed to a deduped set of non-empty strings. */
7
+ const cleanWinners = (value) => value === void 0 ? [] : [...new Set(value.filter((entry) => typeof entry === "string" && entry.length > 0))];
4
8
  /**
5
- * Record a poll's final outcome from host domain logic (e.g. after the race is decided): writes
6
- * `poll.outcome.winningValue` and stamps `resolvedAt` with the current time. The outcome fields are
7
- * admin read-only with field-level `update: () => false`, so this server-side call (with
8
- * `overrideAccess`) is the only write path. Throws when the form does not exist or is not
9
- * poll-enabled. `<Poll>` treats a present `winningValue` as the final state and highlights the
10
- * matching result bucket.
9
+ * Record a poll's final outcome server-side, in one of two modes:
10
+ * - explicit: pass `winningValues` when host code already knows the winner(s); more than one is a
11
+ * tie, and an empty array clears the outcome;
12
+ * - auto: omit it and the poll's selected outcome strategy (`poll.type`, from the `poll.types`
13
+ * registry, defaulting to `manual`) resolves the winners. `manual` is a no-op; `mostVoted` reads
14
+ * the aggregated results field; `source` delegates to the option source. A strategy that is not
15
+ * yet decidable (or `manual`) writes nothing and this returns an empty set, so auto mode is safe
16
+ * to call repeatedly from the close job and the read fallback.
17
+ *
18
+ * Every write runs through `payload.update` with `overrideAccess`, so the forms collection hook
19
+ * validates each value against the poll's effective options and owns the `poll.outcome.resolvedAt`
20
+ * stamp in both modes (auto-resolved winners are validated exactly as hand-picked ones). Throws only
21
+ * when the form does not exist or is not poll-enabled. Returns the recorded winning set (empty when
22
+ * auto mode did not decide).
11
23
  */
12
24
  const resolvePollOutcome = async (args) => {
13
- const { payload, formId, winningValue, req } = args;
14
- if (pollConfigOf((await payload.findByID({
15
- collection: "forms",
25
+ const { payload, formId, req } = args;
26
+ const form = await payload.findByID({
27
+ collection: FORMS_SLUG,
16
28
  id: formId,
17
29
  depth: 0,
18
30
  overrideAccess: true,
19
31
  req
20
- })).poll)?.enabled !== true) throw new Error(`Form ${String(formId)} is not poll-enabled; cannot record an outcome.`);
32
+ });
33
+ const poll = pollConfigOf(form.poll);
34
+ if (form.pollEnabled !== true || !poll) throw new Error(`Form ${String(formId)} is not poll-enabled; cannot record an outcome.`);
35
+ let winningValues;
36
+ if (args.winningValues !== void 0) winningValues = args.winningValues;
37
+ else {
38
+ const strategyType = typeof poll.type === "string" && poll.type.length > 0 ? poll.type : "manual";
39
+ const strategy = pollTypesOf(payload).get(strategyType);
40
+ if (!strategy) return [];
41
+ let aggregated;
42
+ const aggregate = async () => {
43
+ if (aggregated === void 0) {
44
+ const resultsField = typeof poll.resultsField === "string" && poll.resultsField.length > 0 ? poll.resultsField : void 0;
45
+ aggregated = resultsField ? await aggregateFieldResponses({
46
+ payload,
47
+ formId,
48
+ field: resultsField,
49
+ req
50
+ }) : null;
51
+ }
52
+ return aggregated;
53
+ };
54
+ winningValues = cleanWinners(await strategy.resolveOutcome({
55
+ payload,
56
+ req,
57
+ form,
58
+ poll,
59
+ aggregate
60
+ }));
61
+ if (winningValues.length === 0) return [];
62
+ }
21
63
  await payload.update({
22
64
  collection: FORMS_SLUG,
23
65
  id: formId,
24
- data: { poll: { outcome: {
25
- winningValue,
26
- resolvedAt: (/* @__PURE__ */ new Date()).toISOString()
27
- } } },
66
+ data: { poll: { outcome: { winningValues } } },
28
67
  depth: 0,
29
68
  overrideAccess: true,
30
69
  req
31
70
  });
71
+ return winningValues;
32
72
  };
33
73
  //#endregion
34
74
  export { resolvePollOutcome };
@@ -1 +1 @@
1
- {"version":3,"file":"resolvePollOutcome.js","names":[],"sources":["../../src/poll/resolvePollOutcome.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport { FORMS_SLUG } from '../collections/forms'\nimport { pollConfigOf } from '../form/pollState'\n\nexport type ResolvePollOutcomeArgs = {\n\tpayload: Payload\n\tformId: number | string\n\t/** A stable option value, matching one of the poll's choice values (the host's contract with `PollOption.value`). */\n\twinningValue: string\n\treq?: PayloadRequest\n}\n\n/**\n * Record a poll's final outcome from host domain logic (e.g. after the race is decided): writes\n * `poll.outcome.winningValue` and stamps `resolvedAt` with the current time. The outcome fields are\n * admin read-only with field-level `update: () => false`, so this server-side call (with\n * `overrideAccess`) is the only write path. Throws when the form does not exist or is not\n * poll-enabled. `<Poll>` treats a present `winningValue` as the final state and highlights the\n * matching result bucket.\n */\nexport const resolvePollOutcome = async (args: ResolvePollOutcomeArgs): Promise<void> => {\n\tconst { payload, formId, winningValue, req } = args\n\tconst form = await payload.findByID({\n\t\tcollection: FORMS_SLUG,\n\t\tid: formId,\n\t\tdepth: 0,\n\t\toverrideAccess: true,\n\t\treq,\n\t})\n\tconst poll = pollConfigOf(form.poll)\n\tif (poll?.enabled !== true) {\n\t\tthrow new Error(`Form ${String(formId)} is not poll-enabled; cannot record an outcome.`)\n\t}\n\tawait payload.update({\n\t\tcollection: FORMS_SLUG,\n\t\tid: formId,\n\t\tdata: { poll: { outcome: { winningValue, resolvedAt: new Date().toISOString() } } },\n\t\tdepth: 0,\n\t\toverrideAccess: true,\n\t\treq,\n\t})\n}\n"],"mappings":";;;;;;;;;;;AAoBA,MAAa,qBAAqB,OAAO,SAAgD;CACxF,MAAM,EAAE,SAAS,QAAQ,cAAc,QAAQ;CAS/C,IADa,cAAa,MAPP,QAAQ,SAAS;EACnC,YAAA;EACA,IAAI;EACJ,OAAO;EACP,gBAAgB;EAChB;CACD,CAAC,GAC8B,IACxB,GAAG,YAAY,MACrB,MAAM,IAAI,MAAM,QAAQ,OAAO,MAAM,EAAE,gDAAgD;CAExF,MAAM,QAAQ,OAAO;EACpB,YAAY;EACZ,IAAI;EACJ,MAAM,EAAE,MAAM,EAAE,SAAS;GAAE;GAAc,6BAAY,IAAI,KAAK,GAAE,YAAY;EAAE,EAAE,EAAE;EAClF,OAAO;EACP,gBAAgB;EAChB;CACD,CAAC;AACF"}
1
+ {"version":3,"file":"resolvePollOutcome.js","names":[],"sources":["../../src/poll/resolvePollOutcome.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport { aggregateFieldResponses } from '../aggregation/aggregateResponses'\nimport type { FieldAggregation } from '../aggregation/types'\nimport { FORMS_SLUG } from '../collections/forms'\nimport { pollConfigOf } from '../form/pollState'\nimport type { PollOutcomeStrategyArgs } from './definePollType'\nimport { pollTypesOf } from './pollTypeRegistry'\n\nexport type ResolvePollOutcomeArgs = {\n\tpayload: Payload\n\tformId: number | string\n\t/**\n\t * The winning set: stable option values, each matching one of the poll's choice values (the\n\t * host's contract with `PollOption.value`). Pass more than one for a tie, an empty array to clear\n\t * the outcome. Omit to auto-resolve via the poll's selected outcome strategy (`poll.type`).\n\t */\n\twinningValues?: string[]\n\treq?: PayloadRequest\n}\n\n/** A strategy result narrowed to a deduped set of non-empty strings. */\nconst cleanWinners = (value: string[] | undefined): string[] =>\n\tvalue === undefined\n\t\t? []\n\t\t: [\n\t\t\t\t...new Set(\n\t\t\t\t\tvalue.filter((entry): entry is string => typeof entry === 'string' && entry.length > 0)\n\t\t\t\t),\n\t\t\t]\n\n/**\n * Record a poll's final outcome server-side, in one of two modes:\n * - explicit: pass `winningValues` when host code already knows the winner(s); more than one is a\n * tie, and an empty array clears the outcome;\n * - auto: omit it and the poll's selected outcome strategy (`poll.type`, from the `poll.types`\n * registry, defaulting to `manual`) resolves the winners. `manual` is a no-op; `mostVoted` reads\n * the aggregated results field; `source` delegates to the option source. A strategy that is not\n * yet decidable (or `manual`) writes nothing and this returns an empty set, so auto mode is safe\n * to call repeatedly from the close job and the read fallback.\n *\n * Every write runs through `payload.update` with `overrideAccess`, so the forms collection hook\n * validates each value against the poll's effective options and owns the `poll.outcome.resolvedAt`\n * stamp in both modes (auto-resolved winners are validated exactly as hand-picked ones). Throws only\n * when the form does not exist or is not poll-enabled. Returns the recorded winning set (empty when\n * auto mode did not decide).\n */\nexport const resolvePollOutcome = async (args: ResolvePollOutcomeArgs): Promise<string[]> => {\n\tconst { payload, formId, req } = args\n\tconst form = await payload.findByID({\n\t\tcollection: FORMS_SLUG,\n\t\tid: formId,\n\t\tdepth: 0,\n\t\toverrideAccess: true,\n\t\treq,\n\t})\n\tconst poll = pollConfigOf(form.poll)\n\tif (form.pollEnabled !== true || !poll) {\n\t\tthrow new Error(`Form ${String(formId)} is not poll-enabled; cannot record an outcome.`)\n\t}\n\n\tlet winningValues: string[]\n\tif (args.winningValues !== undefined) {\n\t\twinningValues = args.winningValues\n\t} else {\n\t\tconst strategyType =\n\t\t\ttypeof poll.type === 'string' && poll.type.length > 0 ? poll.type : 'manual'\n\t\tconst strategy = pollTypesOf(payload).get(strategyType)\n\t\tif (!strategy) {\n\t\t\treturn []\n\t\t}\n\t\tlet aggregated: FieldAggregation | null | undefined\n\t\tconst aggregate = async (): Promise<FieldAggregation | null> => {\n\t\t\tif (aggregated === undefined) {\n\t\t\t\tconst resultsField =\n\t\t\t\t\ttypeof poll.resultsField === 'string' && poll.resultsField.length > 0\n\t\t\t\t\t\t? poll.resultsField\n\t\t\t\t\t\t: undefined\n\t\t\t\taggregated = resultsField\n\t\t\t\t\t? await aggregateFieldResponses({ payload, formId, field: resultsField, req })\n\t\t\t\t\t: null\n\t\t\t}\n\t\t\treturn aggregated\n\t\t}\n\t\twinningValues = cleanWinners(\n\t\t\tawait strategy.resolveOutcome({\n\t\t\t\tpayload,\n\t\t\t\treq,\n\t\t\t\tform: form as unknown as PollOutcomeStrategyArgs['form'],\n\t\t\t\tpoll,\n\t\t\t\taggregate,\n\t\t\t})\n\t\t)\n\t\tif (winningValues.length === 0) {\n\t\t\treturn []\n\t\t}\n\t}\n\n\tawait payload.update({\n\t\tcollection: FORMS_SLUG,\n\t\tid: formId,\n\t\tdata: { poll: { outcome: { winningValues } } },\n\t\tdepth: 0,\n\t\toverrideAccess: true,\n\t\treq,\n\t})\n\treturn winningValues\n}\n"],"mappings":";;;;;;AAqBA,MAAM,gBAAgB,UACrB,UAAU,KAAA,IACP,CAAC,IACD,CACA,GAAG,IAAI,IACN,MAAM,QAAQ,UAA2B,OAAO,UAAU,YAAY,MAAM,SAAS,CAAC,CACvF,CACD;;;;;;;;;;;;;;;;;AAkBH,MAAa,qBAAqB,OAAO,SAAoD;CAC5F,MAAM,EAAE,SAAS,QAAQ,QAAQ;CACjC,MAAM,OAAO,MAAM,QAAQ,SAAS;EACnC,YAAY;EACZ,IAAI;EACJ,OAAO;EACP,gBAAgB;EAChB;CACD,CAAC;CACD,MAAM,OAAO,aAAa,KAAK,IAAI;CACnC,IAAI,KAAK,gBAAgB,QAAQ,CAAC,MACjC,MAAM,IAAI,MAAM,QAAQ,OAAO,MAAM,EAAE,gDAAgD;CAGxF,IAAI;CACJ,IAAI,KAAK,kBAAkB,KAAA,GAC1B,gBAAgB,KAAK;MACf;EACN,MAAM,eACL,OAAO,KAAK,SAAS,YAAY,KAAK,KAAK,SAAS,IAAI,KAAK,OAAO;EACrE,MAAM,WAAW,YAAY,OAAO,EAAE,IAAI,YAAY;EACtD,IAAI,CAAC,UACJ,OAAO,CAAC;EAET,IAAI;EACJ,MAAM,YAAY,YAA8C;GAC/D,IAAI,eAAe,KAAA,GAAW;IAC7B,MAAM,eACL,OAAO,KAAK,iBAAiB,YAAY,KAAK,aAAa,SAAS,IACjE,KAAK,eACL,KAAA;IACJ,aAAa,eACV,MAAM,wBAAwB;KAAE;KAAS;KAAQ,OAAO;KAAc;IAAI,CAAC,IAC3E;GACJ;GACA,OAAO;EACR;EACA,gBAAgB,aACf,MAAM,SAAS,eAAe;GAC7B;GACA;GACM;GACN;GACA;EACD,CAAC,CACF;EACA,IAAI,cAAc,WAAW,GAC5B,OAAO,CAAC;CAEV;CAEA,MAAM,QAAQ,OAAO;EACpB,YAAY;EACZ,IAAI;EACJ,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,EAAE;EAC7C,OAAO;EACP,gBAAgB;EAChB;CACD,CAAC;CACD,OAAO;AACR"}
@@ -0,0 +1,21 @@
1
+ import { fieldNamesOfType } from "../fields/fieldNamesOfType.js";
2
+ import { keys } from "../translations/keys.js";
3
+ import { asTranslate } from "../translations/server.js";
4
+ //#region src/poll/resultsField.ts
5
+ /** Slugs of registered field types choosable as a poll's results field (`pollEligible: true`). */
6
+ const pollEligibleTypes = (registry) => [...registry.values()].filter((definition) => definition.pollEligible === true).map((definition) => definition.type);
7
+ /**
8
+ * Validate for `poll.resultsField`, closed over the eligible type slugs derived from the registry
9
+ * at collection-build time (mirrors the confirmation action's `toField`): unset is fine, otherwise
10
+ * the value must name an existing, named field instance whose type is poll-eligible. Payload skips
11
+ * the validate while the poll is disabled (the field's `admin.condition`), so a stale reference on
12
+ * a disabled poll never blocks a save.
13
+ */
14
+ const buildValidateResultsField = (eligibleTypes) => (value, { data, req }) => {
15
+ if (typeof value !== "string" || value.length === 0) return true;
16
+ return fieldNamesOfType(data && typeof data === "object" ? data.fields : void 0, eligibleTypes).includes(value) ? true : asTranslate(req.t)(keys.validationResultsFieldUnknown);
17
+ };
18
+ //#endregion
19
+ export { buildValidateResultsField, pollEligibleTypes };
20
+
21
+ //# sourceMappingURL=resultsField.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resultsField.js","names":[],"sources":["../../src/poll/resultsField.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\nimport { fieldNamesOfType } from '../fields/fieldNamesOfType'\nimport type { FieldTypeRegistry } from '../fields/registry'\nimport { keys } from '../translations/keys'\nimport { asTranslate } from '../translations/server'\n\n/** Slugs of registered field types choosable as a poll's results field (`pollEligible: true`). */\nexport const pollEligibleTypes = (registry: FieldTypeRegistry): string[] =>\n\t[...registry.values()]\n\t\t.filter((definition) => definition.pollEligible === true)\n\t\t.map((definition) => definition.type)\n\n/**\n * Validate for `poll.resultsField`, closed over the eligible type slugs derived from the registry\n * at collection-build time (mirrors the confirmation action's `toField`): unset is fine, otherwise\n * the value must name an existing, named field instance whose type is poll-eligible. Payload skips\n * the validate while the poll is disabled (the field's `admin.condition`), so a stale reference on\n * a disabled poll never blocks a save.\n */\nexport const buildValidateResultsField =\n\t(eligibleTypes: readonly string[]) =>\n\t(value: unknown, { data, req }: { data?: unknown; req: PayloadRequest }): string | true => {\n\t\tif (typeof value !== 'string' || value.length === 0) {\n\t\t\treturn true\n\t\t}\n\t\tconst fields =\n\t\t\tdata && typeof data === 'object' ? (data as Record<string, unknown>).fields : undefined\n\t\treturn fieldNamesOfType(fields, eligibleTypes).includes(value)\n\t\t\t? true\n\t\t\t: asTranslate(req.t)(keys.validationResultsFieldUnknown)\n\t}\n"],"mappings":";;;;;AAOA,MAAa,qBAAqB,aACjC,CAAC,GAAG,SAAS,OAAO,CAAC,EACnB,QAAQ,eAAe,WAAW,iBAAiB,IAAI,EACvD,KAAK,eAAe,WAAW,IAAI;;;;;;;;AAStC,MAAa,6BACX,mBACA,OAAgB,EAAE,MAAM,UAAkE;CAC1F,IAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GACjD,OAAO;CAIR,OAAO,iBADN,QAAQ,OAAO,SAAS,WAAY,KAAiC,SAAS,KAAA,GAC/C,aAAa,EAAE,SAAS,KAAK,IAC1D,OACA,YAAY,IAAI,CAAC,EAAE,KAAK,6BAA6B;AACzD"}
@@ -1,3 +1,4 @@
1
+ import { isNamedField } from "../fields/fieldKey.js";
1
2
  //#region src/prefill/valuesFromSearchParams.ts
2
3
  const TRUTHY = new Set([
3
4
  "true",
@@ -24,7 +25,7 @@ const coerce = (kind, raw, all) => {
24
25
  const valuesFromSearchParams = (params, fields, registry, options = {}) => {
25
26
  const isSearch = params instanceof URLSearchParams;
26
27
  const paramKeys = isSearch ? [...new Set(params.keys())] : Object.keys(params);
27
- const byName = new Map(fields.map((field) => [field.name, field]));
28
+ const byName = new Map(fields.filter(isNamedField).map((field) => [field.name, field]));
28
29
  const result = {};
29
30
  for (const param of paramKeys) {
30
31
  const fieldName = options.map?.[param] ?? param;
@@ -1 +1 @@
1
- {"version":3,"file":"valuesFromSearchParams.js","names":[],"sources":["../../src/prefill/valuesFromSearchParams.ts"],"sourcesContent":["import type { FieldTypeRegistry } from '../fields/registry'\nimport type { FormFieldValueKind } from '../fields/types'\nimport type { FormFieldInstance } from '../submissions/types'\n\nexport type PrefillOptions = {\n\t/** Map a query-param name to a field name. Unmapped params use their own name. */\n\tmap?: Record<string, string>\n\t/** If set, only these field names may be prefilled. */\n\tallow?: string[]\n\t/** These field names may never be prefilled. */\n\tdeny?: string[]\n}\n\nconst TRUTHY = new Set(['true', '1', 'on', 'yes'])\n\nconst coerce = (kind: FormFieldValueKind, raw: string, all: string[]): unknown => {\n\tswitch (kind) {\n\t\tcase 'number': {\n\t\t\tconst n = Number(raw)\n\t\t\treturn Number.isNaN(n) ? undefined : n\n\t\t}\n\t\tcase 'boolean':\n\t\t\treturn TRUTHY.has(raw.toLowerCase())\n\t\tcase 'text[]':\n\t\t\treturn all\n\t\tdefault:\n\t\t\treturn raw\n\t}\n}\n\n/**\n * Map URL/query params to typed initial values for KNOWN fields only, coerced by each field's value\n * kind. Unknown, denied, or un-allowed params are ignored; invalid numbers are dropped. Prefilled\n * values are never trusted -- they still validate on submit. Pass the result to `<Form initialValues>`.\n */\n// biome-ignore lint/complexity/useMaxParams: four positional args are the minimal surface (params, fields, registry, options)\nexport const valuesFromSearchParams = (\n\tparams: URLSearchParams | Record<string, string>,\n\tfields: FormFieldInstance[],\n\tregistry: FieldTypeRegistry,\n\toptions: PrefillOptions = {}\n): Record<string, unknown> => {\n\tconst isSearch = params instanceof URLSearchParams\n\tconst paramKeys = isSearch ? [...new Set(params.keys())] : Object.keys(params)\n\tconst byName = new Map(fields.map((field) => [field.name, field]))\n\tconst result: Record<string, unknown> = {}\n\n\tfor (const param of paramKeys) {\n\t\tconst fieldName = options.map?.[param] ?? param\n\n\t\tif (options.allow && !options.allow.includes(fieldName)) {\n\t\t\tcontinue\n\t\t}\n\t\tif (options.deny?.includes(fieldName)) {\n\t\t\tcontinue\n\t\t}\n\n\t\tconst field = byName.get(fieldName)\n\t\tconst definition = field && registry.get(field.blockType)\n\t\t// 'none'-kind (display-only) fields carry no value, so a param under their name is ignored.\n\t\tif (!field || !definition || definition.value === 'none') {\n\t\t\tcontinue\n\t\t}\n\n\t\tconst all = isSearch ? params.getAll(param) : [params[param] as string]\n\t\tconst value = coerce(definition.value, all[0] ?? '', all)\n\t\tif (value !== undefined) {\n\t\t\tresult[fieldName] = value\n\t\t}\n\t}\n\n\treturn result\n}\n"],"mappings":";AAaA,MAAM,SAAS,IAAI,IAAI;CAAC;CAAQ;CAAK;CAAM;AAAK,CAAC;AAEjD,MAAM,UAAU,MAA0B,KAAa,QAA2B;CACjF,QAAQ,MAAR;EACC,KAAK,UAAU;GACd,MAAM,IAAI,OAAO,GAAG;GACpB,OAAO,OAAO,MAAM,CAAC,IAAI,KAAA,IAAY;EACtC;EACA,KAAK,WACJ,OAAO,OAAO,IAAI,IAAI,YAAY,CAAC;EACpC,KAAK,UACJ,OAAO;EACR,SACC,OAAO;CACT;AACD;;;;;;AAQA,MAAa,0BACZ,QACA,QACA,UACA,UAA0B,CAAC,MACE;CAC7B,MAAM,WAAW,kBAAkB;CACnC,MAAM,YAAY,WAAW,CAAC,GAAG,IAAI,IAAI,OAAO,KAAK,CAAC,CAAC,IAAI,OAAO,KAAK,MAAM;CAC7E,MAAM,SAAS,IAAI,IAAI,OAAO,KAAK,UAAU,CAAC,MAAM,MAAM,KAAK,CAAC,CAAC;CACjE,MAAM,SAAkC,CAAC;CAEzC,KAAK,MAAM,SAAS,WAAW;EAC9B,MAAM,YAAY,QAAQ,MAAM,UAAU;EAE1C,IAAI,QAAQ,SAAS,CAAC,QAAQ,MAAM,SAAS,SAAS,GACrD;EAED,IAAI,QAAQ,MAAM,SAAS,SAAS,GACnC;EAGD,MAAM,QAAQ,OAAO,IAAI,SAAS;EAClC,MAAM,aAAa,SAAS,SAAS,IAAI,MAAM,SAAS;EAExD,IAAI,CAAC,SAAS,CAAC,cAAc,WAAW,UAAU,QACjD;EAGD,MAAM,MAAM,WAAW,OAAO,OAAO,KAAK,IAAI,CAAC,OAAO,MAAgB;EACtE,MAAM,QAAQ,OAAO,WAAW,OAAO,IAAI,MAAM,IAAI,GAAG;EACxD,IAAI,UAAU,KAAA,GACb,OAAO,aAAa;CAEtB;CAEA,OAAO;AACR"}
1
+ {"version":3,"file":"valuesFromSearchParams.js","names":[],"sources":["../../src/prefill/valuesFromSearchParams.ts"],"sourcesContent":["import { isNamedField } from '../fields/fieldKey'\nimport type { FieldTypeRegistry } from '../fields/registry'\nimport type { FormFieldValueKind } from '../fields/types'\nimport type { FormFieldInstance } from '../submissions/types'\n\nexport type PrefillOptions = {\n\t/** Map a query-param name to a field name. Unmapped params use their own name. */\n\tmap?: Record<string, string>\n\t/** If set, only these field names may be prefilled. */\n\tallow?: string[]\n\t/** These field names may never be prefilled. */\n\tdeny?: string[]\n}\n\nconst TRUTHY = new Set(['true', '1', 'on', 'yes'])\n\nconst coerce = (kind: FormFieldValueKind, raw: string, all: string[]): unknown => {\n\tswitch (kind) {\n\t\tcase 'number': {\n\t\t\tconst n = Number(raw)\n\t\t\treturn Number.isNaN(n) ? undefined : n\n\t\t}\n\t\tcase 'boolean':\n\t\t\treturn TRUTHY.has(raw.toLowerCase())\n\t\tcase 'text[]':\n\t\t\treturn all\n\t\tdefault:\n\t\t\treturn raw\n\t}\n}\n\n/**\n * Map URL/query params to typed initial values for KNOWN fields only, coerced by each field's value\n * kind. Unknown, denied, or un-allowed params are ignored; invalid numbers are dropped. Prefilled\n * values are never trusted -- they still validate on submit. Pass the result to `<Form initialValues>`.\n */\n// biome-ignore lint/complexity/useMaxParams: four positional args are the minimal surface (params, fields, registry, options)\nexport const valuesFromSearchParams = (\n\tparams: URLSearchParams | Record<string, string>,\n\tfields: FormFieldInstance[],\n\tregistry: FieldTypeRegistry,\n\toptions: PrefillOptions = {}\n): Record<string, unknown> => {\n\tconst isSearch = params instanceof URLSearchParams\n\tconst paramKeys = isSearch ? [...new Set(params.keys())] : Object.keys(params)\n\tconst byName = new Map(fields.filter(isNamedField).map((field) => [field.name, field]))\n\tconst result: Record<string, unknown> = {}\n\n\tfor (const param of paramKeys) {\n\t\tconst fieldName = options.map?.[param] ?? param\n\n\t\tif (options.allow && !options.allow.includes(fieldName)) {\n\t\t\tcontinue\n\t\t}\n\t\tif (options.deny?.includes(fieldName)) {\n\t\t\tcontinue\n\t\t}\n\n\t\tconst field = byName.get(fieldName)\n\t\tconst definition = field && registry.get(field.blockType)\n\t\t// 'none'-kind (display-only) fields carry no value, so a param under their name is ignored.\n\t\tif (!field || !definition || definition.value === 'none') {\n\t\t\tcontinue\n\t\t}\n\n\t\tconst all = isSearch ? params.getAll(param) : [params[param] as string]\n\t\tconst value = coerce(definition.value, all[0] ?? '', all)\n\t\tif (value !== undefined) {\n\t\t\tresult[fieldName] = value\n\t\t}\n\t}\n\n\treturn result\n}\n"],"mappings":";;AAcA,MAAM,SAAS,IAAI,IAAI;CAAC;CAAQ;CAAK;CAAM;AAAK,CAAC;AAEjD,MAAM,UAAU,MAA0B,KAAa,QAA2B;CACjF,QAAQ,MAAR;EACC,KAAK,UAAU;GACd,MAAM,IAAI,OAAO,GAAG;GACpB,OAAO,OAAO,MAAM,CAAC,IAAI,KAAA,IAAY;EACtC;EACA,KAAK,WACJ,OAAO,OAAO,IAAI,IAAI,YAAY,CAAC;EACpC,KAAK,UACJ,OAAO;EACR,SACC,OAAO;CACT;AACD;;;;;;AAQA,MAAa,0BACZ,QACA,QACA,UACA,UAA0B,CAAC,MACE;CAC7B,MAAM,WAAW,kBAAkB;CACnC,MAAM,YAAY,WAAW,CAAC,GAAG,IAAI,IAAI,OAAO,KAAK,CAAC,CAAC,IAAI,OAAO,KAAK,MAAM;CAC7E,MAAM,SAAS,IAAI,IAAI,OAAO,OAAO,YAAY,EAAE,KAAK,UAAU,CAAC,MAAM,MAAM,KAAK,CAAC,CAAC;CACtF,MAAM,SAAkC,CAAC;CAEzC,KAAK,MAAM,SAAS,WAAW;EAC9B,MAAM,YAAY,QAAQ,MAAM,UAAU;EAE1C,IAAI,QAAQ,SAAS,CAAC,QAAQ,MAAM,SAAS,SAAS,GACrD;EAED,IAAI,QAAQ,MAAM,SAAS,SAAS,GACnC;EAGD,MAAM,QAAQ,OAAO,IAAI,SAAS;EAClC,MAAM,aAAa,SAAS,SAAS,IAAI,MAAM,SAAS;EAExD,IAAI,CAAC,SAAS,CAAC,cAAc,WAAW,UAAU,QACjD;EAGD,MAAM,MAAM,WAAW,OAAO,OAAO,KAAK,IAAI,CAAC,OAAO,MAAgB;EACtE,MAAM,QAAQ,OAAO,WAAW,OAAO,IAAI,MAAM,IAAI,GAAG;EACxD,IAAI,UAAU,KAAA,GACb,OAAO,aAAa;CAEtB;CAEA,OAAO;AACR"}
@@ -1,5 +1,5 @@
1
1
  import { AnyFormFieldDefinition } from "../fields/types.js";
2
- import { FormDisplaySettings, FormDocument, FormPollSettings, FormResponseSettings } from "../form/types.js";
2
+ import { FormButtonSettings, FormDocument, FormPollSettings, FormResponseSettings } from "../form/types.js";
3
3
  import { RendererTranslate } from "./contract.js";
4
4
  import { FormEventSink } from "../events/types.js";
5
5
  import { AnyValidationRuleDefinition } from "../validation/types.js";
@@ -23,10 +23,10 @@ type FormProps = {
23
23
  events?: FormEventSink;
24
24
  t?: RendererTranslate;
25
25
  locale?: string;
26
- layout?: boolean; /** Submit button label. Precedence: this prop, then the form's `response.submitLabel`, then the translated default. */
27
- submitLabel?: string; /** "Next" button label for multi-step forms. Defaults to the translated `'Next'`. */
28
- nextLabel?: string; /** "Back" button label for multi-step forms. Defaults to the translated `'Back'`. */
29
- backLabel?: string; /** Label for the overlay close control (modal/drawer). */
26
+ layout?: boolean; /** Submit button label. Precedence: this prop, then the form's `buttons.submitLabel`, then the translated default. */
27
+ submitLabel?: string; /** "Next" button label for multi-step forms. Precedence: this prop, then the form's `buttons.nextLabel`, then the translated default. */
28
+ nextLabel?: string; /** "Back" button label for multi-step forms. Precedence: this prop, then the form's `buttons.prevLabel`, then the translated default. */
29
+ prevLabel?: string; /** Label for the overlay close control (modal/drawer). */
30
30
  closeLabel?: string;
31
31
  successMessage?: string; /** Active presentation: a name into the registry or an inline presentation. Defaults to `'page'` when omitted. */
32
32
  presentation?: string | FormPresentation; /** Per-render presentation overrides merged onto the defaults (add, replace, or `false` to remove). */
@@ -34,8 +34,8 @@ type FormProps = {
34
34
  onClose?: () => void;
35
35
  /**
36
36
  * Accessible name for an overlay surface (modal/drawer). Hosts choosing between a trigger
37
- * label and the form's own display title should prefer `form.display?.title` when
38
- * `form.display?.showTitle` is set, falling back to their own label otherwise.
37
+ * label and the form's own admin title should prefer `form.title` when set, falling back to
38
+ * their own label otherwise.
39
39
  */
40
40
  title?: string; /** Seed initial field values (e.g. from `valuesFromSearchParams`). Still validated on submit. */
41
41
  initialValues?: Record<string, unknown>; /** Honeypot decoy (on by default). `false` removes it; `{ name }` matches a customized server `spam.honeypot.fieldName`. */
@@ -68,7 +68,7 @@ declare const Form: ({
68
68
  layout,
69
69
  submitLabel,
70
70
  nextLabel,
71
- backLabel,
71
+ prevLabel,
72
72
  closeLabel,
73
73
  successMessage,
74
74
  presentation,
@@ -4,11 +4,12 @@ import { keys } from "../translations/keys.js";
4
4
  import { serializeBody } from "../actions/body/serializeBody.js";
5
5
  import { noopEventSink } from "../events/noopSink.js";
6
6
  import { CAPTCHA_TOKEN_KEY } from "../spam/constants.js";
7
+ import { fieldKey, isNamedField } from "../fields/fieldKey.js";
7
8
  import { calcExpressionOf, computeCalcFields } from "../calc/computeCalcFields.js";
8
9
  import { evaluateCondition } from "../conditions/evaluate.js";
9
10
  import { en } from "../translations/en.js";
10
11
  import { defaultPresentationDescriptors } from "../presentations/defaults.js";
11
- import { buildRecallResolver } from "../recall/resolver.js";
12
+ import { buildRecallResolver, descriptorsFor } from "../recall/resolver.js";
12
13
  import { firstStepId, isTerminalStepId, resolveNextStepId, stepFieldNames } from "../flow/engine.js";
13
14
  import { cn } from "./cn.js";
14
15
  import { makeTranslate } from "../translations/makeTranslate.js";
@@ -31,6 +32,8 @@ import { jsx, jsxs } from "react/jsx-runtime";
31
32
  import { createElement, useCallback, useEffect, useId, useMemo, useReducer, useRef, useState } from "react";
32
33
  //#region src/react/Form.tsx
33
34
  const isEmpty = (value) => value == null || value === "" || Array.isArray(value) && value.length === 0;
35
+ /** A stored button label counts only when it is a non-empty string; anything else falls through. */
36
+ const storedLabel = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
34
37
  const FIELD_WIDTHS = new Set([
35
38
  "full",
36
39
  "half",
@@ -39,7 +42,7 @@ const FIELD_WIDTHS = new Set([
39
42
  ]);
40
43
  const FieldHost = ({ field, renderer, locale, t }) => {
41
44
  const id = useId();
42
- const { value, errors, warnings, setValue, onBlur } = useField(field.name);
45
+ const { value, errors, setValue, onBlur } = useField(field.name);
43
46
  return createElement(renderer, {
44
47
  field,
45
48
  id,
@@ -48,7 +51,6 @@ const FieldHost = ({ field, renderer, locale, t }) => {
48
51
  onChange: setValue,
49
52
  onBlur,
50
53
  errors,
51
- warnings,
52
54
  required: Boolean(field.required),
53
55
  locale,
54
56
  t
@@ -64,15 +66,32 @@ const CalcFieldHost = ({ field, renderer, value, locale, t }) => {
64
66
  onChange: () => {},
65
67
  onBlur: () => {},
66
68
  errors: [],
67
- warnings: [],
68
69
  required: false,
69
70
  disabled: true,
70
71
  locale,
71
72
  t
72
73
  });
73
74
  };
75
+ /**
76
+ * Hosts a nameless (bare) display block, e.g. a message: no `useField` binding at all. The
77
+ * renderer reads only the instance and the form context; `name` is the row key for consistency.
78
+ */
79
+ const StaticFieldHost = ({ field, renderer, locale, t }) => {
80
+ return createElement(renderer, {
81
+ field,
82
+ id: useId(),
83
+ name: fieldKey(field),
84
+ value: void 0,
85
+ onChange: () => {},
86
+ onBlur: () => {},
87
+ errors: [],
88
+ required: false,
89
+ locale,
90
+ t
91
+ });
92
+ };
74
93
  /** The headless form controller: state, progressive client validation, conditional visibility, submission, events. */
75
- const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSuccess, onError, events, t, locale = "en", layout, submitLabel, nextLabel, backLabel, closeLabel = "Close", successMessage = "Thank you.", presentation, presentations, onClose, title, initialValues, honeypot, captchaToken, children, className, renderSubmit, renderNext, renderBack, submitButtonClassName, nextButtonClassName, backButtonClassName }) => {
94
+ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSuccess, onError, events, t, locale = "en", layout, submitLabel, nextLabel, prevLabel, closeLabel = "Close", successMessage = "Thank you.", presentation, presentations, onClose, title, initialValues, honeypot, captchaToken, children, className, renderSubmit, renderNext, renderBack, submitButtonClassName, nextButtonClassName, backButtonClassName }) => {
76
95
  const honeypotName = honeypot === false ? null : honeypot?.name ?? "website";
77
96
  const honeypotRef = useRef(null);
78
97
  const registry = useMemo(() => buildFieldTypeRegistry(fieldTypes), [fieldTypes]);
@@ -80,18 +99,18 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
80
99
  const rendererRegistry = useMemo(() => resolveRenderers(defaultRenderers, renderers), [renderers]);
81
100
  const presentationRegistry = useMemo(() => resolvePresentations(defaultPresentations, presentations), [presentations]);
82
101
  const activePresentation = typeof presentation === "object" ? presentation : presentationRegistry.get(presentation ?? "page") ?? presentationRegistry.get("page") ?? defaultPresentationDescriptors.page;
83
- const fieldsByName = useMemo(() => new Map(form.fields.map((field) => [field.name, field])), [form.fields]);
102
+ const fieldsByName = useMemo(() => new Map(form.fields.filter(isNamedField).map((field) => [field.name, field])), [form.fields]);
84
103
  const translate = useMemo(() => t ?? makeTranslate(en), [t]);
85
- const docSubmitLabel = typeof form.response?.submitLabel === "string" && form.response.submitLabel.length > 0 ? form.response.submitLabel : void 0;
104
+ const docButtons = form.buttons;
86
105
  const labels = useMemo(() => ({
87
- back: backLabel ?? translate(keys.formBack),
88
- next: nextLabel ?? translate(keys.formNext),
89
- submit: submitLabel ?? docSubmitLabel ?? translate(keys.formSubmit)
106
+ prev: prevLabel ?? storedLabel(docButtons?.prevLabel) ?? translate(keys.formBack),
107
+ next: nextLabel ?? storedLabel(docButtons?.nextLabel) ?? translate(keys.formNext),
108
+ submit: submitLabel ?? storedLabel(docButtons?.submitLabel) ?? translate(keys.formSubmit)
90
109
  }), [
91
- backLabel,
110
+ prevLabel,
92
111
  nextLabel,
93
112
  submitLabel,
94
- docSubmitLabel,
113
+ docButtons,
95
114
  translate
96
115
  ]);
97
116
  const sinkRef = useRef(noopEventSink);
@@ -116,7 +135,7 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
116
135
  locale,
117
136
  translate
118
137
  ]);
119
- const flow = form.flow && form.flow.steps.length >= 2 ? form.flow : void 0;
138
+ const flow = form.multistep === true && form.flow && form.flow.steps.length >= 2 ? form.flow : void 0;
120
139
  const [currentStepId, setCurrentStepId] = useState(() => flow ? firstStepId(flow) : void 0);
121
140
  const [history, setHistory] = useState([]);
122
141
  const startedRef = useRef(false);
@@ -142,8 +161,7 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
142
161
  rawDispatch({
143
162
  type: "SET_FIELD_ISSUES",
144
163
  name,
145
- errors: [],
146
- warnings: []
164
+ errors: []
147
165
  });
148
166
  return;
149
167
  }
@@ -155,12 +173,11 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
155
173
  answers,
156
174
  locale,
157
175
  t: translate
158
- }).then(({ errors, warnings }) => {
176
+ }).then(({ errors }) => {
159
177
  rawDispatch({
160
178
  type: "SET_FIELD_ISSUES",
161
179
  name,
162
- errors,
163
- warnings
180
+ errors
164
181
  });
165
182
  const [firstError] = errors;
166
183
  if (firstError !== void 0) emitFormEvent(sinkRef.current, formIdRef.current, {
@@ -178,15 +195,25 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
178
195
  translate
179
196
  ]);
180
197
  const visible = visibleFields(form.fields, effectiveValues);
181
- /** Answered visible fields as submission values. Display-only ('none' kind) fields never contribute. */
182
- const answeredValues = () => visibleFields(form.fields, effectiveValues).filter((field) => registry.get(field.blockType)?.value !== "none").filter((field) => !isEmpty(effectiveValues[field.name])).map((field) => ({
198
+ /** Visible, answered named fields. Display-only ('none' kind) and nameless (bare) fields never contribute. */
199
+ const answerableFields = () => visibleFields(form.fields, effectiveValues).filter((field) => registry.get(field.blockType)?.value !== "none").filter(isNamedField).filter((field) => !isEmpty(effectiveValues[field.name]));
200
+ /** Answered visible fields as raw submission values, sent to the server on submit. */
201
+ const answeredValues = () => answerableFields().map((field) => ({
183
202
  field: field.name,
184
203
  value: effectiveValues[field.name]
185
204
  }));
186
- const stepVisible = (flow && currentStepId ? stepFieldNames(flow, currentStepId) : []).map((name) => visible.find((field) => field.name === name)).filter((field) => Boolean(field));
205
+ /**
206
+ * Same fields, formatted via `recall` (option labels, Yes/No, localized dates): recall-fidelity
207
+ * values for client-rendered templates, matching what the server gives email-team's body.
208
+ */
209
+ const formattedValues = () => answerableFields().map((field) => ({
210
+ field: field.name,
211
+ value: recall(field.name)
212
+ }));
213
+ const stepVisible = (flow && currentStepId ? stepFieldNames(flow, currentStepId) : []).map((key) => visible.find((field) => fieldKey(field) === key)).filter((field) => Boolean(field));
187
214
  const goNext = async () => {
188
215
  if (!flow || !currentStepId) return;
189
- const results = await Promise.all(stepVisible.filter((field) => !calcExpressionOf(field)).filter((field) => registry.get(field.blockType)?.value !== "none").filter((field) => evaluateCondition(field.validateWhen, effectiveValues)).map(async (field) => ({
216
+ const results = await Promise.all(stepVisible.filter((field) => !calcExpressionOf(field)).filter((field) => registry.get(field.blockType)?.value !== "none").filter(isNamedField).filter((field) => evaluateCondition(field.validateWhen, effectiveValues)).map(async (field) => ({
190
217
  field,
191
218
  ...await validateFieldValue({
192
219
  field,
@@ -207,8 +234,7 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
207
234
  rawDispatch({
208
235
  type: "SET_FIELD_ISSUES",
209
236
  name: result.field.name,
210
- errors: result.errors,
211
- warnings: result.warnings
237
+ errors: result.errors
212
238
  });
213
239
  if (result.errors.length > 0) hasError = true;
214
240
  }
@@ -258,7 +284,7 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
258
284
  if (submittingRef.current) return;
259
285
  submittingRef.current = true;
260
286
  const visible = visibleFields(form.fields, effectiveValues);
261
- const results = await Promise.all(visible.filter((field) => !calcExpressionOf(field)).filter((field) => registry.get(field.blockType)?.value !== "none").filter((field) => evaluateCondition(field.validateWhen, effectiveValues)).map(async (field) => ({
287
+ const results = await Promise.all(visible.filter((field) => !calcExpressionOf(field)).filter((field) => registry.get(field.blockType)?.value !== "none").filter(isNamedField).filter((field) => evaluateCondition(field.validateWhen, effectiveValues)).map(async (field) => ({
262
288
  field,
263
289
  ...await validateFieldValue({
264
290
  field,
@@ -271,22 +297,18 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
271
297
  })
272
298
  })));
273
299
  const errors = {};
274
- const warnings = {};
275
- for (const result of results) {
276
- if (result.errors.length > 0) {
277
- errors[result.field.name] = result.errors;
278
- const [firstError] = result.errors;
279
- if (firstError !== void 0) emitFormEvent(sinkRef.current, formIdRef.current, {
280
- type: "field.errored",
281
- field: result.field.name,
282
- message: firstError
283
- });
284
- }
285
- if (result.warnings.length > 0) warnings[result.field.name] = result.warnings;
300
+ for (const result of results) if (result.errors.length > 0) {
301
+ errors[result.field.name] = result.errors;
302
+ const [firstError] = result.errors;
303
+ if (firstError !== void 0) emitFormEvent(sinkRef.current, formIdRef.current, {
304
+ type: "field.errored",
305
+ field: result.field.name,
306
+ message: firstError
307
+ });
286
308
  }
287
- for (const field of visible.filter((f) => f.blockType === "repeater")) {
309
+ for (const field of visible.filter((f) => f.blockType === "repeater" && isNamedField(f))) {
288
310
  const rows = Array.isArray(effectiveValues[field.name]) ? effectiveValues[field.name] : [];
289
- const subFields = Array.isArray(field.subFields) ? field.subFields : [];
311
+ const subFields = (Array.isArray(field.subFields) ? field.subFields : []).filter(isNamedField);
290
312
  for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
291
313
  const row = rows[rowIndex] ?? {};
292
314
  for (const subField of subFields) {
@@ -303,14 +325,12 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
303
325
  });
304
326
  const compositeKey = `${field.name}[${rowIndex}].${subField.name}`;
305
327
  if (subResult.errors.length > 0) errors[compositeKey] = subResult.errors;
306
- if (subResult.warnings.length > 0) warnings[compositeKey] = subResult.warnings;
307
328
  }
308
329
  }
309
330
  }
310
331
  rawDispatch({
311
332
  type: "SET_ALL_ISSUES",
312
- errors,
313
- warnings
333
+ errors
314
334
  });
315
335
  if (Object.keys(errors).length > 0) {
316
336
  submittingRef.current = false;
@@ -352,8 +372,7 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
352
372
  } else {
353
373
  if (result.fieldErrors) rawDispatch({
354
374
  type: "SET_ALL_ISSUES",
355
- errors: result.fieldErrors,
356
- warnings: {}
375
+ errors: result.fieldErrors
357
376
  });
358
377
  const message = result.message ?? "Submission failed";
359
378
  rawDispatch({
@@ -364,6 +383,7 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
364
383
  }
365
384
  };
366
385
  const contextValue = {
386
+ form,
367
387
  state,
368
388
  dispatch,
369
389
  validateField,
@@ -418,8 +438,8 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
418
438
  if (state.submitted) {
419
439
  const responseMessage = form.response?.type === "message" || form.response?.type == null ? form.response?.message : void 0;
420
440
  const responseHtml = responseMessage ? serializeBody(responseMessage, {
421
- values: answeredValues(),
422
- descriptors: []
441
+ values: formattedValues(),
442
+ descriptors: descriptorsFor(answerableFields())
423
443
  }) : void 0;
424
444
  return /* @__PURE__ */ jsx(FormContext.Provider, {
425
445
  value: contextValue,
@@ -439,11 +459,6 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
439
459
  });
440
460
  }
441
461
  const rendered = (flow ? stepVisible : visible).filter((field) => field.hidden !== true && field.calcDisplay !== false);
442
- const displayTitleText = form.display?.showTitle && typeof form.display.title === "string" && form.display.title.length > 0 ? interpolate(form.display.title, recall) : void 0;
443
- const displayIntroHtml = form.display?.intro ? serializeBody(form.display.intro, {
444
- values: answeredValues(),
445
- descriptors: []
446
- }) : "";
447
462
  return /* @__PURE__ */ jsx(FormContext.Provider, {
448
463
  value: contextValue,
449
464
  children: wrap(/* @__PURE__ */ jsxs("form", {
@@ -457,14 +472,6 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
457
472
  name: honeypotName,
458
473
  inputRef: honeypotRef
459
474
  }) : null,
460
- displayTitleText ? /* @__PURE__ */ jsx("h2", {
461
- className: "fb-form__title",
462
- children: displayTitleText
463
- }) : null,
464
- displayIntroHtml ? /* @__PURE__ */ jsx("div", {
465
- className: "fb-form__intro",
466
- dangerouslySetInnerHTML: { __html: displayIntroHtml }
467
- }) : null,
468
475
  /* @__PURE__ */ jsx(FormLayout, {
469
476
  enabled: layout !== false,
470
477
  children: rendered.map((field) => {
@@ -474,10 +481,15 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
474
481
  const recalledField = applyRecall(field, recall);
475
482
  return /* @__PURE__ */ jsx("div", {
476
483
  ...widthProps(width),
477
- children: calcExpressionOf(field) ? /* @__PURE__ */ jsx(CalcFieldHost, {
484
+ children: !isNamedField(recalledField) ? /* @__PURE__ */ jsx(StaticFieldHost, {
485
+ field: recalledField,
486
+ renderer,
487
+ locale,
488
+ t: translate
489
+ }) : calcExpressionOf(recalledField) ? /* @__PURE__ */ jsx(CalcFieldHost, {
478
490
  field: recalledField,
479
491
  renderer,
480
- value: effectiveValues[field.name],
492
+ value: effectiveValues[recalledField.name],
481
493
  locale,
482
494
  t: translate
483
495
  }) : /* @__PURE__ */ jsx(FieldHost, {
@@ -486,7 +498,7 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
486
498
  locale,
487
499
  t: translate
488
500
  })
489
- }, field.name);
501
+ }, fieldKey(field));
490
502
  })
491
503
  }),
492
504
  state.submitError ? /* @__PURE__ */ jsx("p", {