@verbb/formie-browser 1.0.12 → 1.0.14

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 (383) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/dist/chunks/{address-country-Rx6GQTND.js → address-country-Dn71jroa.js} +10 -4
  3. package/dist/chunks/{address-finder-k7_BXaRe.js → address-finder-O49uDRoR.js} +2 -2
  4. package/dist/chunks/{address-state-BwE01m2_.js → address-state-ByYatUur.js} +154 -143
  5. package/dist/chunks/{api-DqpfkZWL.js → api-D6kJ7kXZ.js} +183 -179
  6. package/dist/chunks/{api-BUb6P-xu.js → api-DYYsVIub.js} +77 -47
  7. package/dist/chunks/{api-sPqGbOww.js → api-ghNr2uxX.js} +15 -9
  8. package/dist/chunks/{bpoint-Dlq34XH1.js → bpoint-D0RKFM4F.js} +3 -3
  9. package/dist/chunks/calculations-YEoVCG2q.js +310 -0
  10. package/dist/chunks/{captcha-eu-CGm8Lbrj.js → captcha-eu-DERJbXnH.js} +5 -4
  11. package/dist/chunks/{checkbox-radio-BY2aTCOG.js → checkbox-radio-gsm8QPJg.js} +74 -75
  12. package/dist/chunks/{combobox-C0EuExSI.js → combobox-CWNdx4Fk.js} +290 -279
  13. package/dist/chunks/conditions-DqNHLTbN.js +464 -0
  14. package/dist/chunks/country-from-ip-DDj_mFki.js +29 -0
  15. package/dist/chunks/csrf-DxHg_ZYt.js +61 -0
  16. package/dist/chunks/{custom-google-maps-BsaSirEZ.js → custom-google-maps-C4l2S3GO.js} +9 -3
  17. package/dist/chunks/{custom-link-CJ1-FjEM.js → custom-link-CLazgKYH.js} +8 -2
  18. package/dist/chunks/{custom-maps-DYW_pBid.js → custom-maps-DqcAlD0v.js} +10 -4
  19. package/dist/chunks/{date-picker-5CA_-zGb.js → date-picker-RUnevF7l.js} +456 -430
  20. package/dist/chunks/{dist-BdUy8m_p.js → dist-ZLeW0zZ3.js} +1280 -452
  21. package/dist/chunks/{eway-D9_m8ooj.js → eway-or6dtjoT.js} +3 -3
  22. package/dist/chunks/field-references.resolver-Ca8MguFv.js +104 -0
  23. package/dist/chunks/{file-upload-rqfZNqlH.js → file-upload-Bm-TXyNB.js} +104 -96
  24. package/dist/chunks/friendly-captcha-v1-GFqCt1Mh.js +34 -0
  25. package/dist/chunks/{friendly-captcha-v2-CLSAbkqx.js → friendly-captcha-v2-WoW246JG.js} +6 -5
  26. package/dist/chunks/{friendly-challenge-8uPatWqk.js → friendly-challenge-DPG9MJb1.js} +111 -102
  27. package/dist/chunks/{go-cardless-DAT3ZaQf.js → go-cardless-DzJ-0FKg.js} +2 -2
  28. package/dist/chunks/google-address-C1P1nTDM.js +163 -0
  29. package/dist/chunks/{hcaptcha-CGLAmgjO.js → hcaptcha-D0T8aIHz.js} +3 -3
  30. package/dist/chunks/{hidden-D7_Ch-QN.js → hidden-BZhXnNPe.js} +8 -2
  31. package/dist/chunks/http-C2QdHuWO.js +27 -0
  32. package/dist/chunks/{leaflet-src-j-lHNmhR.js → leaflet-src-DE6rv6M-.js} +42 -46
  33. package/dist/chunks/{loqate-CJPzBmSm.js → loqate-DD1S7PY9.js} +2 -2
  34. package/dist/chunks/{mollie-Bibhl67p.js → mollie-DG8Ju83x.js} +2 -2
  35. package/dist/chunks/{moneris-H2wSqk9h.js → moneris-C8m3Fvqu.js} +3 -3
  36. package/dist/chunks/opayo-B3EvrVxw.js +188 -0
  37. package/dist/chunks/{paddle-DkvIxQdG.js → paddle-BIYR9Um3.js} +3 -3
  38. package/dist/chunks/{password-validation-T0vDQPCr.js → password-validation-C8_uO_c_.js} +18 -12
  39. package/dist/chunks/{paypal-CQpAF7Ua.js → paypal-B_yxIo2d.js} +7 -5
  40. package/dist/chunks/{payway-CwDkC7YZ.js → payway-CeOzS0sL.js} +3 -3
  41. package/dist/chunks/{phone-country-D3ayr60L.js → phone-country-bAqje17P.js} +80 -63
  42. package/dist/chunks/{place-kit-BGHklIZ-.js → place-kit-BE5gi3vo.js} +3 -3
  43. package/dist/chunks/{placekit-autocomplete.esm-DWVabQlq.js → placekit-autocomplete.esm-CZZERYur.js} +11 -15
  44. package/dist/chunks/{recaptcha-enterprise-XPId5-bS.js → recaptcha-enterprise-CruXddZq.js} +4 -4
  45. package/dist/chunks/{recaptcha-shared-CLYJvojk.js → recaptcha-shared-AQRMDMme.js} +1 -1
  46. package/dist/chunks/{recaptcha-v2-checkbox-Cy-0PEPc.js → recaptcha-v2-checkbox-CCnJYCnG.js} +4 -4
  47. package/dist/chunks/{recaptcha-v2-invisible-BwRH540O.js → recaptcha-v2-invisible-BuVqRRYn.js} +4 -4
  48. package/dist/chunks/{recaptcha-v3-No4Ykrhk.js → recaptcha-v3-CloqhO2A.js} +4 -4
  49. package/dist/chunks/{repeater-BVnPUqTj.js → repeater-DwE27jI1.js} +25 -19
  50. package/dist/chunks/request-profile-DhwkeCpS.js +10 -0
  51. package/dist/chunks/rich-text-uV4o_pGk.js +361 -0
  52. package/dist/chunks/{chunk-3b4jIN3o.js → rolldown-runtime-DtPi1Y-2.js} +2 -2
  53. package/dist/chunks/{sdk-BhHAYgga.js → sdk-lkwSvlpM.js} +282 -234
  54. package/dist/chunks/{shared-Bx9s0i0P.js → shared-OKhSay59.js} +27 -21
  55. package/dist/chunks/{signature-xz4QO7CR.js → signature-7cldFbqj.js} +160 -112
  56. package/dist/chunks/{snaptcha-DKx98oon.js → snaptcha-C04kNGYQ.js} +2 -2
  57. package/dist/chunks/{square-DwdcOI1G.js → square-DEzxv9D9.js} +8 -6
  58. package/dist/chunks/{stripe-DaoG2FT1.js → stripe-CVPQGDz3.js} +10 -11
  59. package/dist/chunks/{summary-Dks0gXS3.js → summary-BF_8mrXI.js} +47 -45
  60. package/dist/chunks/{survey-likert-DkE6bR7B.js → survey-likert-fk1hUPff.js} +7 -1
  61. package/dist/chunks/{survey-rank-DN6x4IZM.js → survey-rank-BsLGl2LX.js} +37 -25
  62. package/dist/chunks/{survey-rating-BnsNL9U5.js → survey-rating-DVS6e6fM.js} +17 -5
  63. package/dist/chunks/{table-CAjCJHrL.js → table-DnIBdeKF.js} +21 -15
  64. package/dist/chunks/{text-limit-Bn18WrPU.js → text-limit-LGMPhFuC.js} +40 -34
  65. package/dist/chunks/{theme-classes-Tv7q7ToE.js → theme-classes-DAQuEqdP.js} +1 -1
  66. package/dist/chunks/{turnstile-DJakPzQD.js → turnstile-n9DNyPxi.js} +3 -3
  67. package/dist/chunks/{upload-manager-W-QDgxKD.js → upload-manager-BbiUosZq.js} +410 -350
  68. package/dist/chunks/{utils-Vqp8i_RM.js → utils-DpUBPadZ.js} +20 -14
  69. package/dist/css/formie-base.css +104 -1
  70. package/dist/css/theme/_messages.css +2 -0
  71. package/dist/css/theme/_tokens.css +31 -25
  72. package/dist/css/theme/fields/_signature.css +2 -1
  73. package/dist/css/theme/forms/_form.css +5 -1
  74. package/dist/index.d.ts +5 -4
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +1778 -1425
  77. package/dist/js/contracts/client.d.ts +5 -3
  78. package/dist/js/contracts/client.d.ts.map +1 -1
  79. package/dist/js/contracts/common.d.ts +2 -2
  80. package/dist/js/contracts/common.d.ts.map +1 -1
  81. package/dist/js/contracts/modules.d.ts +37 -12
  82. package/dist/js/contracts/modules.d.ts.map +1 -1
  83. package/dist/js/contracts/schema.d.ts +20 -38
  84. package/dist/js/contracts/schema.d.ts.map +1 -1
  85. package/dist/js/contracts/theme.d.ts +4 -1
  86. package/dist/js/contracts/theme.d.ts.map +1 -1
  87. package/dist/js/core/client-rendered-modules.d.ts +17 -0
  88. package/dist/js/core/client-rendered-modules.d.ts.map +1 -0
  89. package/dist/js/core/create-formie-client.d.ts.map +1 -1
  90. package/dist/js/core/field-error-aria.d.ts +23 -0
  91. package/dist/js/core/field-error-aria.d.ts.map +1 -0
  92. package/dist/js/core/hydrate-modules.d.ts +10 -8
  93. package/dist/js/core/hydrate-modules.d.ts.map +1 -1
  94. package/dist/js/core/submit-result-state.d.ts.map +1 -1
  95. package/dist/js/core/submit-result-ui.d.ts.map +1 -1
  96. package/dist/js/core/validation-focus.d.ts.map +1 -1
  97. package/dist/js/modules/address/address-finder.d.ts +1 -1
  98. package/dist/js/modules/address/address-finder.d.ts.map +1 -1
  99. package/dist/js/modules/address/factories.d.ts +3 -3
  100. package/dist/js/modules/address/factories.d.ts.map +1 -1
  101. package/dist/js/modules/address/google-address.d.ts +2 -1
  102. package/dist/js/modules/address/google-address.d.ts.map +1 -1
  103. package/dist/js/modules/address/index.d.ts +2 -2
  104. package/dist/js/modules/address/index.d.ts.map +1 -1
  105. package/dist/js/modules/address/loqate.d.ts +1 -1
  106. package/dist/js/modules/address/loqate.d.ts.map +1 -1
  107. package/dist/js/modules/address/place-kit.d.ts +1 -1
  108. package/dist/js/modules/address/place-kit.d.ts.map +1 -1
  109. package/dist/js/modules/captchas/captcha-eu.d.ts +1 -1
  110. package/dist/js/modules/captchas/captcha-eu.d.ts.map +1 -1
  111. package/dist/js/modules/captchas/factories.d.ts +11 -11
  112. package/dist/js/modules/captchas/factories.d.ts.map +1 -1
  113. package/dist/js/modules/captchas/friendly-captcha-v1.d.ts +1 -1
  114. package/dist/js/modules/captchas/friendly-captcha-v1.d.ts.map +1 -1
  115. package/dist/js/modules/captchas/friendly-captcha-v2.d.ts +1 -1
  116. package/dist/js/modules/captchas/friendly-captcha-v2.d.ts.map +1 -1
  117. package/dist/js/modules/captchas/hcaptcha.d.ts +1 -1
  118. package/dist/js/modules/captchas/hcaptcha.d.ts.map +1 -1
  119. package/dist/js/modules/captchas/index.d.ts +2 -2
  120. package/dist/js/modules/captchas/index.d.ts.map +1 -1
  121. package/dist/js/modules/captchas/recaptcha-enterprise.d.ts +1 -1
  122. package/dist/js/modules/captchas/recaptcha-enterprise.d.ts.map +1 -1
  123. package/dist/js/modules/captchas/recaptcha-v2-checkbox.d.ts +1 -1
  124. package/dist/js/modules/captchas/recaptcha-v2-checkbox.d.ts.map +1 -1
  125. package/dist/js/modules/captchas/recaptcha-v2-invisible.d.ts +1 -1
  126. package/dist/js/modules/captchas/recaptcha-v2-invisible.d.ts.map +1 -1
  127. package/dist/js/modules/captchas/recaptcha-v3.d.ts +1 -1
  128. package/dist/js/modules/captchas/recaptcha-v3.d.ts.map +1 -1
  129. package/dist/js/modules/captchas/snaptcha.d.ts +1 -1
  130. package/dist/js/modules/captchas/snaptcha.d.ts.map +1 -1
  131. package/dist/js/modules/captchas/turnstile.d.ts +1 -1
  132. package/dist/js/modules/captchas/turnstile.d.ts.map +1 -1
  133. package/dist/js/modules/fields/address-country.d.ts +2 -2
  134. package/dist/js/modules/fields/address-country.d.ts.map +1 -1
  135. package/dist/js/modules/fields/address-state.d.ts +2 -2
  136. package/dist/js/modules/fields/address-state.d.ts.map +1 -1
  137. package/dist/js/modules/fields/calculations.d.ts +2 -2
  138. package/dist/js/modules/fields/calculations.d.ts.map +1 -1
  139. package/dist/js/modules/fields/checkbox-radio.d.ts +2 -2
  140. package/dist/js/modules/fields/checkbox-radio.d.ts.map +1 -1
  141. package/dist/js/modules/fields/combobox.d.ts +2 -2
  142. package/dist/js/modules/fields/combobox.d.ts.map +1 -1
  143. package/dist/js/modules/fields/conditions/config.d.ts.map +1 -1
  144. package/dist/js/modules/fields/conditions/effects.d.ts +1 -0
  145. package/dist/js/modules/fields/conditions/effects.d.ts.map +1 -1
  146. package/dist/js/modules/fields/conditions/evaluator.d.ts +4 -1
  147. package/dist/js/modules/fields/conditions/evaluator.d.ts.map +1 -1
  148. package/dist/js/modules/fields/conditions/references.d.ts.map +1 -1
  149. package/dist/js/modules/fields/conditions/submission-context.d.ts +15 -0
  150. package/dist/js/modules/fields/conditions/submission-context.d.ts.map +1 -0
  151. package/dist/js/modules/fields/conditions/types.d.ts +3 -1
  152. package/dist/js/modules/fields/conditions/types.d.ts.map +1 -1
  153. package/dist/js/modules/fields/conditions/values.d.ts +1 -1
  154. package/dist/js/modules/fields/conditions/values.d.ts.map +1 -1
  155. package/dist/js/modules/fields/conditions.d.ts +2 -2
  156. package/dist/js/modules/fields/conditions.d.ts.map +1 -1
  157. package/dist/js/modules/fields/custom-google-maps.d.ts +2 -2
  158. package/dist/js/modules/fields/custom-google-maps.d.ts.map +1 -1
  159. package/dist/js/modules/fields/custom-link.d.ts +2 -2
  160. package/dist/js/modules/fields/custom-link.d.ts.map +1 -1
  161. package/dist/js/modules/fields/custom-maps.d.ts +2 -2
  162. package/dist/js/modules/fields/custom-maps.d.ts.map +1 -1
  163. package/dist/js/modules/fields/date-picker.d.ts +2 -2
  164. package/dist/js/modules/fields/date-picker.d.ts.map +1 -1
  165. package/dist/js/modules/fields/file-upload.d.ts +2 -2
  166. package/dist/js/modules/fields/file-upload.d.ts.map +1 -1
  167. package/dist/js/modules/fields/hidden.d.ts +2 -2
  168. package/dist/js/modules/fields/hidden.d.ts.map +1 -1
  169. package/dist/js/modules/fields/index.d.ts +2 -2
  170. package/dist/js/modules/fields/index.d.ts.map +1 -1
  171. package/dist/js/modules/fields/password-validation.d.ts +2 -2
  172. package/dist/js/modules/fields/password-validation.d.ts.map +1 -1
  173. package/dist/js/modules/fields/phone-country.d.ts +2 -2
  174. package/dist/js/modules/fields/phone-country.d.ts.map +1 -1
  175. package/dist/js/modules/fields/repeater.d.ts +2 -2
  176. package/dist/js/modules/fields/repeater.d.ts.map +1 -1
  177. package/dist/js/modules/fields/rich-text.d.ts +2 -2
  178. package/dist/js/modules/fields/rich-text.d.ts.map +1 -1
  179. package/dist/js/modules/fields/shared.d.ts +1 -0
  180. package/dist/js/modules/fields/shared.d.ts.map +1 -1
  181. package/dist/js/modules/fields/signature.d.ts +2 -2
  182. package/dist/js/modules/fields/signature.d.ts.map +1 -1
  183. package/dist/js/modules/fields/summary.d.ts +2 -2
  184. package/dist/js/modules/fields/summary.d.ts.map +1 -1
  185. package/dist/js/modules/fields/survey-likert.d.ts +2 -2
  186. package/dist/js/modules/fields/survey-likert.d.ts.map +1 -1
  187. package/dist/js/modules/fields/survey-rank.d.ts +2 -2
  188. package/dist/js/modules/fields/survey-rank.d.ts.map +1 -1
  189. package/dist/js/modules/fields/survey-rating.d.ts +2 -2
  190. package/dist/js/modules/fields/survey-rating.d.ts.map +1 -1
  191. package/dist/js/modules/fields/table.d.ts +2 -2
  192. package/dist/js/modules/fields/table.d.ts.map +1 -1
  193. package/dist/js/modules/fields/text-limit.d.ts +2 -2
  194. package/dist/js/modules/fields/text-limit.d.ts.map +1 -1
  195. package/dist/js/modules/fields/upload-manager.d.ts +2 -2
  196. package/dist/js/modules/fields/upload-manager.d.ts.map +1 -1
  197. package/dist/js/modules/loader.d.ts +10 -4
  198. package/dist/js/modules/loader.d.ts.map +1 -1
  199. package/dist/js/modules/payments/bpoint.d.ts +1 -1
  200. package/dist/js/modules/payments/bpoint.d.ts.map +1 -1
  201. package/dist/js/modules/payments/eway.d.ts +1 -1
  202. package/dist/js/modules/payments/eway.d.ts.map +1 -1
  203. package/dist/js/modules/payments/factories.d.ts +7 -7
  204. package/dist/js/modules/payments/factories.d.ts.map +1 -1
  205. package/dist/js/modules/payments/go-cardless.d.ts +1 -1
  206. package/dist/js/modules/payments/go-cardless.d.ts.map +1 -1
  207. package/dist/js/modules/payments/index.d.ts +2 -2
  208. package/dist/js/modules/payments/index.d.ts.map +1 -1
  209. package/dist/js/modules/payments/mollie.d.ts +1 -1
  210. package/dist/js/modules/payments/mollie.d.ts.map +1 -1
  211. package/dist/js/modules/payments/moneris.d.ts +1 -1
  212. package/dist/js/modules/payments/moneris.d.ts.map +1 -1
  213. package/dist/js/modules/payments/opayo.d.ts +1 -1
  214. package/dist/js/modules/payments/opayo.d.ts.map +1 -1
  215. package/dist/js/modules/payments/paddle.d.ts +1 -1
  216. package/dist/js/modules/payments/paddle.d.ts.map +1 -1
  217. package/dist/js/modules/payments/paypal.d.ts +1 -1
  218. package/dist/js/modules/payments/paypal.d.ts.map +1 -1
  219. package/dist/js/modules/payments/payway.d.ts +1 -1
  220. package/dist/js/modules/payments/payway.d.ts.map +1 -1
  221. package/dist/js/modules/payments/square.d.ts +1 -1
  222. package/dist/js/modules/payments/square.d.ts.map +1 -1
  223. package/dist/js/modules/payments/stripe.d.ts +1 -1
  224. package/dist/js/modules/payments/stripe.d.ts.map +1 -1
  225. package/dist/js/modules/payments/utils.d.ts +6 -0
  226. package/dist/js/modules/payments/utils.d.ts.map +1 -1
  227. package/dist/js/modules/registry.d.ts +6 -4
  228. package/dist/js/modules/registry.d.ts.map +1 -1
  229. package/dist/js/submit/pipeline.d.ts.map +1 -1
  230. package/dist/js/theme/browser-theme-state.generated.d.ts +24 -0
  231. package/dist/js/theme/browser-theme-state.generated.d.ts.map +1 -0
  232. package/dist/js/theme/theme-classes.d.ts +3 -3
  233. package/dist/js/theme/theme-classes.d.ts.map +1 -1
  234. package/dist/js/transport/forms-api.d.ts +5 -3
  235. package/dist/js/transport/forms-api.d.ts.map +1 -1
  236. package/dist/js/utils/country-from-ip.d.ts +2 -2
  237. package/dist/js/utils/country-from-ip.d.ts.map +1 -1
  238. package/dist/js/utils/csrf.d.ts +17 -0
  239. package/dist/js/utils/csrf.d.ts.map +1 -0
  240. package/dist/js/utils/field-references.parser.d.ts +1 -0
  241. package/dist/js/utils/field-references.parser.d.ts.map +1 -1
  242. package/dist/js/utils/field-references.resolver.d.ts.map +1 -1
  243. package/dist/js/utils/field-references.types.d.ts +1 -0
  244. package/dist/js/utils/field-references.types.d.ts.map +1 -1
  245. package/dist/js/utils/http.d.ts +3 -1
  246. package/dist/js/utils/http.d.ts.map +1 -1
  247. package/dist/js/utils/request-profile.d.ts +4 -0
  248. package/dist/js/utils/request-profile.d.ts.map +1 -0
  249. package/dist/js/utils/unload-warning.d.ts.map +1 -1
  250. package/dist/js/validation/rules/email.d.ts.map +1 -1
  251. package/dist/js/validation/rules/match.d.ts.map +1 -1
  252. package/dist/js/validation/rules/number.d.ts.map +1 -1
  253. package/dist/js/validation/rules/required.d.ts.map +1 -1
  254. package/dist/js/validation/rules/shared.d.ts.map +1 -1
  255. package/dist/js/validation/rules/url.d.ts.map +1 -1
  256. package/dist/js/validation/skip.d.ts +7 -0
  257. package/dist/js/validation/skip.d.ts.map +1 -0
  258. package/dist/js/validation/submit-readiness.d.ts.map +1 -1
  259. package/dist/js/validation/validator.d.ts.map +1 -1
  260. package/package.json +43 -12
  261. package/src/css/formie-base.css +104 -1
  262. package/src/css/theme/_messages.css +2 -0
  263. package/src/css/theme/_tokens.css +31 -25
  264. package/src/css/theme/fields/_signature.css +2 -1
  265. package/src/css/theme/forms/_form.css +5 -1
  266. package/src/index.ts +13 -8
  267. package/src/js/contracts/client.ts +5 -3
  268. package/src/js/contracts/common.ts +5 -6
  269. package/src/js/contracts/modules.ts +41 -12
  270. package/src/js/contracts/schema.ts +21 -43
  271. package/src/js/contracts/theme.ts +6 -1
  272. package/src/js/core/client-rendered-modules.ts +69 -0
  273. package/src/js/core/create-formie-client.ts +119 -73
  274. package/src/js/core/field-error-aria.ts +93 -0
  275. package/src/js/core/hydrate-modules.ts +54 -29
  276. package/src/js/core/submit-result-state.ts +34 -45
  277. package/src/js/core/submit-result-ui.ts +21 -65
  278. package/src/js/core/validation-focus.ts +20 -20
  279. package/src/js/modules/address/address-finder.ts +1 -1
  280. package/src/js/modules/address/factories.ts +15 -13
  281. package/src/js/modules/address/google-address.ts +10 -6
  282. package/src/js/modules/address/index.ts +2 -2
  283. package/src/js/modules/address/loqate.ts +1 -1
  284. package/src/js/modules/address/place-kit.ts +1 -1
  285. package/src/js/modules/captchas/captcha-eu.ts +2 -2
  286. package/src/js/modules/captchas/factories.ts +66 -42
  287. package/src/js/modules/captchas/friendly-captcha-v1.ts +12 -2
  288. package/src/js/modules/captchas/friendly-captcha-v2.ts +4 -2
  289. package/src/js/modules/captchas/hcaptcha.ts +5 -5
  290. package/src/js/modules/captchas/index.ts +2 -2
  291. package/src/js/modules/captchas/recaptcha-enterprise.ts +2 -2
  292. package/src/js/modules/captchas/recaptcha-v2-checkbox.ts +3 -3
  293. package/src/js/modules/captchas/recaptcha-v2-invisible.ts +2 -2
  294. package/src/js/modules/captchas/recaptcha-v3.ts +2 -2
  295. package/src/js/modules/captchas/snaptcha.ts +1 -1
  296. package/src/js/modules/captchas/turnstile.ts +3 -3
  297. package/src/js/modules/fields/address-country.ts +6 -4
  298. package/src/js/modules/fields/address-state.ts +20 -12
  299. package/src/js/modules/fields/calculations.ts +5 -3
  300. package/src/js/modules/fields/checkbox-radio.ts +24 -42
  301. package/src/js/modules/fields/combobox.ts +25 -9
  302. package/src/js/modules/fields/conditions/config.ts +17 -20
  303. package/src/js/modules/fields/conditions/effects.ts +23 -26
  304. package/src/js/modules/fields/conditions/evaluator.ts +21 -29
  305. package/src/js/modules/fields/conditions/references.ts +16 -5
  306. package/src/js/modules/fields/conditions/submission-context.ts +80 -0
  307. package/src/js/modules/fields/conditions/types.ts +3 -1
  308. package/src/js/modules/fields/conditions/values.ts +31 -6
  309. package/src/js/modules/fields/conditions.ts +59 -18
  310. package/src/js/modules/fields/custom-google-maps.ts +5 -3
  311. package/src/js/modules/fields/custom-link.ts +5 -3
  312. package/src/js/modules/fields/custom-maps.ts +5 -3
  313. package/src/js/modules/fields/date-picker.ts +40 -4
  314. package/src/js/modules/fields/file-upload.ts +19 -10
  315. package/src/js/modules/fields/hidden.ts +5 -3
  316. package/src/js/modules/fields/index.ts +2 -2
  317. package/src/js/modules/fields/password-validation.ts +5 -3
  318. package/src/js/modules/fields/phone-country.ts +33 -6
  319. package/src/js/modules/fields/repeater.ts +5 -3
  320. package/src/js/modules/fields/rich-text.ts +20 -4
  321. package/src/js/modules/fields/shared.ts +13 -0
  322. package/src/js/modules/fields/signature.ts +64 -6
  323. package/src/js/modules/fields/summary.ts +8 -20
  324. package/src/js/modules/fields/survey-likert.ts +5 -3
  325. package/src/js/modules/fields/survey-rank.ts +21 -3
  326. package/src/js/modules/fields/survey-rating.ts +17 -3
  327. package/src/js/modules/fields/table.ts +5 -3
  328. package/src/js/modules/fields/text-limit.ts +5 -3
  329. package/src/js/modules/fields/upload-manager.ts +190 -49
  330. package/src/js/modules/loader.ts +345 -281
  331. package/src/js/modules/payments/bpoint.ts +2 -2
  332. package/src/js/modules/payments/eway.ts +2 -2
  333. package/src/js/modules/payments/factories.ts +53 -52
  334. package/src/js/modules/payments/go-cardless.ts +1 -1
  335. package/src/js/modules/payments/index.ts +2 -2
  336. package/src/js/modules/payments/mollie.ts +1 -1
  337. package/src/js/modules/payments/moneris.ts +2 -2
  338. package/src/js/modules/payments/opayo.ts +21 -8
  339. package/src/js/modules/payments/paddle.ts +10 -6
  340. package/src/js/modules/payments/payment.ts +22 -18
  341. package/src/js/modules/payments/paypal.ts +1 -1
  342. package/src/js/modules/payments/payway.ts +2 -2
  343. package/src/js/modules/payments/square.ts +2 -2
  344. package/src/js/modules/payments/stripe.ts +5 -5
  345. package/src/js/modules/payments/utils.ts +14 -0
  346. package/src/js/modules/registry.ts +19 -11
  347. package/src/js/submit/pipeline.ts +10 -11
  348. package/src/js/theme/browser-theme-state.generated.ts +25 -0
  349. package/src/js/theme/theme-classes.ts +3 -3
  350. package/src/js/transport/forms-api.ts +66 -67
  351. package/src/js/utils/country-from-ip.ts +14 -9
  352. package/src/js/utils/csrf.ts +136 -0
  353. package/src/js/utils/field-references.parser.ts +13 -113
  354. package/src/js/utils/field-references.resolver.ts +27 -64
  355. package/src/js/utils/field-references.row-scope.ts +1 -1
  356. package/src/js/utils/field-references.types.ts +1 -0
  357. package/src/js/utils/http.ts +5 -5
  358. package/src/js/utils/request-profile.ts +10 -0
  359. package/src/js/utils/unload-warning.ts +12 -4
  360. package/src/js/validation/rules/email.ts +3 -5
  361. package/src/js/validation/rules/match.ts +2 -1
  362. package/src/js/validation/rules/minmax.ts +4 -43
  363. package/src/js/validation/rules/number.ts +3 -47
  364. package/src/js/validation/rules/pattern.ts +3 -8
  365. package/src/js/validation/rules/required.ts +2 -1
  366. package/src/js/validation/rules/shared.ts +5 -1
  367. package/src/js/validation/rules/url.ts +3 -10
  368. package/src/js/validation/skip.ts +9 -0
  369. package/src/js/validation/submit-readiness.ts +9 -2
  370. package/src/js/validation/validator.ts +35 -59
  371. package/dist/chunks/calculations-DERi2xaC.js +0 -299
  372. package/dist/chunks/conditions-65kBcFWW.js +0 -424
  373. package/dist/chunks/country-from-ip-BwxKaBuJ.js +0 -26
  374. package/dist/chunks/field-references.resolver-CHwn0G0L.js +0 -145
  375. package/dist/chunks/friendly-captcha-v1-C_2reOXr.js +0 -31
  376. package/dist/chunks/google-address-BH3Zhi06.js +0 -161
  377. package/dist/chunks/http-D-JExro7.js +0 -27
  378. package/dist/chunks/opayo-CR0DtsLa.js +0 -179
  379. package/dist/chunks/rich-text-Bg_--4TG.js +0 -349
  380. package/dist/js/modules/fields/conditions/transforms.d.ts +0 -3
  381. package/dist/js/modules/fields/conditions/transforms.d.ts.map +0 -1
  382. package/src/js/modules/fields/conditions/transforms.ts +0 -206
  383. package/src/js/modules/payments/stub-payment-module.ts +0 -87
@@ -1,7 +1,6 @@
1
1
  import type { FormEventUnsubscribe } from '#contracts/client';
2
- import type { FormMode } from '#contracts/common';
3
- import type { FormieModuleDefinition, FormieModuleInstance, ModuleRegistrationOptions } from '#contracts/modules';
4
- import type { FormModuleManifest } from '#contracts/schema';
2
+ import type { BrowserModuleDefinition, BrowserModuleHydrationReport, ModuleRegistrationOptions } from '#contracts/modules';
3
+ import type { BrowserModuleEntry } from '#contracts/schema';
5
4
  import { EventBus } from '#events/event-bus';
6
5
  import { loadModulesFromManifest } from '#modules/loader';
7
6
  import { ModuleRegistry } from '#modules/registry';
@@ -10,34 +9,38 @@ import { createDebug } from '#utils/debug';
10
9
  export type FormieModuleHydratorOptions = {
11
10
  root: Element;
12
11
  form?: HTMLFormElement | null;
13
- modules?: FormModuleManifest[];
14
- mode?: FormMode;
12
+ modules?: import('@verbb/formie-core').BrowserModuleManifest;
13
+ surface?: import('@verbb/formie-core').BrowserSurface;
15
14
  registry?: ModuleRegistry;
16
15
  };
17
16
 
18
- export type FormieModuleHydrator = {
17
+ export type FormieModuleHydrator = BrowserModuleHydrationReport & {
18
+ assertReady: () => void;
19
+ prepare: (action: import('@verbb/formie-core').ClientSubmitAction) => Promise<Record<string, unknown>>;
20
+ result: (result: import('#contracts/schema').FormSubmitResult) => Promise<void>;
21
+ update: (manifest: import('@verbb/formie-core').BrowserModuleManifest) => Promise<void>;
19
22
  destroy: () => Promise<void>;
20
23
  on: (eventName: string, callback: (payload: unknown) => void | Promise<void>) => FormEventUnsubscribe;
21
24
  emit: (eventName: string, payload?: unknown) => Promise<void>;
22
- registerModule: (moduleDefinition: FormieModuleDefinition, options?: ModuleRegistrationOptions) => boolean;
25
+ registerModule: (moduleDefinition: BrowserModuleDefinition, options?: ModuleRegistrationOptions) => boolean;
23
26
  unregisterModule: (moduleId: string) => void;
24
- getRegisteredModules: () => FormieModuleDefinition[];
27
+ getRegisteredModules: () => BrowserModuleDefinition[];
25
28
  };
26
29
 
27
30
  const debug = createDebug('general', 'module-hydrator');
28
31
 
29
32
  export async function hydrateFormieModules(options: FormieModuleHydratorOptions): Promise<FormieModuleHydrator> {
30
33
  const root = options.root;
31
- const form = options.form ?? (root instanceof HTMLFormElement ? root : root.closest('form'));
32
- const modules = options.modules ?? [];
33
- const mode = options.mode ?? 'server-rendered';
34
+ const form = options.form ?? (root instanceof HTMLFormElement ? root : root.closest('form') ?? root.querySelector('form'));
35
+ const surface = options.surface ?? 'cp-edit';
36
+ const modules = options.modules ?? { contractVersion: 2, surface, entries: [] };
34
37
  const registry = options.registry ?? new ModuleRegistry();
35
38
  const bus = new EventBus();
36
39
 
37
40
  // This helper reuses the canonical module manifest loader without mounting the
38
- // full form client, which lets non-frontend hosts like the CP opt into shared
41
+ // full form client, which lets CP edit hosts opt into shared
39
42
  // field modules without inheriting submit or pagination ownership.
40
- const instances = await loadModulesFromManifest(modules, {
43
+ const runtime = await loadModulesFromManifest(modules, {
41
44
  registry,
42
45
  setupContext: {
43
46
  formId: form?.id || (root as HTMLElement).id || 'formie-modules',
@@ -45,31 +48,64 @@ export async function hydrateFormieModules(options: FormieModuleHydratorOptions)
45
48
  form,
46
49
  target: root,
47
50
  scope: 'form',
51
+ surface,
48
52
  state: {},
49
53
  options: {},
50
54
  on: (eventName, callback) => {
51
55
  return bus.on(eventName, callback);
52
56
  },
53
57
  emit: async(eventName, payload) => {
58
+ root.dispatchEvent(new CustomEvent(eventName, { detail: payload, bubbles: true }));
54
59
  await bus.emit(eventName, payload);
55
60
  },
56
61
  },
57
62
  matchContext: {
58
63
  root,
59
64
  form,
60
- mode,
65
+ surface,
61
66
  },
62
67
  });
63
68
 
64
69
  debug.log('Hydrated module manifest.', {
65
- moduleCount: modules.length,
66
- instanceCount: instances.length,
67
- mode,
70
+ moduleCount: modules.entries.length,
71
+ instanceCount: runtime.instances.length,
72
+ surface,
68
73
  });
69
74
 
70
75
  return {
76
+ get instances() { return runtime.instances; },
77
+ get failures() { return runtime.failures; },
78
+ prepare: async(action) => {
79
+ if (!form) throw new Error('Browser modules require a mounted form element.');
80
+ runtime.assertReady();
81
+ runtime.instances.forEach((instance) => instance.assertReady?.());
82
+ let reason: string | undefined;
83
+ const context = {
84
+ form, action: action === 'back' || action === 'save' ? action : 'submit' as const,
85
+ formData: new FormData(form),
86
+ abort: (message?: string) => { reason = message || 'A form feature could not complete.'; },
87
+ isAborted: () => Boolean(reason), abortReason: () => reason,
88
+ };
89
+ const priority = { core: 0, field: 0, address: 0, captcha: 1, payment: 2 };
90
+ for (const instance of [...runtime.instances].sort((a, b) => priority[a.kind ?? 'core'] - priority[b.kind ?? 'core'])) {
91
+ await instance.beforeSubmit?.(context);
92
+ if (reason) throw new Error(reason);
93
+ }
94
+ // Only transient module inputs cross this seam. PHP reads CAPTCHA data
95
+ // and payment fields without allowing arbitrary request/authority overrides.
96
+ return Object.fromEntries(Array.from(form.querySelectorAll<HTMLInputElement>('input[type="hidden"][name]')).map((input) => [input.name, input.value]));
97
+ },
98
+ result: async(result) => {
99
+ if (!form) return;
100
+ const context = { form, action: result.action ?? 'submit' as const, formData: new FormData(form) };
101
+ await bus.emit('formie:submit:result', result);
102
+ for (const instance of runtime.instances) await instance.afterSubmit?.(context, result);
103
+ form.dispatchEvent(new CustomEvent('formie:submit:result', { detail: result, bubbles: true }));
104
+ },
105
+ update: (manifest) => runtime.updateManifest(manifest),
106
+ assertReady: () => { runtime.assertReady(); runtime.instances.forEach((instance) => instance.assertReady?.()); },
71
107
  destroy: async() => {
72
- await destroyModuleInstances(instances);
108
+ await runtime.destroy();
73
109
  bus.clear();
74
110
  },
75
111
  on: (eventName, callback) => {
@@ -89,14 +125,3 @@ export async function hydrateFormieModules(options: FormieModuleHydratorOptions)
89
125
  },
90
126
  };
91
127
  }
92
-
93
- async function destroyModuleInstances(instances: FormieModuleInstance[]): Promise<void> {
94
- for (const instance of instances) {
95
- try {
96
- await instance.destroy();
97
- } catch (error) {
98
- console.error('[formie] Failed to destroy module instance.', error);
99
- debug.warn('Failed destroying module instance.', { error });
100
- }
101
- }
102
- }
@@ -99,22 +99,17 @@ function getProgressState(progress: number): 'start' | 'middle' | 'end' {
99
99
  return 'middle';
100
100
  }
101
101
 
102
- function getConfiguredSubmitAction(form: HTMLFormElement): string {
103
- return (form.dataset.formieSubmitAction || '').trim();
102
+ function getConfiguredCompletionBehavior(form: HTMLFormElement): string {
103
+ return (form.dataset.formieCompletionBehavior || form.dataset.formieSubmitAction || '').trim();
104
104
  }
105
105
 
106
- function getResolvedSubmitAction(form: HTMLFormElement, result: FormSubmitResult): string {
107
- const fromResponse = result.meta?.effectiveSubmitAction;
108
-
109
- if (typeof fromResponse === 'string' && fromResponse.trim() !== '') {
110
- return fromResponse.trim();
111
- }
112
-
113
- return getConfiguredSubmitAction(form);
106
+ function getResolvedCompletionBehavior(form: HTMLFormElement, result: FormSubmitResult): string {
107
+ if (result.completion) return String(result.completion.behavior);
108
+ return getConfiguredCompletionBehavior(form);
114
109
  }
115
110
 
116
111
  function shouldHideFormOnSuccess(form: HTMLFormElement): boolean {
117
- const rawValue = form.dataset.formieSubmitActionFormHide;
112
+ const rawValue = form.dataset.formieHideFormAfterSubmit ?? form.dataset.formieSubmitActionFormHide;
118
113
 
119
114
  if (rawValue === undefined) {
120
115
  return false;
@@ -199,7 +194,7 @@ function applyCompletedProgressState(form: HTMLFormElement): void {
199
194
 
200
195
  function scheduleFinalSubmitReset(form: HTMLFormElement): void {
201
196
  clearPendingFinalSubmitReset(form);
202
- const preserveHiddenState = getConfiguredSubmitAction(form) === 'message' && shouldHideFormOnSuccess(form);
197
+ const preserveHiddenState = getConfiguredCompletionBehavior(form) === 'message' && shouldHideFormOnSuccess(form);
203
198
 
204
199
  if (DEFAULT_FINAL_SUBMIT_RESET_DELAY_MS < 1) {
205
200
  resetSubmissionState(form, { preserveHiddenState });
@@ -300,12 +295,10 @@ export function clearSubmitLoading(form: HTMLFormElement): void {
300
295
  button.removeAttribute('data-formie-loading-text');
301
296
  });
302
297
 
303
- if (form.dataset.formieDisableSubmitUntilValid === 'true') {
304
- const formWithValidation = form as FormWithValidationApi;
298
+ const formWithValidation = form as FormWithValidationApi;
305
299
 
306
- if (formWithValidation.formieValidation) {
307
- syncSubmitReadiness(form, formWithValidation.formieValidation);
308
- }
300
+ if (formWithValidation.formieValidation) {
301
+ syncSubmitReadiness(form, formWithValidation.formieValidation);
309
302
  }
310
303
  }
311
304
 
@@ -370,27 +363,30 @@ export function applyPageState(form: HTMLFormElement, nextPageId: string): void
370
363
  setHiddenInputValue(form, 'pageId', nextPageId);
371
364
  syncPageTabErrors(form);
372
365
 
373
- if (form.dataset.formieDisableSubmitUntilValid === 'true') {
374
- const formWithValidation = form as FormWithValidationApi;
366
+ const formWithValidation = form as FormWithValidationApi;
375
367
 
376
- if (formWithValidation.formieValidation) {
377
- syncSubmitReadiness(form, formWithValidation.formieValidation);
378
- }
368
+ if (formWithValidation.formieValidation) {
369
+ syncSubmitReadiness(form, formWithValidation.formieValidation);
379
370
  }
380
371
  }
381
372
 
382
373
  function syncSubmissionIdentity(form: HTMLFormElement, result: FormSubmitResult): void {
374
+ const session = result.meta?.session as { version?: number } | undefined;
375
+ const version = session?.version ?? result.meta?.version;
376
+ if (typeof version === 'number') {
377
+ setHiddenInputValue(form, 'expectedVersion', String(version));
378
+ }
383
379
  const submissionUid = result.meta?.submissionUid;
384
380
  if (typeof submissionUid === 'string' && submissionUid.trim() !== '') {
385
381
  setHiddenInputValue(form, 'submissionUid', submissionUid);
386
382
  }
387
383
 
388
- const continuationToken = (result.meta?.session as { continuation?: { continuationToken?: unknown } } | undefined)
389
- ?.continuation?.continuationToken;
390
- if (typeof continuationToken === 'string' && continuationToken.trim() !== '') {
391
- setHiddenInputValue(form, 'continuationToken', continuationToken);
384
+ const progressId = (result.meta?.session as { continuation?: { progressId?: unknown } } | undefined)
385
+ ?.continuation?.progressId;
386
+ if (typeof progressId === 'string' && progressId.trim() !== '') {
387
+ setHiddenInputValue(form, 'progressId', progressId);
392
388
  } else {
393
- removeHiddenInput(form, 'continuationToken');
389
+ removeHiddenInput(form, 'progressId');
394
390
  }
395
391
  }
396
392
 
@@ -457,8 +453,9 @@ function resetSubmissionState(form: HTMLFormElement, options: ResetSubmissionSta
457
453
  setFormHiddenState(form, false);
458
454
  }
459
455
  removeHiddenInput(form, 'submissionId');
456
+ setHiddenInputValue(form, 'expectedVersion', '0');
460
457
  removeHiddenInput(form, 'submissionUid');
461
- removeHiddenInput(form, 'continuationToken');
458
+ removeHiddenInput(form, 'progressId');
462
459
  removeHiddenInput(form, 'pageId');
463
460
  clearResumeTokenState(form);
464
461
  validator?.resetLiveState();
@@ -508,15 +505,7 @@ function dispatchSubmitDataEvents(form: HTMLFormElement, result: FormSubmitResul
508
505
  }
509
506
  }
510
507
 
511
- // Fallback: some payment action-required responses can carry action payloads
512
- // as `paymentAction`/`paymentDecision` metadata without submitData entries.
513
- const meta = (result.meta || {}) as Record<string, unknown>;
514
- const paymentAction = (
515
- (meta.paymentAction && typeof meta.paymentAction === 'object' ? meta.paymentAction : null)
516
- || (meta.paymentDecision && typeof meta.paymentDecision === 'object'
517
- ? ((meta.paymentDecision as Record<string, unknown>).action as Record<string, unknown> | null)
518
- : null)
519
- ) as Record<string, unknown> | null;
508
+ const paymentAction = result.payment?.action;
520
509
 
521
510
  const actionEvent = paymentAction ? String(paymentAction.event || '') : '';
522
511
  const actionPayload = paymentAction ? paymentAction.payload : undefined;
@@ -564,7 +553,7 @@ export function applySubmitResultState(form: HTMLFormElement, result: FormSubmit
564
553
  });
565
554
  clearPendingFinalSubmitReset(form);
566
555
  if (result.redirect.target === 'new-tab') {
567
- window.open(result.redirect.url, '_blank');
556
+ window.open(result.redirect.url, '_blank', 'noopener,noreferrer');
568
557
  } else {
569
558
  markInternalNavigation(form, 'redirect');
570
559
  window.location.href = result.redirect.url;
@@ -609,18 +598,18 @@ export function applySubmitResultState(form: HTMLFormElement, result: FormSubmit
609
598
  return;
610
599
  }
611
600
 
612
- if (action === 'submit' && !result.redirect?.url) {
613
- const configuredSubmitAction = getResolvedSubmitAction(form, result);
614
- const preserveHiddenState = configuredSubmitAction === 'message' && shouldHideFormOnSuccess(form);
601
+ if (result.completion && action === 'submit' && !result.redirect?.url) {
602
+ const completionBehavior = getResolvedCompletionBehavior(form, result);
603
+ const preserveHiddenState = completionBehavior === 'message' && shouldHideFormOnSuccess(form);
615
604
 
616
- if (configuredSubmitAction === 'reload') {
605
+ if (completionBehavior === 'reload') {
617
606
  clearPendingFinalSubmitReset(form);
618
607
  markInternalNavigation(form, 'reload');
619
608
  window.location.reload();
620
609
  return;
621
610
  }
622
611
 
623
- if (configuredSubmitAction === 'reset') {
612
+ if (completionBehavior === 'reset') {
624
613
  resetSubmissionState(form);
625
614
  return;
626
615
  }
@@ -633,8 +622,8 @@ export function applySubmitResultState(form: HTMLFormElement, result: FormSubmit
633
622
  }
634
623
 
635
624
  if (action === 'submit' && result.redirect?.url && result.redirect.target === 'new-tab') {
636
- const configuredSubmitAction = getResolvedSubmitAction(form, result);
637
- const preserveHiddenState = configuredSubmitAction === 'message' && shouldHideFormOnSuccess(form);
625
+ const completionBehavior = getResolvedCompletionBehavior(form, result);
626
+ const preserveHiddenState = completionBehavior === 'message' && shouldHideFormOnSuccess(form);
638
627
  clearPendingFinalSubmitReset(form);
639
628
  resetSubmissionState(form, { preserveHiddenState });
640
629
  return;
@@ -1,5 +1,10 @@
1
1
  import type { FormSubmitResult } from '#contracts/schema';
2
2
  import { applyErrorAriaLive, getErrorAriaLivePreference, resolveSubmitErrorAriaLive } from '#core/error-aria-live';
3
+ import {
4
+ appendDescribedBy,
5
+ clearFieldErrorAria,
6
+ setErrorMessageReference,
7
+ } from '#core/field-error-aria';
3
8
  import { ensureFieldErrorContainer as resolveFieldErrorContainer } from '#core/field-error-container';
4
9
  import { syncPageTabErrors } from '#core/page-tab-errors';
5
10
  import { focusFirstValidationError } from '#core/validation-focus';
@@ -8,8 +13,8 @@ import { addThemeClasses, removeThemeClasses } from '#theme/theme-classes';
8
13
 
9
14
  const successHideTimers = new WeakMap<HTMLFormElement, number>();
10
15
 
11
- function getConfiguredSubmitAction(form: HTMLFormElement): string {
12
- return (form.dataset.formieSubmitAction || '').trim();
16
+ function getConfiguredCompletionBehavior(form: HTMLFormElement): string {
17
+ return (form.dataset.formieCompletionBehavior || form.dataset.formieSubmitAction || '').trim();
13
18
  }
14
19
 
15
20
  function getErrorMessagePosition(form: HTMLFormElement): string {
@@ -17,11 +22,11 @@ function getErrorMessagePosition(form: HTMLFormElement): string {
17
22
  }
18
23
 
19
24
  function getSuccessMessagePosition(form: HTMLFormElement): string {
20
- return (form.dataset.formieSubmitActionMessagePosition || '').trim();
25
+ return (form.dataset.formieSuccessMessagePosition || form.dataset.formieSubmitActionMessagePosition || '').trim();
21
26
  }
22
27
 
23
28
  function getSuccessMessageTimeoutMs(form: HTMLFormElement): number | null {
24
- const rawValue = (form.dataset.formieSubmitActionMessageTimeout || '').trim();
29
+ const rawValue = (form.dataset.formieSuccessMessageTimeout || form.dataset.formieSubmitActionMessageTimeout || '').trim();
25
30
 
26
31
  if (!rawValue) {
27
32
  return null;
@@ -37,7 +42,7 @@ function getSuccessMessageTimeoutMs(form: HTMLFormElement): number | null {
37
42
  }
38
43
 
39
44
  function shouldHideFormOnSuccess(form: HTMLFormElement): boolean {
40
- const rawValue = form.dataset.formieSubmitActionFormHide;
45
+ const rawValue = form.dataset.formieHideFormAfterSubmit ?? form.dataset.formieSubmitActionFormHide;
41
46
 
42
47
  if (rawValue === undefined) {
43
48
  return false;
@@ -160,40 +165,10 @@ function ensureFieldErrorContainer(fieldNode: Element): HTMLElement {
160
165
  });
161
166
  }
162
167
 
163
- function removeDescribedBy(input: HTMLElement, describedById: string): void {
164
- const current = (input.getAttribute('aria-describedby') || '').trim();
165
-
166
- if (!current) {
167
- return;
168
- }
169
-
170
- const nextValue = current.split(/\s+/).filter((item) => {
171
- return item !== describedById;
172
- }).join(' ').trim();
173
-
174
- if (nextValue) {
175
- input.setAttribute('aria-describedby', nextValue);
176
- return;
177
- }
178
-
179
- input.removeAttribute('aria-describedby');
180
- }
181
-
182
- function setErrorMessageReference(input: HTMLElement, errorMessageId: string): void {
183
- input.setAttribute('aria-errormessage', errorMessageId);
184
- }
185
-
186
- function clearErrorMessageReference(input: HTMLElement, errorMessageId: string): void {
187
- if (input.getAttribute('aria-errormessage') === errorMessageId) {
188
- input.removeAttribute('aria-errormessage');
189
- }
190
- }
191
-
192
168
  export function clearFieldErrors(form: HTMLFormElement): void {
193
169
  form.querySelectorAll('[data-formie-field-handle]').forEach((fieldNode) => {
194
170
  const fieldElement = fieldNode as HTMLElement;
195
171
  const container = fieldElement.querySelector('[data-formie-field-errors]') as HTMLElement | null;
196
- const containerId = container?.id || '';
197
172
  const errorMessageIds = Array.from(fieldElement.querySelectorAll('[data-formie-field-error]')).map((node) => {
198
173
  return (node as HTMLElement).id;
199
174
  }).filter(Boolean);
@@ -214,14 +189,7 @@ export function clearFieldErrors(form: HTMLFormElement): void {
214
189
  element.removeAttribute('aria-invalid');
215
190
  removeThemeClasses(element, form, 'fieldControlError');
216
191
  element.removeAttribute('data-formie-input-has-error');
217
-
218
- if (containerId) {
219
- removeDescribedBy(element, containerId);
220
- }
221
-
222
- errorMessageIds.forEach((errorMessageId) => {
223
- clearErrorMessageReference(element, errorMessageId);
224
- });
192
+ clearFieldErrorAria(element, errorMessageIds);
225
193
  });
226
194
  });
227
195
 
@@ -275,7 +243,7 @@ export function clearFormSuccess(form: HTMLFormElement): void {
275
243
  }
276
244
  });
277
245
 
278
- if (!(getConfiguredSubmitAction(form) === 'message' && shouldHideFormOnSuccess(form))) {
246
+ if (!(getConfiguredCompletionBehavior(form) === 'message' && shouldHideFormOnSuccess(form))) {
279
247
  setFormHiddenState(form, false);
280
248
  }
281
249
  }
@@ -286,22 +254,13 @@ export function clearAriaInvalid(form: HTMLFormElement): void {
286
254
  });
287
255
  }
288
256
 
289
- function appendDescribedBy(input: HTMLElement, describedById: string): void {
290
- const current = (input.getAttribute('aria-describedby') || '').trim();
291
- const items = current ? current.split(/\s+/) : [];
292
-
293
- if (!items.includes(describedById)) {
294
- items.push(describedById);
295
- }
296
-
297
- input.setAttribute('aria-describedby', items.join(' ').trim());
298
- }
299
-
300
257
  export function renderFieldErrors(form: HTMLFormElement, fieldErrors: Record<string, string[]>): void {
301
258
  const errorAriaLive = resolveSubmitErrorAriaLive(getErrorAriaLivePreference(form));
302
259
 
303
260
  Object.entries(fieldErrors).forEach(([handle, messages]) => {
304
- const fieldNode = form.querySelector(`[data-formie-field-handle="${handle}"]`);
261
+ const inputName = `fields[${handle.split('.').join('][')}]`;
262
+ const exactInput = form.querySelector(`[name="${CSS.escape(inputName)}"], [name="${CSS.escape(inputName + '[]')}"]`);
263
+ const fieldNode = exactInput?.closest('[data-formie-field-handle]') || form.querySelector(`[data-formie-field-handle="${CSS.escape(handle)}"]`);
305
264
 
306
265
  if (!fieldNode) {
307
266
  return;
@@ -319,7 +278,6 @@ export function renderFieldErrors(form: HTMLFormElement, fieldErrors: Record<str
319
278
  messages.forEach((message, index) => {
320
279
  const errorNode = document.createElement('div');
321
280
  errorNode.setAttribute('data-formie-field-error', 'true');
322
- errorNode.setAttribute('role', 'alert');
323
281
  errorNode.id = `${containerId}-${index + 1}`;
324
282
  addThemeClasses(errorNode, form, 'fieldError');
325
283
  errorNode.textContent = message;
@@ -327,7 +285,8 @@ export function renderFieldErrors(form: HTMLFormElement, fieldErrors: Record<str
327
285
  });
328
286
 
329
287
  // Point every control in the field at the first rendered message while
330
- // still rendering the full list for visual output.
288
+ // still rendering the full list for visual output. Pair aria-errormessage
289
+ // with aria-describedby on that message id (#2946).
331
290
  const primaryErrorId = (container.querySelector('[data-formie-field-error]') as HTMLElement | null)?.id;
332
291
 
333
292
  fieldNode.querySelectorAll('input, select, textarea').forEach((input) => {
@@ -335,7 +294,6 @@ export function renderFieldErrors(form: HTMLFormElement, fieldErrors: Record<str
335
294
  element.setAttribute('aria-invalid', 'true');
336
295
  addThemeClasses(element, form, 'fieldControlError');
337
296
  element.setAttribute('data-formie-input-has-error', 'true');
338
- appendDescribedBy(element, containerId);
339
297
 
340
298
  if (primaryErrorId) {
341
299
  setErrorMessageReference(element, primaryErrorId);
@@ -372,10 +330,9 @@ function isPaymentFollowUpResult(result: FormSubmitResult): boolean {
372
330
  return false;
373
331
  }
374
332
 
375
- const meta = (result.meta || {}) as Record<string, unknown>;
376
- const paymentStatus = String(meta.paymentStatus || '');
333
+ const paymentStatus = result.payment?.status;
377
334
 
378
- return paymentStatus === 'actionRequired' || paymentStatus === 'pending';
335
+ return paymentStatus === 'requiresAction' || paymentStatus === 'pending' || paymentStatus === 'unknown';
379
336
  }
380
337
 
381
338
  export function renderFormNotice(form: HTMLFormElement, message: string): void {
@@ -403,7 +360,7 @@ function shouldRenderSuccessMessage(form: HTMLFormElement, result: FormSubmitRes
403
360
  return true;
404
361
  }
405
362
 
406
- return getConfiguredSubmitAction(form) === 'message' && getSuccessMessagePosition(form) !== '';
363
+ return getConfiguredCompletionBehavior(form) === 'message' && getSuccessMessagePosition(form) !== '';
407
364
  }
408
365
 
409
366
  export function renderFormSuccess(form: HTMLFormElement, message: string): void {
@@ -459,8 +416,7 @@ export function applySubmitResultUi(form: HTMLFormElement, result: FormSubmitRes
459
416
 
460
417
  if (!result.ok) {
461
418
  if (isPaymentFollowUpResult(result)) {
462
- const meta = (result.meta || {}) as Record<string, unknown>;
463
- const message = String(meta.paymentMessage || '').trim();
419
+ const message = String(result.payment?.message || '').trim();
464
420
 
465
421
  if (message) {
466
422
  renderFormNotice(form, message);
@@ -1,15 +1,6 @@
1
+ import { appendDescribedBy, setErrorMessageReference } from '#core/field-error-aria';
1
2
  import { addThemeClasses } from '#theme/theme-classes';
2
-
3
- function appendDescribedBy(input: HTMLElement, describedById: string): void {
4
- const current = (input.getAttribute('aria-describedby') || '').trim();
5
- const items = current ? current.split(/\s+/) : [];
6
-
7
- if (!items.includes(describedById)) {
8
- items.push(describedById);
9
- }
10
-
11
- input.setAttribute('aria-describedby', items.join(' ').trim());
12
- }
3
+ import { isValidationSkipped } from '#validation/skip';
13
4
 
14
5
  function getFirstErroredField(form: HTMLFormElement): HTMLElement | null {
15
6
  const fields = Array.from(form.querySelectorAll('[data-formie-field-handle]')) as HTMLElement[];
@@ -24,15 +15,19 @@ function getFirstErroredField(form: HTMLFormElement): HTMLElement | null {
24
15
  }
25
16
 
26
17
  function getFieldFocusTarget(field: HTMLElement): HTMLElement | null {
27
- const invalidControl = field.querySelector('[aria-invalid="true"]') as HTMLElement | null;
18
+ const invalidControl = Array.from(field.querySelectorAll('[aria-invalid="true"]')).find((node) => {
19
+ return !isValidationSkipped(node);
20
+ }) as HTMLElement | undefined;
28
21
 
29
22
  if (invalidControl) {
30
23
  return invalidControl;
31
24
  }
32
25
 
33
- return field.querySelector(
26
+ return Array.from(field.querySelectorAll(
34
27
  'input:not([type="hidden"]):not([disabled]), select:not([disabled]), textarea:not([disabled])',
35
- ) as HTMLElement | null;
28
+ )).find((node) => {
29
+ return !isValidationSkipped(node);
30
+ }) as HTMLElement | undefined ?? null;
36
31
  }
37
32
 
38
33
  function getFormLevelErrorTarget(form: HTMLFormElement): HTMLElement | null {
@@ -54,23 +49,28 @@ export function enhanceServerRenderedFieldErrors(form: HTMLFormElement): void {
54
49
  field.setAttribute('data-formie-field-has-error', 'true');
55
50
  addThemeClasses(field, form, 'fieldLayoutError');
56
51
 
57
- const errorContainer = field.querySelector('[data-formie-field-errors]') as HTMLElement | null;
58
- const errorContainerId = errorContainer?.id || '';
59
52
  const primaryError = field.querySelector('[data-formie-field-error]') as HTMLElement | null;
60
53
  const primaryErrorId = primaryError?.id || '';
61
54
 
62
55
  field.querySelectorAll('input, select, textarea').forEach((input) => {
63
56
  const element = input as HTMLElement;
57
+
58
+ if (isValidationSkipped(element)) {
59
+ return;
60
+ }
61
+
64
62
  element.setAttribute('aria-invalid', 'true');
65
63
  addThemeClasses(element, form, 'fieldControlError');
66
64
  element.setAttribute('data-formie-input-has-error', 'true');
67
65
 
68
- if (errorContainerId) {
69
- appendDescribedBy(element, errorContainerId);
66
+ if (primaryErrorId) {
67
+ // Pair aria-errormessage + aria-describedby on the message id (#2946).
68
+ setErrorMessageReference(element, primaryErrorId);
70
69
  }
71
70
 
72
- if (primaryErrorId) {
73
- element.setAttribute('aria-errormessage', primaryErrorId);
71
+ const instructions = field.querySelector('[data-formie-instructions]') as HTMLElement | null;
72
+ if (instructions?.id) {
73
+ appendDescribedBy(element, instructions.id);
74
74
  }
75
75
  });
76
76
  });
@@ -28,7 +28,7 @@ export const addressFinderModule = defineAddressModule<
28
28
  AddressFinderGlobal,
29
29
  AddressFinderWidget
30
30
  >({
31
- id: 'address-finder',
31
+ moduleId: 'formie:address-finder',
32
32
  load: async () => {
33
33
  return loadScriptAndEnsureGlobal<AddressFinderGlobal>('AddressFinder', {
34
34
  id: SCRIPT_ID,