@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
@@ -66,7 +66,7 @@ type StripeFieldState = Element & {
66
66
 
67
67
  const ZERO_DECIMAL_CURRENCIES = new Set([
68
68
  'BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG',
69
- 'RWF', 'UGX', 'VND', 'VUV', 'XAF', 'XOF', 'XPF',
69
+ 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF',
70
70
  ]);
71
71
 
72
72
  function toStripeSubunitAmount(amount: number, currency: string): number {
@@ -74,7 +74,7 @@ function toStripeSubunitAmount(amount: number, currency: string): number {
74
74
  return Math.ceil(amount);
75
75
  }
76
76
 
77
- return Math.ceil(amount * 100);
77
+ return Math.round((amount + Number.EPSILON) * 100);
78
78
  }
79
79
 
80
80
  function clearPlaceholderError(placeholder: HTMLElement | null, themeSource: Element | null): void {
@@ -97,7 +97,7 @@ function renderPlaceholderLoading(placeholder: HTMLElement | null, themeSource:
97
97
  placeholder.removeAttribute('hidden');
98
98
  placeholder.innerHTML = '';
99
99
  const spinner = document.createElement('div');
100
- spinner.className = 'formie-loading';
100
+ addThemeClasses(spinner, themeSource, 'loading');
101
101
  const text = document.createElement('div');
102
102
  text.textContent = message;
103
103
  placeholder.append(spinner, text);
@@ -120,7 +120,7 @@ function showPlaceholderMessage(placeholder: HTMLElement | null, themeSource: El
120
120
  }
121
121
 
122
122
  export const stripeModule = definePaymentModule<StripeProviderOptions, StripeInstance | null, StripeWidget | null>({
123
- id: 'stripe',
123
+ moduleId: 'formie:stripe',
124
124
  defaultRequiredInputSuffixes: ['stripePaymentIntentId'],
125
125
  load: async (ctx) => {
126
126
  const { provider } = ctx.options;
@@ -288,7 +288,7 @@ export const stripeModule = definePaymentModule<StripeProviderOptions, StripeIns
288
288
  stripeField.__formieStripeDynamicUnbind?.();
289
289
  stripeField.__formieStripeDynamicUnbind = null;
290
290
  },
291
- onBeforeAuthorize: async (args) => {
291
+ onBeforePayment: async (args) => {
292
292
  const { widget, services, field } = args;
293
293
  const stripeField = field as StripeFieldState;
294
294
  let activeWidget = widget;
@@ -15,6 +15,20 @@ export function getPaymentProviderHandle(id: string, options: PaymentProviderOpt
15
15
  return id;
16
16
  }
17
17
 
18
+ /**
19
+ * Payment widgets should neither mount nor enforce authorize tokens when the
20
+ * field (or an ancestor row/page) is hidden by conditions / page flow.
21
+ * Matches server-side `isConditionallyHidden()` skips in payment workflow tasks.
22
+ */
23
+ export function isPaymentFieldActive(element: Element): boolean {
24
+ const node = element as HTMLElement;
25
+
26
+ return !node.closest('[data-formie-conditionally-hidden]')
27
+ && !node.closest('[data-formie-row-hidden]')
28
+ && !node.closest('[data-formie-page-hidden]')
29
+ && !node.closest('[hidden]');
30
+ }
31
+
18
32
  export function findPaymentInputBySuffix(root: Element, suffix: string): HTMLInputElement | null {
19
33
  const escapedSuffix = suffix.replace(/"/g, '\\"');
20
34
  return (root.querySelector(`input[name$="[${escapedSuffix}]"]`) ||
@@ -1,12 +1,17 @@
1
- import type { FormieModuleDefinition, ModuleRegistrationOptions } from '#contracts/modules';
1
+ import type { BrowserModuleDefinition, ModuleRegistrationOptions } from '#contracts/modules';
2
2
 
3
3
  export class ModuleRegistry {
4
- private modules = new Map<string, FormieModuleDefinition>();
5
-
6
- register(moduleDefinition: FormieModuleDefinition, options: ModuleRegistrationOptions = {}): boolean {
7
- // The registry is intentionally lightweight: the loader handles lazy
8
- // resolution, while this class only tracks already-available definitions.
9
- const existing = this.modules.get(moduleDefinition.id);
4
+ private modules = new Map<string, BrowserModuleDefinition>();
5
+
6
+ register(moduleDefinition: BrowserModuleDefinition, options: ModuleRegistrationOptions = {}): boolean {
7
+ if (!/^[a-z][a-z0-9.-]*:[a-z][a-z0-9.-]*$/.test(moduleDefinition.moduleId) ||
8
+ moduleDefinition.version !== 2 || !Array.isArray(moduleDefinition.surfaces) || moduleDefinition.surfaces.length === 0 ||
9
+ moduleDefinition.surfaces.some((surface) => !['server-rendered', 'client-rendered', 'cp-edit'].includes(surface)) ||
10
+ !['field', 'captcha', 'payment', 'address', 'core'].includes(moduleDefinition.kind) ||
11
+ typeof moduleDefinition.match !== 'function' || typeof moduleDefinition.setup !== 'function') {
12
+ throw new Error('Unsupported browser module definition. Register a namespaced moduleId compatible with version 2.');
13
+ }
14
+ const existing = this.modules.get(moduleDefinition.moduleId);
10
15
 
11
16
  if (existing === moduleDefinition) {
12
17
  return true;
@@ -14,13 +19,13 @@ export class ModuleRegistry {
14
19
 
15
20
  if (existing && !options.replace) {
16
21
  console.warn(
17
- `[formie] Module "${moduleDefinition.id}" is already registered. `
22
+ `[formie] Module "${moduleDefinition.moduleId}" is already registered. `
18
23
  + 'Pass { replace: true } to override the existing definition.',
19
24
  );
20
25
  return false;
21
26
  }
22
27
 
23
- this.modules.set(moduleDefinition.id, moduleDefinition);
28
+ this.modules.set(moduleDefinition.moduleId, moduleDefinition);
24
29
  return true;
25
30
  }
26
31
 
@@ -28,11 +33,14 @@ export class ModuleRegistry {
28
33
  this.modules.delete(moduleId);
29
34
  }
30
35
 
31
- get(moduleId: string): FormieModuleDefinition | null {
36
+ get(moduleId: string): BrowserModuleDefinition | null {
32
37
  return this.modules.get(moduleId) || null;
33
38
  }
34
39
 
35
- getAll(): FormieModuleDefinition[] {
40
+ getAll(): BrowserModuleDefinition[] {
36
41
  return Array.from(this.modules.values());
37
42
  }
38
43
  }
44
+
45
+ /** Trusted application registrations shared by client-rendered framework hosts. */
46
+ export const clientRenderedModuleRegistry = new ModuleRegistry();
@@ -17,8 +17,8 @@ export type SubmitPipelineContext = {
17
17
 
18
18
  type StageRunner = (ctx: SubmitPipelineContext) => Promise<FormSubmitResult | null>;
19
19
 
20
- const STAGES: SubmitStage[] = ['prepare', 'normalize', 'validate', 'screen', 'authorize', 'dispatch', 'finalize'];
21
- const PREFLIGHT_STAGES: SubmitStage[] = ['prepare', 'normalize', 'validate', 'screen', 'authorize'];
20
+ const STAGES: SubmitStage[] = ['prepare', 'validate', 'challenge', 'payment', 'send', 'result'];
21
+ const PREFLIGHT_STAGES: SubmitStage[] = ['prepare', 'validate', 'challenge', 'payment'];
22
22
  const debug = createDebug('general', 'pipeline');
23
23
 
24
24
  function getAbortedResult(stage: SubmitStage, reason?: string): FormSubmitResult {
@@ -231,9 +231,6 @@ export async function runSubmitPipeline(
231
231
  ctx.formData.set('submitAction', ctx.action);
232
232
  return null;
233
233
  },
234
- normalize: async() => {
235
- return null;
236
- },
237
234
  validate: async(ctx) => {
238
235
  if (ctx.action !== 'submit') {
239
236
  // Back/save actions bypass final validation so they can persist
@@ -290,13 +287,13 @@ export async function runSubmitPipeline(
290
287
 
291
288
  return null;
292
289
  },
293
- screen: async() => {
290
+ challenge: async() => {
294
291
  return null;
295
292
  },
296
- authorize: async() => {
293
+ payment: async() => {
297
294
  return null;
298
295
  },
299
- dispatch: async(ctx) => {
296
+ send: async(ctx) => {
300
297
  // Rehydrate just-in-time so provider hooks can mutate hidden inputs
301
298
  // during `screen` / `authorize` and still be submitted.
302
299
  ctx.formData = buildSubmitFormData(ctx.form, ctx.action);
@@ -306,7 +303,7 @@ export async function runSubmitPipeline(
306
303
  // success and failure outcomes (e.g. provider token/element resets).
307
304
  return result;
308
305
  },
309
- finalize: async(resultCtx) => {
306
+ result: async(resultCtx) => {
310
307
  if (!dispatchResult) {
311
308
  return null;
312
309
  }
@@ -315,8 +312,9 @@ export async function runSubmitPipeline(
315
312
  // rest of the browser client can react to one result shape first.
316
313
  if (dispatchResult.ok && dispatchResult.redirect?.url) {
317
314
  if (dispatchResult.redirect.target === 'new-tab') {
318
- window.open(dispatchResult.redirect.url, '_blank');
315
+ window.open(dispatchResult.redirect.url, '_blank', 'noopener,noreferrer');
319
316
  } else {
317
+ resultCtx.form.setAttribute('data-formie-internal-navigation', 'redirect');
320
318
  window.location.href = dispatchResult.redirect.url;
321
319
  }
322
320
  }
@@ -469,7 +467,8 @@ export async function runSubmitPipeline(
469
467
 
470
468
  const successResult: FormSubmitResult = dispatchResult || {
471
469
  ok: true,
472
- stage: options.preflightOnly ? 'authorize' : 'finalize',
470
+ stage: options.preflightOnly ? 'payment' : 'result',
471
+ code: options.preflightOnly ? 'PREFLIGHT_COMPLETE' : undefined,
473
472
  message: options.preflightOnly ? 'Submission preflight completed.' : 'Submission completed.',
474
473
  };
475
474
 
@@ -0,0 +1,25 @@
1
+ // Generated from src/config/browser-theme-state.json. Do not edit by hand.
2
+ export const BROWSER_THEME_STATE_DEFAULTS = {
3
+ errors: ["formie-errors"],
4
+ successes: ["formie-successes"],
5
+ message: ["formie-message"],
6
+ messageError: ["formie-message-error"],
7
+ messageSuccess: ["formie-message-success"],
8
+ tabError: ["formie-tab-error"],
9
+ tabCurrent: ["formie-tab-current"],
10
+ tabComplete: ["formie-tab-complete"],
11
+ tabLinkCurrent: [],
12
+ tabLinkInactive: [],
13
+ pageHidden: ["formie-page-hidden"],
14
+ conditionalHidden: ["formie-conditionally-hidden"],
15
+ rowHidden: ["formie-row-hidden"],
16
+ loading: ["formie-loading"],
17
+ success: ["formie-success"],
18
+ error: ["formie-error"],
19
+ fieldLayoutError: ["formie-field-has-error"],
20
+ fieldControlError: ["formie-input-error"],
21
+ fieldErrors: ["formie-field-errors"],
22
+ fieldError: ["formie-field-error"],
23
+ } as const;
24
+
25
+ export type BrowserThemeStateKey = keyof typeof BROWSER_THEME_STATE_DEFAULTS;
@@ -1,4 +1,4 @@
1
- import type { ThemeClassMap } from '#contracts/theme';
1
+ import type { BrowserThemeClassMap } from '#contracts/theme';
2
2
 
3
3
  type NormalizedThemeClassMap = Record<string, string[]>;
4
4
 
@@ -46,7 +46,7 @@ function resolveThemeClassMap(source: Element | null): NormalizedThemeClassMap {
46
46
  return themeRegistry.get(root) || {};
47
47
  }
48
48
 
49
- export function normalizeThemeClassMap(theme?: ThemeClassMap | null): NormalizedThemeClassMap {
49
+ export function normalizeThemeClassMap(theme?: BrowserThemeClassMap | null): NormalizedThemeClassMap {
50
50
  const normalized: NormalizedThemeClassMap = {};
51
51
 
52
52
  Object.entries(theme || {}).forEach(([key, value]) => {
@@ -60,7 +60,7 @@ export function normalizeThemeClassMap(theme?: ThemeClassMap | null): Normalized
60
60
  return normalized;
61
61
  }
62
62
 
63
- export function registerThemeClassMap(target: Element, theme?: ThemeClassMap | null, form?: HTMLFormElement | null): NormalizedThemeClassMap {
63
+ export function registerThemeClassMap(target: Element, theme?: BrowserThemeClassMap | null, form?: HTMLFormElement | null): NormalizedThemeClassMap {
64
64
  const normalized = normalizeThemeClassMap(theme);
65
65
  const resolvedForm = form || (target instanceof HTMLFormElement ? target : target.querySelector('form'));
66
66
 
@@ -1,6 +1,8 @@
1
+ import { browserRequest, type BrowserRequestOptions } from '@verbb/formie-core';
1
2
  import type { FormEndpointPayload, FormSubmitResult } from '#contracts/schema';
3
+ import { appendFormCsrfToFormData } from '#utils/csrf';
2
4
  import { createDebug } from '#utils/debug';
3
- import { requestJson } from '#utils/http';
5
+ import { request, requestJson } from '#utils/http';
4
6
 
5
7
  const debug = createDebug('general', 'transport');
6
8
 
@@ -25,40 +27,15 @@ function toServerRenderPayloadInput(renderOptions: Record<string, unknown>): Rec
25
27
  return input;
26
28
  }
27
29
 
28
- function flattenErrors(errors: unknown, path = '', output: Record<string, string[]> = {}): Record<string, string[]> {
29
- if (Array.isArray(errors)) {
30
- const messages = errors
31
- .map((value) => {
32
- return typeof value === 'string' ? value : String(value ?? '');
33
- })
34
- .filter((value) => {
35
- return value.trim() !== '';
36
- });
37
-
38
- if (path && messages.length) {
39
- output[path] = (output[path] || []).concat(messages);
40
- }
41
-
42
- return output;
43
- }
44
-
45
- // Craft/Formie error payloads can be nested by page/field/path. Flatten first
46
- // so the browser client can map them back onto field handles consistently.
47
- if (errors && typeof errors === 'object') {
48
- Object.entries(errors as Record<string, unknown>).forEach(([key, value]) => {
49
- const nextPath = path ? `${path}.${key}` : key;
50
- flattenErrors(value, nextPath, output);
51
- });
52
- }
53
-
54
- return output;
55
- }
56
-
57
30
  function normalizePayload(payload: Record<string, unknown>, fallbackFormError?: string): FormSubmitResult {
58
31
  const success = payload.success === true;
59
- const keepSubmitLoading = payload.keepSubmitLoading === true;
32
+ const completion = payload.completion && typeof payload.completion === 'object'
33
+ ? payload.completion as NonNullable<FormSubmitResult['completion']>
34
+ : null;
35
+ const payment = payload.payment && typeof payload.payment === 'object' ? payload.payment as FormSubmitResult['payment'] : null;
36
+ const keepSubmitLoading = ['requiresAction', 'pending', 'unknown'].includes(payment?.status || '');
60
37
  const errors = payload.errors;
61
- const fieldErrorsFlat = flattenErrors(errors || {});
38
+ const fieldErrorsFlat: Record<string, string[]> = Object.fromEntries(Object.entries(errors && typeof errors === 'object' ? errors : {}).map(([path, messages]) => [path, Array.isArray(messages) ? messages.filter((message): message is string => typeof message === 'string') : []]));
62
39
  const formErrors = fieldErrorsFlat.form || [];
63
40
  const fieldErrors: Record<string, string[]> = {};
64
41
 
@@ -67,10 +44,8 @@ function normalizePayload(payload: Record<string, unknown>, fallbackFormError?:
67
44
  return;
68
45
  }
69
46
 
70
- // The client renders field errors against top-level field handles even when
71
- // the backend returns deeper nested keys for subfields or row paths.
72
- const topKey = key.split('.')[0];
73
- fieldErrors[topKey] = (fieldErrors[topKey] || []).concat(value);
47
+ // The server normalises Yii keys; retain the entire control value path.
48
+ fieldErrors[key] = value;
74
49
  });
75
50
 
76
51
  const resolvedFormErrors = !success && formErrors.length === 0 && Object.keys(fieldErrors).length > 0
@@ -84,15 +59,24 @@ function normalizePayload(payload: Record<string, unknown>, fallbackFormError?:
84
59
 
85
60
  const result: FormSubmitResult = {
86
61
  ok: success,
62
+ outcome: typeof payload.outcome === 'string' ? payload.outcome : undefined,
63
+ version: typeof payload.version === 'number' ? payload.version : null,
64
+ submissionUid: typeof payload.submissionUid === 'string' ? payload.submissionUid : null,
65
+ errors: payload.errors,
66
+ session: payload.session,
67
+ completion,
87
68
  action: (payload.submitAction === 'back' || payload.submitAction === 'save' || payload.submitAction === 'submit')
88
69
  ? payload.submitAction
89
70
  : undefined,
90
71
  message: (
91
- payload.submitActionMessage
72
+ completion?.message
73
+ || payload.successMessage
74
+ || payload.submitActionMessage
92
75
  || (success ? 'Submission completed.' : (isTransientPendingResult ? '' : (resolvedFormErrors[0] || 'Submission failed.')))
93
76
  ) as string,
94
77
  code: success ? undefined : String(payload.code || 'SUBMIT_ERROR'),
95
78
  keepSubmitLoading,
79
+ payment,
96
80
  fieldErrors: Object.keys(fieldErrors).length ? fieldErrors : undefined,
97
81
  formErrors: resolvedFormErrors.length ? resolvedFormErrors : undefined,
98
82
  nextPage: payload.nextPageId
@@ -100,10 +84,20 @@ function normalizePayload(payload: Record<string, unknown>, fallbackFormError?:
100
84
  id: String(payload.nextPageId),
101
85
  }
102
86
  : null,
103
- redirect: payload.redirectUrl
87
+ redirect: payment?.status === 'requiresAction' && payment.action?.type === 'redirect' && payment.action.url
88
+ ? {
89
+ url: payment.action.url,
90
+ target: 'same-tab',
91
+ }
92
+ : completion?.url
93
+ ? {
94
+ url: completion.url,
95
+ target: completion.target,
96
+ }
97
+ : payload.redirectUrl
104
98
  ? {
105
99
  url: String(payload.redirectUrl),
106
- target: payload.submitActionTab === 'new-tab' ? 'new-tab' : 'same-tab',
100
+ target: payload.redirectTarget === 'new-tab' ? 'new-tab' : 'same-tab',
107
101
  }
108
102
  : null,
109
103
  submitData: Array.isArray(payload.submitData) ? payload.submitData : undefined,
@@ -114,7 +108,7 @@ function normalizePayload(payload: Record<string, unknown>, fallbackFormError?:
114
108
  return result;
115
109
  }
116
110
 
117
- export async function requestRender(endpoint: string, handle: string, renderOptions: Record<string, unknown> = {}): Promise<FormEndpointPayload> {
111
+ export async function requestRender(endpoint: string, handle: string, renderOptions: Record<string, unknown> = {}, requestOptions: BrowserRequestOptions = {}): Promise<FormEndpointPayload> {
118
112
  const body = JSON.stringify({
119
113
  handle,
120
114
  renderOptions,
@@ -122,6 +116,7 @@ export async function requestRender(endpoint: string, handle: string, renderOpti
122
116
 
123
117
  debug.log('requestRender start.', { endpoint, handle });
124
118
  const result = await requestJson<FormEndpointPayload>(endpoint, {
119
+ ...requestOptions,
125
120
  method: 'POST',
126
121
  body,
127
122
  headers: {
@@ -134,7 +129,7 @@ export async function requestRender(endpoint: string, handle: string, renderOpti
134
129
  return result;
135
130
  }
136
131
 
137
- export async function requestGraphqlRender(endpoint: string, handle: string, renderOptions: Record<string, unknown> = {}): Promise<FormEndpointPayload> {
132
+ export async function requestGraphqlRender(endpoint: string, handle: string, renderOptions: Record<string, unknown> = {}, requestOptions: BrowserRequestOptions = {}): Promise<FormEndpointPayload> {
138
133
  const query = `
139
134
  query FormieHtmlForm($handle: String!, $input: ServerRenderPayloadInput) {
140
135
  formieHtmlForm(handle: $handle, input: $input) {
@@ -151,6 +146,7 @@ query FormieHtmlForm($handle: String!, $input: ServerRenderPayloadInput) {
151
146
 
152
147
  debug.log('requestGraphqlRender start.', { endpoint, handle });
153
148
  const result = await requestJson<GraphqlResponse<GraphqlFormQueryResult>>(endpoint, {
149
+ ...requestOptions,
154
150
  method: 'POST',
155
151
  body,
156
152
  headers: {
@@ -174,7 +170,7 @@ query FormieHtmlForm($handle: String!, $input: ServerRenderPayloadInput) {
174
170
  return payload;
175
171
  }
176
172
 
177
- export async function requestRefreshTokens(endpoint: string, handle: string, renderId?: string): Promise<FormEndpointPayload['refreshTokens']> {
173
+ export async function requestRefreshTokens(endpoint: string, handle: string, renderId?: string, requestOptions: BrowserRequestOptions = {}, requestToken?: string): Promise<FormEndpointPayload['refreshTokens']> {
178
174
  const url = new URL(endpoint, window.location.origin);
179
175
  url.searchParams.set('handle', handle);
180
176
 
@@ -182,12 +178,14 @@ export async function requestRefreshTokens(endpoint: string, handle: string, ren
182
178
  url.searchParams.set('renderId', renderId);
183
179
  }
184
180
 
181
+ if (requestToken) url.searchParams.set('requestToken', requestToken);
182
+
185
183
  debug.log('requestRefreshTokens start.', {
186
184
  endpoint: url.toString(),
187
185
  handle,
188
186
  hasRenderId: !!renderId,
189
187
  });
190
- const response = await requestJson<FormEndpointPayload>(url.toString());
188
+ const response = await requestJson<FormEndpointPayload>(url.toString(), requestOptions);
191
189
  debug.log('requestRefreshTokens complete.', {
192
190
  hasRefreshTokens: !!response.refreshTokens,
193
191
  });
@@ -198,6 +196,7 @@ export async function requestRefreshTokens(endpoint: string, handle: string, ren
198
196
  export async function requestSetPage(url: string, form?: HTMLFormElement, pageId?: string): Promise<{
199
197
  success?: boolean;
200
198
  pageId?: string | number;
199
+ errors?: Record<string, string[]>;
201
200
  }> {
202
201
  const requestUrl = new URL(url, window.location.origin);
203
202
  const body = new FormData();
@@ -209,7 +208,7 @@ export async function requestSetPage(url: string, form?: HTMLFormElement, pageId
209
208
  if (form) {
210
209
  // Page changes must carry the same continuity identifiers the submit flow
211
210
  // uses so the backend can persist draft/session state for multipage forms.
212
- const inputNames = ['handle', 'renderId', 'draftContextToken', 'draftContext', 'continuationToken'];
211
+ const inputNames = ['handle', 'renderId', 'draftContextToken', 'draftContext', 'progressId', 'requestToken', 'expectedVersion'];
213
212
 
214
213
  inputNames.forEach((name) => {
215
214
  const input = form.querySelector(`input[name="${name}"]`) as HTMLInputElement | null;
@@ -220,11 +219,9 @@ export async function requestSetPage(url: string, form?: HTMLFormElement, pageId
220
219
  }
221
220
  });
222
221
 
223
- const csrfInput = form.querySelector('input[name="CRAFT_CSRF_TOKEN"]') as HTMLInputElement | null;
224
- const csrfValue = csrfInput?.value?.trim();
225
-
226
- if (csrfValue) {
227
- body.append('CRAFT_CSRF_TOKEN', csrfValue);
222
+ appendFormCsrfToFormData(body, form);
223
+ for (const [name, value] of new FormData(form)) {
224
+ if (name.startsWith('fields[')) body.append(name, value);
228
225
  }
229
226
  }
230
227
 
@@ -232,13 +229,22 @@ export async function requestSetPage(url: string, form?: HTMLFormElement, pageId
232
229
  requestUrl: requestUrl.toString(),
233
230
  pageId: pageId || null,
234
231
  });
235
- const result = await requestJson<{
232
+ const response = await request(requestUrl.toString(), {
233
+ method: 'POST', body, profile: form?.dataset.formieRequestProfile as BrowserRequestOptions['profile'],
234
+ });
235
+ const result = await response.json() as {
236
236
  success?: boolean;
237
237
  pageId?: string | number;
238
- }>(requestUrl.toString(), {
239
- method: 'POST',
240
- body,
241
- });
238
+ errors?: Record<string, string[]>;
239
+ session?: { version: number; tokens?: { request?: string } };
240
+ };
241
+ if (form && result.session) {
242
+ const session = result.session;
243
+ const version = form.querySelector<HTMLInputElement>('input[name="expectedVersion"]');
244
+ const token = form.querySelector<HTMLInputElement>('input[name="requestToken"]');
245
+ if (version) version.value = String(session.version);
246
+ if (token && session.tokens?.request) token.value = session.tokens.request;
247
+ }
242
248
  debug.log('requestSetPage complete.', result);
243
249
  return result;
244
250
  }
@@ -257,34 +263,28 @@ export function clearSubmissionOnUnload(endpoint: string, form: HTMLFormElement)
257
263
  }
258
264
  });
259
265
 
260
- const csrfInput = form.querySelector('input[name="CRAFT_CSRF_TOKEN"]') as HTMLInputElement | null;
261
- const csrfValue = csrfInput?.value?.trim();
262
-
263
- if (csrfValue) {
264
- body.append('CRAFT_CSRF_TOKEN', csrfValue);
265
- }
266
+ appendFormCsrfToFormData(body, form);
266
267
 
267
268
  debug.log('clearSubmissionOnUnload start.', {
268
269
  requestUrl: requestUrl.toString(),
269
270
  });
270
271
 
271
272
  try {
272
- if (typeof navigator.sendBeacon === 'function' && navigator.sendBeacon(requestUrl.toString(), body)) {
273
+ if (form.dataset.formieRequestProfile !== 'cross-origin-public' && typeof navigator.sendBeacon === 'function' && navigator.sendBeacon(requestUrl.toString(), body)) {
273
274
  return;
274
275
  }
275
276
  } catch (_error) {
276
277
  // Fall back to keepalive fetch below when Beacon is unavailable or rejects the payload.
277
278
  }
278
279
 
279
- void fetch(requestUrl.toString(), {
280
+ void browserRequest(requestUrl.toString(), {
280
281
  method: 'POST',
281
282
  body,
282
- credentials: 'include',
283
283
  keepalive: true,
284
284
  headers: {
285
285
  Accept: 'application/json',
286
286
  },
287
- });
287
+ }, { profile: form.dataset.formieRequestProfile as BrowserRequestOptions['profile'] });
288
288
  }
289
289
 
290
290
  export async function submitForm(form: HTMLFormElement, formData: FormData): Promise<FormSubmitResult> {
@@ -297,14 +297,13 @@ export async function submitForm(form: HTMLFormElement, formData: FormData): Pro
297
297
  action,
298
298
  submitAction: formData.get('submitAction'),
299
299
  });
300
- const response = await fetch(action, {
300
+ const response = await browserRequest(action, {
301
301
  method,
302
302
  body: formData,
303
- credentials: 'include',
304
303
  headers: {
305
304
  Accept: 'application/json',
306
305
  },
307
- });
306
+ }, { profile: form.dataset.formieRequestProfile as BrowserRequestOptions['profile'] });
308
307
 
309
308
  const contentType = response.headers.get('content-type') || '';
310
309
 
@@ -1,3 +1,5 @@
1
+ import { browserRequest } from '@verbb/formie-core';
2
+ import { getFormBrowserRequestOptions } from './request-profile';
1
3
  export type CountryFromIpResponse = {
2
4
  countryCode?: string | null;
3
5
  countryName?: string | null;
@@ -5,23 +7,25 @@ export type CountryFromIpResponse = {
5
7
 
6
8
  const DEFAULT_COUNTRY_FROM_IP_ACTION = 'formie/address/country-from-ip';
7
9
 
8
- let cachedLookup: Promise<CountryFromIpResponse | null> | null = null;
10
+ const cachedLookups = new Map<string, Promise<CountryFromIpResponse | null>>();
9
11
 
10
12
  function buildActionUrl(action: string): string {
11
- return new URL(action.startsWith('/') ? action : `/actions/${action}`, window.location.origin).toString();
13
+ return new URL(/^https?:\/\//.test(action) || action.startsWith('/') ? action : `/actions/${action}`, window.location.origin).toString();
12
14
  }
13
15
 
14
16
  export async function fetchCountryFromIp(
15
17
  action: string = DEFAULT_COUNTRY_FROM_IP_ACTION,
18
+ form?: HTMLFormElement | null,
16
19
  ): Promise<CountryFromIpResponse | null> {
17
- if (!cachedLookup) {
18
- cachedLookup = (async () => {
20
+ const key = buildActionUrl(action);
21
+ if (!cachedLookups.has(key)) {
22
+ cachedLookups.set(key, (async () => {
19
23
  try {
20
- const response = await fetch(buildActionUrl(action), {
24
+ const response = await browserRequest(key, {
21
25
  headers: {
22
26
  Accept: 'application/json',
23
27
  },
24
- });
28
+ }, getFormBrowserRequestOptions(form));
25
29
 
26
30
  if (!response.ok) {
27
31
  return null;
@@ -37,17 +41,18 @@ export async function fetchCountryFromIp(
37
41
  } catch {
38
42
  return null;
39
43
  }
40
- })();
44
+ })());
41
45
  }
42
46
 
43
- return cachedLookup;
47
+ return cachedLookups.get(key)!;
44
48
  }
45
49
 
46
50
  export function createGeoIpLookup(
47
51
  action: string = DEFAULT_COUNTRY_FROM_IP_ACTION,
52
+ form?: HTMLFormElement | null,
48
53
  ): (callback: (countryCode: string) => void) => void {
49
54
  return (callback) => {
50
- void fetchCountryFromIp(action).then((data) => {
55
+ void fetchCountryFromIp(action, form).then((data) => {
51
56
  callback(data?.countryCode?.toLowerCase() || '');
52
57
  });
53
58
  };