@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,6 +1,6 @@
1
1
  import type {
2
- FormieModuleDefinition,
3
- FormieModuleInstance,
2
+ BrowserModuleDefinition,
3
+ BrowserModuleInstance,
4
4
  ModuleSetupContext,
5
5
  } from '#contracts/modules';
6
6
  import {
@@ -30,7 +30,7 @@ export type ManagedAddressModuleAdapter<
30
30
  TApi,
31
31
  TWidget,
32
32
  > = {
33
- id: string;
33
+ moduleId: string;
34
34
  load: (ctx: AddressModuleSetupContext<TProvider>) => Promise<TApi>;
35
35
  mount: (args: {
36
36
  api: TApi;
@@ -61,9 +61,11 @@ export function createManagedAddressModule<
61
61
  TProvider extends Record<string, unknown>,
62
62
  TApi,
63
63
  TWidget,
64
- >(adapter: ManagedAddressModuleAdapter<TProvider, TApi, TWidget>): FormieModuleDefinition {
64
+ >(adapter: ManagedAddressModuleAdapter<TProvider, TApi, TWidget>): BrowserModuleDefinition {
65
65
  return {
66
- id: adapter.id,
66
+ moduleId: adapter.moduleId,
67
+ version: 2,
68
+ surfaces: ['server-rendered', 'client-rendered', 'cp-edit'],
67
69
  kind: 'address',
68
70
  match: (ctx) => {
69
71
  const input = ctx.target.querySelector('[data-formie-address-autocomplete-input]');
@@ -71,10 +73,10 @@ export function createManagedAddressModule<
71
73
  return !!input;
72
74
  },
73
75
  setup: async (ctx) => {
74
- const options = normalizeAddressModuleOptions<TProvider>(adapter.id, ctx.options || {});
76
+ const options = normalizeAddressModuleOptions<TProvider>(adapter.moduleId.split(':')[1], ctx.options || {});
75
77
  const services = createAddressHostServices(ctx);
76
78
  debug.log('Setup module.', {
77
- moduleId: adapter.id,
79
+ moduleId: adapter.moduleId,
78
80
  });
79
81
 
80
82
  const setupCtx: AddressModuleSetupContext<TProvider> = {
@@ -90,11 +92,11 @@ export function createManagedAddressModule<
90
92
 
91
93
  if (!input) {
92
94
  console.warn(
93
- `[formie] Address module "${adapter.id}" skipped: no autocomplete input found in target. ` +
95
+ `[formie] Address module "${adapter.moduleId}" skipped: no autocomplete input found in target. ` +
94
96
  'Ensure the Address field has the Auto-Complete subfield enabled.',
95
97
  );
96
98
  debug.warn('Autocomplete input missing; skipping module.', {
97
- moduleId: adapter.id,
99
+ moduleId: adapter.moduleId,
98
100
  });
99
101
  return {
100
102
  destroy: () => { },
@@ -104,7 +106,7 @@ export function createManagedAddressModule<
104
106
  const getApi = async(): Promise<TApi> => {
105
107
  if (!apiPromise) {
106
108
  debug.log('Loading provider API.', {
107
- moduleId: adapter.id,
109
+ moduleId: adapter.moduleId,
108
110
  });
109
111
  apiPromise = adapter.load(setupCtx);
110
112
  }
@@ -126,7 +128,7 @@ export function createManagedAddressModule<
126
128
  provider: options.provider,
127
129
  });
128
130
  debug.log('Widget mounted.', {
129
- moduleId: adapter.id,
131
+ moduleId: adapter.moduleId,
130
132
  });
131
133
  };
132
134
 
@@ -178,7 +180,7 @@ export function createManagedAddressModule<
178
180
  return {
179
181
  destroy: async () => {
180
182
  debug.log('Destroying module.', {
181
- moduleId: adapter.id,
183
+ moduleId: adapter.moduleId,
182
184
  });
183
185
  cleanups.forEach((c) => c());
184
186
 
@@ -193,7 +195,7 @@ export function createManagedAddressModule<
193
195
  provider: options.provider,
194
196
  });
195
197
  debug.log('Widget unmounted.', {
196
- moduleId: adapter.id,
198
+ moduleId: adapter.moduleId,
197
199
  });
198
200
  }
199
201
  },
@@ -1,3 +1,6 @@
1
+ import { appendFormCsrfToFormData } from '#utils/csrf';
2
+ import { browserRequest } from '@verbb/formie-core';
3
+ import { getFormBrowserRequestOptions } from '#utils/request-profile';
1
4
  import { defineAddressModule } from '#modules/address/api';
2
5
  import type { AddressHostServices } from '#modules/address/host';
3
6
  import { getAddressProviderEventName } from '#utils/event-names';
@@ -7,6 +10,7 @@ type GoogleMaps = typeof google;
7
10
 
8
11
  type GoogleAddressProviderOptions = {
9
12
  apiKey?: string;
13
+ geocodeEndpoint?: string;
10
14
  options?: Record<string, unknown>;
11
15
  countryDefaultValue?: string;
12
16
  };
@@ -231,7 +235,7 @@ export const googleAddressModule = defineAddressModule<
231
235
  GoogleMaps,
232
236
  google.maps.places.PlaceAutocompleteElement
233
237
  >({
234
- id: 'google-address',
238
+ moduleId: 'formie:google-address',
235
239
  load: async ({ options }) => {
236
240
  const apiKey = options.provider.apiKey;
237
241
 
@@ -335,10 +339,10 @@ export const googleAddressModule = defineAddressModule<
335
339
 
336
340
  return autocomplete;
337
341
  },
338
- onCurrentLocation: async (position, { field, services }) => {
342
+ onCurrentLocation: async (position, { field, services, provider }) => {
339
343
  const { latitude, longitude } = position.coords;
340
344
  const form = services.form;
341
- const actionUrl = form?.action || window.location.href;
345
+ const actionUrl = provider.geocodeEndpoint || form?.action || window.location.href;
342
346
  const fieldHandle = field.getAttribute('data-formie-field-handle')?.trim();
343
347
  const formHandle = (form?.querySelector('[name="handle"]') as HTMLInputElement | null)?.value?.trim();
344
348
 
@@ -346,20 +350,20 @@ export const googleAddressModule = defineAddressModule<
346
350
 
347
351
  try {
348
352
  const formData = new FormData();
353
+ if (form) appendFormCsrfToFormData(formData, form);
349
354
  formData.append('action', 'formie/address/google-places-geocode');
350
355
  formData.append('latlng', `${latitude},${longitude}`);
351
356
  formData.append('handle', formHandle);
352
357
  formData.append('fieldHandle', fieldHandle);
353
358
 
354
- const response = await fetch(actionUrl, {
359
+ const response = await browserRequest(actionUrl, {
355
360
  method: 'POST',
356
361
  body: formData,
357
- credentials: 'include',
358
362
  headers: {
359
363
  'X-Requested-With': 'XMLHttpRequest',
360
364
  Accept: 'application/json',
361
365
  },
362
- });
366
+ }, getFormBrowserRequestOptions(form));
363
367
 
364
368
  const data = await response.json();
365
369
 
@@ -1,6 +1,6 @@
1
- import type { FormieModuleDefinition } from '#contracts/modules';
1
+ import type { BrowserModuleDefinition } from '#contracts/modules';
2
2
 
3
- export const builtinAddressModuleLoaders: Record<string, () => Promise<FormieModuleDefinition>> = {
3
+ export const builtinAddressModuleLoaders: Record<string, () => Promise<BrowserModuleDefinition>> = {
4
4
  // Address providers stay behind lazy importer entries because their SDKs are
5
5
  // optional and often much heavier than the base form client.
6
6
  'address-finder': () => import('#modules/address/address-finder').then((m) => m.addressFinderModule),
@@ -35,7 +35,7 @@ export const loqateModule = defineAddressModule<
35
35
  PcaGlobal,
36
36
  PcaAddressInstance
37
37
  >({
38
- id: 'loqate',
38
+ moduleId: 'formie:loqate',
39
39
  load: async () => {
40
40
  await loadScriptAndEnsureGlobal<PcaGlobal>('pca', {
41
41
  id: SCRIPT_ID,
@@ -28,7 +28,7 @@ export const placeKitModule = defineAddressModule<
28
28
  (apiKey: string, options: Record<string, unknown>) => PlaceKitPicker,
29
29
  PlaceKitPicker
30
30
  >({
31
- id: 'place-kit',
31
+ moduleId: 'formie:place-kit',
32
32
  load: async () => {
33
33
  const module = await import('@placekit/autocomplete-js');
34
34
 
@@ -34,7 +34,7 @@ async function loadCaptchaEuGlobal(options: CaptchaEuProviderOptions): Promise<C
34
34
  }
35
35
 
36
36
  export const captchaEuModule = defineCaptchaModule<CaptchaEuProviderOptions, CaptchaEuGlobal, HTMLElement>({
37
- id: 'captcha-eu',
37
+ moduleId: 'formie:captcha-eu',
38
38
  defaultPlaceholderSelector: '[data-captcha-eu-placeholder]',
39
39
  defaultTokenFieldNames: ['captcha-eu-token'],
40
40
  load: ({ options }) => {
@@ -69,7 +69,7 @@ export const captchaEuModule = defineCaptchaModule<CaptchaEuProviderOptions, Cap
69
69
 
70
70
  return container;
71
71
  },
72
- screen: async ({ placeholder, services, stageCtx }) => {
72
+ challenge: async ({ placeholder, services, stageCtx }) => {
73
73
  // Captcha.eu solves through its own widget lifecycle and callback. By
74
74
  // screen time we only need to wait for the shared transport layer to
75
75
  // contain the provider payload.
@@ -1,8 +1,8 @@
1
1
  import type {
2
- FormieModuleDefinition,
3
- FormieModuleInstance,
2
+ BrowserModuleDefinition,
3
+ BrowserModuleInstance,
4
4
  ModuleSetupContext,
5
- SubmitHookContext,
5
+ BeforeSubmitContext,
6
6
  } from '#contracts/modules';
7
7
  import type { FormSubmitResult } from '#contracts/schema';
8
8
  import { DEFAULT_WAIT_FOR_VALUE_MS } from '#modules/captchas/constants';
@@ -21,11 +21,11 @@ type Cleanup = () => void;
21
21
  const debug = createDebug('captchas');
22
22
 
23
23
  type CaptchaModuleFactory<TProvider extends Record<string, unknown>> = {
24
- id: string;
24
+ moduleId: string;
25
25
  defaultPlaceholderSelector: string;
26
26
  defaultTokenFieldNames?: string[];
27
27
  defaultWaitForValueMs?: number;
28
- setup: (ctx: CaptchaModuleSetupContext<TProvider>) => Promise<FormieModuleInstance | void>;
28
+ setup: (ctx: CaptchaModuleSetupContext<TProvider>) => Promise<BrowserModuleInstance | void>;
29
29
  };
30
30
 
31
31
  export type ManagedCaptchaModuleAdapter<
@@ -33,7 +33,7 @@ export type ManagedCaptchaModuleAdapter<
33
33
  TApi,
34
34
  TWidget,
35
35
  > = {
36
- id: string;
36
+ moduleId: string;
37
37
  defaultPlaceholderSelector: string;
38
38
  defaultTokenFieldNames?: string[];
39
39
  load: (ctx: CaptchaModuleSetupContext<TProvider>) => Promise<TApi>;
@@ -45,14 +45,14 @@ export type ManagedCaptchaModuleAdapter<
45
45
  options: NormalizedCaptchaModuleOptions<TProvider>;
46
46
  provider: TProvider;
47
47
  }) => Promise<TWidget> | TWidget;
48
- screen: (args: {
48
+ challenge: (args: {
49
49
  api: TApi;
50
50
  widget: TWidget;
51
51
  placeholder: HTMLElement;
52
52
  services: CaptchaHostServices;
53
53
  options: NormalizedCaptchaModuleOptions<TProvider>;
54
54
  provider: TProvider;
55
- stageCtx: SubmitHookContext;
55
+ stageCtx: BeforeSubmitContext;
56
56
  }) => Promise<void> | void;
57
57
  unmount?: (args: {
58
58
  api: TApi;
@@ -79,14 +79,16 @@ export type CaptchaModuleSetupContext<TProvider extends Record<string, unknown>>
79
79
  };
80
80
 
81
81
  export function createCaptchaModule<TProvider extends Record<string, unknown> = Record<string, unknown>>({
82
- id,
82
+ moduleId,
83
83
  defaultPlaceholderSelector,
84
84
  defaultTokenFieldNames = [],
85
85
  defaultWaitForValueMs = DEFAULT_WAIT_FOR_VALUE_MS,
86
86
  setup,
87
- }: CaptchaModuleFactory<TProvider>): FormieModuleDefinition {
87
+ }: CaptchaModuleFactory<TProvider>): BrowserModuleDefinition {
88
88
  return {
89
- id,
89
+ moduleId,
90
+ version: 2,
91
+ surfaces: ['server-rendered', 'client-rendered'],
90
92
  kind: 'captcha',
91
93
  match: () => true,
92
94
  setup: async(ctx) => {
@@ -95,47 +97,58 @@ export function createCaptchaModule<TProvider extends Record<string, unknown> =
95
97
  // 1. normalized options from the backend manifest
96
98
  // 2. shared host services for placeholders, errors, tokens, and
97
99
  // refresh-token events
98
- const options = normalizeCaptchaModuleOptions<TProvider>(id, ctx.options || {}, {
100
+ const options = normalizeCaptchaModuleOptions<TProvider>(moduleId.split(':')[1], ctx.options || {}, {
99
101
  defaultPlaceholderSelector,
100
102
  defaultTokenFieldNames,
101
103
  defaultWaitForValueMs,
102
104
  });
103
105
  debug.log('Setup module.', {
104
- moduleId: id,
106
+ moduleId: moduleId,
105
107
  placeholderSelector: options.ui.placeholderSelector,
106
108
  tokenFieldNames: options.transport.tokenFieldNames,
107
109
  });
110
+ let placeholder: HTMLElement | undefined;
111
+ if (ctx.surface === 'client-rendered' && !ctx.root.querySelector(options.ui.placeholderSelector)) {
112
+ const attribute = /^\[(data-[a-z0-9-]+)\]$/.exec(options.ui.placeholderSelector)?.[1];
113
+ if (!attribute) throw new Error('The CAPTCHA needs a supported placeholder in the client-rendered form.');
114
+ placeholder = document.createElement('div');
115
+ placeholder.setAttribute(attribute, '');
116
+ (ctx.form ?? ctx.root).append(placeholder);
117
+ }
108
118
  const services = createCaptchaHostServices(ctx, options);
109
119
 
110
- return setup({
120
+ const instance = await setup({
111
121
  ...ctx,
112
122
  options,
113
123
  services,
114
124
  });
125
+ if (!instance) { placeholder?.remove(); return; }
126
+ const destroy = instance.destroy;
127
+ return { ...instance, destroy: async() => { await destroy(); placeholder?.remove(); } };
115
128
  },
116
129
  };
117
130
  }
118
131
 
119
132
  export function createPassiveCaptchaModule({
120
- id,
133
+ moduleId,
121
134
  defaultPlaceholderSelector,
122
135
  defaultTokenFieldNames = [],
123
136
  defaultWaitForValueMs = DEFAULT_WAIT_FOR_VALUE_MS,
124
137
  }: {
125
- id: string;
138
+ moduleId: string;
126
139
  defaultPlaceholderSelector: string;
127
140
  defaultTokenFieldNames?: string[];
128
141
  defaultWaitForValueMs?: number;
129
- }): FormieModuleDefinition {
142
+ }): BrowserModuleDefinition {
130
143
  return createCaptchaModule<Record<string, never>>({
131
- id,
144
+ moduleId,
132
145
  defaultPlaceholderSelector,
133
146
  defaultTokenFieldNames,
134
147
  defaultWaitForValueMs,
135
148
  setup: async({ services, options, root }) => {
136
149
  // Passive captchas do not have a browser SDK or visible widget.
137
150
  // Their lifecycle is basically "keep the hidden transport input in
138
- // sync, then verify it still exists at screen-stage submit time".
151
+ // sync, then verify it still exists at challenge-stage submit time".
139
152
  const cleanups: Cleanup[] = [];
140
153
  let activePlaceholder = services.placeholder.getPrimary();
141
154
  let sessionKey = options.transport.sessionKey;
@@ -159,7 +172,7 @@ export function createPassiveCaptchaModule({
159
172
  (placeholder) => {
160
173
  activePlaceholder = placeholder;
161
174
  debug.log('Passive placeholder visible.', {
162
- moduleId: id,
175
+ moduleId: moduleId,
163
176
  });
164
177
  renderPlaceholder(placeholder);
165
178
  },
@@ -192,8 +205,8 @@ export function createPassiveCaptchaModule({
192
205
  cleanup();
193
206
  });
194
207
  },
195
- onBeforeStage: async(stageCtx) => {
196
- if (stageCtx.stage !== 'screen' || stageCtx.action !== 'submit') {
208
+ beforeSubmit: async(stageCtx) => {
209
+ if (stageCtx.action !== 'submit') {
197
210
  return;
198
211
  }
199
212
 
@@ -209,7 +222,7 @@ export function createPassiveCaptchaModule({
209
222
  const message = services.errors.getDefaultMessage();
210
223
  services.errors.show(message, activePlaceholder);
211
224
  debug.warn('Passive captcha missing token.', {
212
- moduleId: id,
225
+ moduleId: moduleId,
213
226
  tokenFieldNames,
214
227
  });
215
228
  stageCtx.abort(message);
@@ -224,9 +237,9 @@ export function createManagedCaptchaModule<
224
237
  TProvider extends Record<string, unknown>,
225
238
  TApi,
226
239
  TWidget,
227
- >(adapter: ManagedCaptchaModuleAdapter<TProvider, TApi, TWidget>): FormieModuleDefinition {
240
+ >(adapter: ManagedCaptchaModuleAdapter<TProvider, TApi, TWidget>): BrowserModuleDefinition {
228
241
  return createCaptchaModule<TProvider>({
229
- id: adapter.id,
242
+ moduleId: adapter.moduleId,
230
243
  defaultPlaceholderSelector: adapter.defaultPlaceholderSelector,
231
244
  defaultTokenFieldNames: adapter.defaultTokenFieldNames,
232
245
  setup: async(ctx) => {
@@ -234,7 +247,7 @@ export function createManagedCaptchaModule<
234
247
  // owns only the generic plumbing:
235
248
  // - load the provider API once
236
249
  // - mount/unmount when placeholders appear/disappear
237
- // - call the provider's `screen()` at submit time
250
+ // - call the provider's `challenge()` at submit time
238
251
  //
239
252
  // It intentionally does not know provider policy such as "execute
240
253
  // now" vs "must already be solved" - that stays in each module.
@@ -243,13 +256,14 @@ export function createManagedCaptchaModule<
243
256
  const mountPromises = new Map<HTMLElement, Promise<void>>();
244
257
  let activePlaceholder = ctx.services.placeholder.getPrimary();
245
258
  let destroyed = false;
259
+ let initializationError: unknown;
246
260
 
247
261
  let apiPromise: Promise<TApi> | null = null;
248
262
 
249
263
  const getApi = async(): Promise<TApi> => {
250
264
  if (!apiPromise) {
251
265
  debug.log('Loading captcha provider API.', {
252
- moduleId: adapter.id,
266
+ moduleId: adapter.moduleId,
253
267
  });
254
268
  apiPromise = adapter.load(ctx);
255
269
  }
@@ -284,7 +298,7 @@ export function createManagedCaptchaModule<
284
298
  placeholder.innerHTML = '';
285
299
  ctx.services.tokens.clear();
286
300
  debug.log('Unmounted captcha placeholder widget.', {
287
- moduleId: adapter.id,
301
+ moduleId: adapter.moduleId,
288
302
  });
289
303
 
290
304
  if (activePlaceholder === placeholder) {
@@ -293,9 +307,8 @@ export function createManagedCaptchaModule<
293
307
  };
294
308
 
295
309
  const mountPlaceholder = async(placeholder: HTMLElement) => {
296
- if (destroyed || mountedWidgets.has(placeholder) || mountPromises.has(placeholder)) {
297
- return;
298
- }
310
+ if (destroyed || mountedWidgets.has(placeholder)) return;
311
+ if (mountPromises.has(placeholder)) return mountPromises.get(placeholder);
299
312
 
300
313
  // Guard against repeated visibility events or rapid DOM churn
301
314
  // by tracking an in-flight mount promise per placeholder.
@@ -322,7 +335,7 @@ export function createManagedCaptchaModule<
322
335
  mountedWidgets.set(placeholder, widget);
323
336
  activePlaceholder = placeholder;
324
337
  debug.log('Mounted captcha placeholder widget.', {
325
- moduleId: adapter.id,
338
+ moduleId: adapter.moduleId,
326
339
  });
327
340
  })().finally(() => {
328
341
  mountPromises.delete(placeholder);
@@ -336,7 +349,7 @@ export function createManagedCaptchaModule<
336
349
  const visibility = ctx.services.placeholder.observe(
337
350
  (placeholder) => {
338
351
  activePlaceholder = placeholder;
339
- void mountPlaceholder(placeholder);
352
+ void mountPlaceholder(placeholder).catch((error) => { initializationError = error; });
340
353
  },
341
354
  (placeholder) => {
342
355
  void unmountPlaceholder(placeholder);
@@ -399,7 +412,7 @@ export function createManagedCaptchaModule<
399
412
  return;
400
413
  }
401
414
 
402
- if (detail?.ok === false && detail?.stage === 'screen') {
415
+ if (detail?.ok === false && detail?.stage === 'challenge') {
403
416
  return;
404
417
  }
405
418
 
@@ -420,7 +433,18 @@ export function createManagedCaptchaModule<
420
433
  }));
421
434
  }
422
435
 
436
+ visibility.reconcileImmediate();
437
+ try {
438
+ await Promise.all([...mountPromises.values()]);
439
+ if (initializationError) throw initializationError;
440
+ } catch (error) {
441
+ destroyed = true;
442
+ cleanups.forEach((cleanup) => cleanup());
443
+ throw error;
444
+ }
445
+
423
446
  return {
447
+ assertReady: () => { if (initializationError) throw initializationError; },
424
448
  destroy: async() => {
425
449
  destroyed = true;
426
450
  cleanups.forEach((cleanup) => {
@@ -431,22 +455,22 @@ export function createManagedCaptchaModule<
431
455
  await unmountPlaceholder(placeholder);
432
456
  }
433
457
  },
434
- onBeforeStage: async(stageCtx) => {
435
- if (stageCtx.stage !== 'screen' || stageCtx.action !== 'submit') {
458
+ beforeSubmit: async(stageCtx) => {
459
+ if (stageCtx.action !== 'submit') {
436
460
  return;
437
461
  }
438
462
 
439
463
  // Reconcile immediately so captcha placeholders on the newly
440
- // visible page are mounted before screen-stage policy runs.
464
+ // visible page are mounted before challenge-stage policy runs.
441
465
  visibility.reconcileImmediate();
442
466
 
443
- // By the time provider `screen()` runs, the widget is
467
+ // By the time provider `challenge()` runs, the widget is
444
468
  // guaranteed to be mounted and the active placeholder is
445
469
  // resolved. That lets provider code focus on challenge
446
470
  // policy rather than generic lifecycle setup.
447
471
  const visiblePlaceholders = visibility.getVisible();
448
472
 
449
- // Captcha fields on hidden pages should not run in screen stage.
473
+ // Captcha fields on hidden pages should not run in challenge stage.
450
474
  if (visiblePlaceholders.length === 0) {
451
475
  return;
452
476
  }
@@ -464,8 +488,8 @@ export function createManagedCaptchaModule<
464
488
  if (!widget) {
465
489
  const message = ctx.services.errors.getDefaultMessage();
466
490
  ctx.services.errors.show(message, placeholder);
467
- debug.warn('Captcha widget unavailable at screen stage.', {
468
- moduleId: adapter.id,
491
+ debug.warn('Captcha widget unavailable at challenge stage.', {
492
+ moduleId: adapter.moduleId,
469
493
  });
470
494
  stageCtx.abort(message);
471
495
  return;
@@ -473,7 +497,7 @@ export function createManagedCaptchaModule<
473
497
 
474
498
  const api = await getApi();
475
499
 
476
- await adapter.screen({
500
+ await adapter.challenge({
477
501
  api,
478
502
  widget,
479
503
  placeholder,
@@ -8,6 +8,7 @@ type FriendlyCaptchaProviderOptions = {
8
8
  siteKey?: string | null;
9
9
  language?: string;
10
10
  startMode?: string;
11
+ theme?: string;
11
12
  };
12
13
 
13
14
  export const friendlyCaptchaV1Module = defineCaptchaModule<
@@ -15,7 +16,7 @@ export const friendlyCaptchaV1Module = defineCaptchaModule<
15
16
  FriendlyChallengeModule,
16
17
  import('friendly-challenge').WidgetInstance
17
18
  >({
18
- id: 'friendly-captcha-v1',
19
+ moduleId: 'formie:friendly-captcha-v1',
19
20
  defaultPlaceholderSelector: '[data-friendly-captcha-placeholder]',
20
21
  defaultTokenFieldNames: ['frc-captcha-solution'],
21
22
  load: async() => {
@@ -24,6 +25,15 @@ export const friendlyCaptchaV1Module = defineCaptchaModule<
24
25
  return import('friendly-challenge');
25
26
  },
26
27
  mount: ({ api, container, provider, services }) => {
28
+ // V1 uses a `dark` class on the widget element (no `auto` theme option).
29
+ const theme = provider.theme || 'auto';
30
+ const prefersDark = window.matchMedia?.('(prefers-color-scheme: dark)').matches ?? false;
31
+ const useDark = theme === 'dark' || (theme === 'auto' && prefersDark);
32
+
33
+ if (useDark) {
34
+ container.classList.add('dark');
35
+ }
36
+
27
37
  return new api.WidgetInstance(container, {
28
38
  sitekey: provider.siteKey || '',
29
39
  startMode: (provider.startMode as 'auto' | 'focus' | 'none' | undefined) || 'none',
@@ -43,7 +53,7 @@ export const friendlyCaptchaV1Module = defineCaptchaModule<
43
53
  },
44
54
  });
45
55
  },
46
- screen: async({ widget, placeholder, services, stageCtx }) => {
56
+ challenge: async({ widget, placeholder, services, stageCtx }) => {
47
57
  // FriendlyCaptcha is started programmatically on submit when needed.
48
58
  if (services.tokens.has()) {
49
59
  return;
@@ -10,6 +10,7 @@ type FriendlyCaptchaProviderOptions = {
10
10
  siteKey?: string | null;
11
11
  language?: string;
12
12
  startMode?: string;
13
+ theme?: string;
13
14
  };
14
15
 
15
16
  export const friendlyCaptchaV2Module = defineCaptchaModule<
@@ -17,7 +18,7 @@ export const friendlyCaptchaV2Module = defineCaptchaModule<
17
18
  FriendlyCaptchaSdkModule,
18
19
  FriendlyCaptchaWidget
19
20
  >({
20
- id: 'friendly-captcha-v2',
21
+ moduleId: 'formie:friendly-captcha-v2',
21
22
  defaultPlaceholderSelector: '[data-friendly-captcha-placeholder]',
22
23
  defaultTokenFieldNames: ['frc-captcha-response'],
23
24
  load: async () => {
@@ -34,6 +35,7 @@ export const friendlyCaptchaV2Module = defineCaptchaModule<
34
35
  formFieldName: 'frc-captcha-response',
35
36
  language: provider.language,
36
37
  startMode: (provider.startMode as 'auto' | 'focus' | 'none' | undefined) || 'none',
38
+ theme: (provider.theme as 'auto' | 'light' | 'dark' | undefined) || 'auto',
37
39
  });
38
40
 
39
41
  widget.addEventListener('frc:widget.complete', (event) => {
@@ -62,7 +64,7 @@ export const friendlyCaptchaV2Module = defineCaptchaModule<
62
64
 
63
65
  return widget;
64
66
  },
65
- screen: async ({ widget, placeholder, services, stageCtx }) => {
67
+ challenge: async ({ widget, placeholder, services, stageCtx }) => {
66
68
  // As with v1, start the challenge only if no token is currently present.
67
69
  if (services.tokens.has()) {
68
70
  return;
@@ -66,7 +66,7 @@ async function loadHcaptchaGlobal(options: HcaptchaProviderOptions): Promise<Hca
66
66
  }
67
67
 
68
68
  export const hcaptchaModule = defineCaptchaModule<HcaptchaProviderOptions, HcaptchaGlobal, number | string>({
69
- id: 'hcaptcha',
69
+ moduleId: 'formie:hcaptcha',
70
70
  defaultPlaceholderSelector: '[data-hcaptcha-placeholder]',
71
71
  defaultTokenFieldNames: ['h-captcha-response'],
72
72
  load: ({ options }) => {
@@ -77,7 +77,7 @@ export const hcaptchaModule = defineCaptchaModule<HcaptchaProviderOptions, Hcapt
77
77
  },
78
78
  mount: ({ api, container, provider, services }) => {
79
79
  // Mount only renders/configures the widget. It does not decide whether
80
- // a submit should be blocked; that responsibility lives in `screen()`.
80
+ // a submit should be blocked; that responsibility lives in `challenge()`.
81
81
  return api.render(container, {
82
82
  sitekey: provider.siteKey || '',
83
83
  theme: provider.theme || 'light',
@@ -113,10 +113,10 @@ export const hcaptchaModule = defineCaptchaModule<HcaptchaProviderOptions, Hcapt
113
113
  },
114
114
  });
115
115
  },
116
- screen: ({ api, widget, placeholder, services, stageCtx }) => {
117
- // `screen()` runs at submit time. By the time we get here the widget is
116
+ challenge: ({ api, widget, placeholder, services, stageCtx }) => {
117
+ // `challenge()` runs at submit time. By the time we get here the widget is
118
118
  // already mounted, so this method only answers "do we have a valid
119
- // token yet, and if not, can we get one before the screen stage ends?"
119
+ // token yet, and if not, can we get one before the challenge stage ends?"
120
120
  if (services.tokens.has()) {
121
121
  // Users can complete hCaptcha before clicking submit. In that case
122
122
  // there is nothing to do; we already have the token the backend
@@ -1,6 +1,6 @@
1
- import type { FormieModuleDefinition } from '#contracts/modules';
1
+ import type { BrowserModuleDefinition } from '#contracts/modules';
2
2
 
3
- export const builtinCaptchaModuleLoaders: Record<string, () => Promise<FormieModuleDefinition>> = {
3
+ export const builtinCaptchaModuleLoaders: Record<string, () => Promise<BrowserModuleDefinition>> = {
4
4
  // Module ids map directly to importer functions so the loader can fetch only
5
5
  // the captcha chunks required by the current form manifest.
6
6
  'captcha-eu': () => import('#modules/captchas/captcha-eu').then((module) => module.captchaEuModule),
@@ -23,7 +23,7 @@ type RecaptchaEnterpriseProviderOptions = RecaptchaProviderOptions & {
23
23
  // Shared captcha services should not know about any of those distinctions, so
24
24
  // this module owns the branching itself.
25
25
  export const recaptchaEnterpriseModule = defineCaptchaModule<RecaptchaEnterpriseProviderOptions, RecaptchaGlobal, number | string>({
26
- id: 'recaptcha-enterprise',
26
+ moduleId: 'formie:recaptcha-enterprise',
27
27
  defaultPlaceholderSelector: '[data-recaptcha-placeholder]',
28
28
  defaultTokenFieldNames: ['g-recaptcha-response'],
29
29
  load: ({ options }) => {
@@ -75,7 +75,7 @@ export const recaptchaEnterpriseModule = defineCaptchaModule<RecaptchaEnterprise
75
75
  });
76
76
  });
77
77
  },
78
- screen: async({ api, widget, provider, placeholder, services, stageCtx }) => {
78
+ challenge: async({ api, widget, provider, placeholder, services, stageCtx }) => {
79
79
  const enterpriseApi = api.enterprise || api;
80
80
 
81
81
  if (provider.enterpriseType === 'checkbox') {