@verbb/formie-browser 1.0.13 → 1.0.15
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.
- package/CHANGELOG.md +80 -0
- package/dist/chunks/{address-country-Rx6GQTND.js → address-country-BcEQKw0T.js} +11 -5
- package/dist/chunks/{address-finder-k7_BXaRe.js → address-finder-BWGINCSA.js} +3 -3
- package/dist/chunks/address-state-DI3ndhMU.js +365 -0
- package/dist/chunks/{api-BUb6P-xu.js → api-BK9b340y.js} +93 -63
- package/dist/chunks/{api-DqpfkZWL.js → api-Dxl_CKw-.js} +202 -193
- package/dist/chunks/{api-sPqGbOww.js → api-ghNr2uxX.js} +15 -9
- package/dist/chunks/{async-nPFRNQ06.js → async-2GdXNFCE.js} +18 -1
- package/dist/chunks/{bpoint-Dlq34XH1.js → bpoint-BwtuyN_O.js} +3 -3
- package/dist/chunks/calculations-YEoVCG2q.js +310 -0
- package/dist/chunks/{captcha-eu-CGm8Lbrj.js → captcha-eu-DWLoQFKB.js} +6 -5
- package/dist/chunks/{checkbox-radio-BY2aTCOG.js → checkbox-radio-gsm8QPJg.js} +74 -75
- package/dist/chunks/{combobox-C0EuExSI.js → combobox-Ba08_cHL.js} +340 -328
- package/dist/chunks/conditions-Ceeg6ReZ.js +467 -0
- package/dist/chunks/country-from-ip-DDj_mFki.js +29 -0
- package/dist/chunks/csrf-DxHg_ZYt.js +61 -0
- package/dist/chunks/{custom-google-maps-BsaSirEZ.js → custom-google-maps-C4l2S3GO.js} +9 -3
- package/dist/chunks/{custom-link-CJ1-FjEM.js → custom-link-CLazgKYH.js} +8 -2
- package/dist/chunks/{custom-maps-DYW_pBid.js → custom-maps-DqcAlD0v.js} +10 -4
- package/dist/chunks/{date-picker-F9BUBDSW.js → date-picker-DaPvMrop.js} +467 -443
- package/dist/chunks/{dist-D6i-VNgM.js → dist-ZLeW0zZ3.js} +1281 -453
- package/dist/chunks/{eway-D9_m8ooj.js → eway-uw0DzU-L.js} +5 -5
- package/dist/chunks/field-references.resolver-Ca8MguFv.js +104 -0
- package/dist/chunks/{file-upload-rqfZNqlH.js → file-upload-B8_mH012.js} +119 -99
- package/dist/chunks/friendly-captcha-v1-CVXaXgDO.js +34 -0
- package/dist/chunks/{friendly-captcha-v2-CLSAbkqx.js → friendly-captcha-v2-DkeIVTKI.js} +6 -5
- package/dist/chunks/{friendly-challenge-8uPatWqk.js → friendly-challenge-DPG9MJb1.js} +111 -102
- package/dist/chunks/{go-cardless-DAT3ZaQf.js → go-cardless-c4wLJdOt.js} +2 -2
- package/dist/chunks/google-address-JHYvjwgN.js +168 -0
- package/dist/chunks/{hcaptcha-CGLAmgjO.js → hcaptcha-Dt3ELLlP.js} +4 -4
- package/dist/chunks/{hidden-D7_Ch-QN.js → hidden-BZhXnNPe.js} +8 -2
- package/dist/chunks/http-C2QdHuWO.js +27 -0
- package/dist/chunks/{leaflet-src-j-lHNmhR.js → leaflet-src-DE6rv6M-.js} +42 -46
- package/dist/chunks/{loqate-CJPzBmSm.js → loqate-m51iPyO1.js} +3 -3
- package/dist/chunks/{mollie-Bibhl67p.js → mollie-Bn0PX173.js} +2 -2
- package/dist/chunks/{moneris-H2wSqk9h.js → moneris-BRbUvI4t.js} +3 -3
- package/dist/chunks/opayo-BwWdInEP.js +188 -0
- package/dist/chunks/{paddle-DkvIxQdG.js → paddle-CympHVvd.js} +4 -4
- package/dist/chunks/{password-validation-D06uH9va.js → password-validation-C8_uO_c_.js} +18 -12
- package/dist/chunks/{paypal-CQpAF7Ua.js → paypal-h75h6wae.js} +8 -6
- package/dist/chunks/{payway-CwDkC7YZ.js → payway-C9rHTCC_.js} +4 -4
- package/dist/chunks/{phone-country-D3ayr60L.js → phone-country-B8fv-L5_.js} +80 -63
- package/dist/chunks/{place-kit-BGHklIZ-.js → place-kit-BnZGQ-L1.js} +11 -11
- package/dist/chunks/{placekit-autocomplete.esm-DWVabQlq.js → placekit-autocomplete.esm-CZZERYur.js} +11 -15
- package/dist/chunks/{recaptcha-enterprise-XPId5-bS.js → recaptcha-enterprise-CUs8BO6V.js} +4 -4
- package/dist/chunks/{recaptcha-shared-CLYJvojk.js → recaptcha-shared-DTbTw-MF.js} +2 -2
- package/dist/chunks/{recaptcha-v2-checkbox-Cy-0PEPc.js → recaptcha-v2-checkbox-P1McW4Pv.js} +4 -4
- package/dist/chunks/{recaptcha-v2-invisible-BwRH540O.js → recaptcha-v2-invisible-FGDqozSP.js} +4 -4
- package/dist/chunks/{recaptcha-v3-No4Ykrhk.js → recaptcha-v3-CflUHeZD.js} +4 -4
- package/dist/chunks/{repeater-BVnPUqTj.js → repeater-CyhDbozO.js} +60 -40
- package/dist/chunks/request-profile-DhwkeCpS.js +10 -0
- package/dist/chunks/rich-text-uV4o_pGk.js +361 -0
- package/dist/chunks/{chunk-3b4jIN3o.js → rolldown-runtime-DtPi1Y-2.js} +2 -2
- package/dist/chunks/{scripts-CbQ7agX3.js → scripts-DrccjrFv.js} +1 -1
- package/dist/chunks/{sdk-BhHAYgga.js → sdk-lkwSvlpM.js} +282 -234
- package/dist/chunks/{shared-Bx9s0i0P.js → shared-OKhSay59.js} +27 -21
- package/dist/chunks/{signature-IOhuicje.js → signature-7cldFbqj.js} +138 -112
- package/dist/chunks/skip-CEW89ExY.js +7 -0
- package/dist/chunks/{snaptcha-DKx98oon.js → snaptcha-C3QLYxFT.js} +2 -2
- package/dist/chunks/{square-DwdcOI1G.js → square-C1z5DPVw.js} +9 -7
- package/dist/chunks/{stripe-DaoG2FT1.js → stripe-Dmkdkq1y.js} +11 -12
- package/dist/chunks/{summary-Dks0gXS3.js → summary-Bn-g5SDx.js} +48 -46
- package/dist/chunks/{survey-likert-DkE6bR7B.js → survey-likert-fk1hUPff.js} +7 -1
- package/dist/chunks/{survey-rank-DN6x4IZM.js → survey-rank-BsLGl2LX.js} +37 -25
- package/dist/chunks/{survey-rating-BnsNL9U5.js → survey-rating-DVS6e6fM.js} +17 -5
- package/dist/chunks/{table-CAjCJHrL.js → table-j7MlLLCc.js} +22 -16
- package/dist/chunks/{text-limit-BGaY0jhI.js → text-limit-LGMPhFuC.js} +40 -34
- package/dist/chunks/{theme-classes-Tv7q7ToE.js → theme-classes-DAQuEqdP.js} +1 -1
- package/dist/chunks/{turnstile-DJakPzQD.js → turnstile-ptihQfLl.js} +4 -4
- package/dist/chunks/{upload-manager-W-QDgxKD.js → upload-manager-BW7l-BhF.js} +459 -387
- package/dist/chunks/{utils-Vqp8i_RM.js → utils-DpUBPadZ.js} +20 -14
- package/dist/css/formie-base.css +104 -1
- package/dist/css/theme/_messages.css +2 -0
- package/dist/css/theme/_tokens.css +31 -25
- package/dist/css/theme/forms/_form.css +5 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1779 -1415
- package/dist/js/contracts/client.d.ts +5 -3
- package/dist/js/contracts/client.d.ts.map +1 -1
- package/dist/js/contracts/common.d.ts +2 -2
- package/dist/js/contracts/common.d.ts.map +1 -1
- package/dist/js/contracts/modules.d.ts +37 -12
- package/dist/js/contracts/modules.d.ts.map +1 -1
- package/dist/js/contracts/schema.d.ts +20 -38
- package/dist/js/contracts/schema.d.ts.map +1 -1
- package/dist/js/contracts/theme.d.ts +4 -1
- package/dist/js/contracts/theme.d.ts.map +1 -1
- package/dist/js/core/client-rendered-modules.d.ts +17 -0
- package/dist/js/core/client-rendered-modules.d.ts.map +1 -0
- package/dist/js/core/create-formie-client.d.ts.map +1 -1
- package/dist/js/core/field-error-aria.d.ts +23 -0
- package/dist/js/core/field-error-aria.d.ts.map +1 -0
- package/dist/js/core/hydrate-modules.d.ts +10 -8
- package/dist/js/core/hydrate-modules.d.ts.map +1 -1
- package/dist/js/core/submit-result-state.d.ts.map +1 -1
- package/dist/js/core/submit-result-ui.d.ts +3 -1
- package/dist/js/core/submit-result-ui.d.ts.map +1 -1
- package/dist/js/core/validation-focus.d.ts.map +1 -1
- package/dist/js/modules/address/address-finder.d.ts +1 -1
- package/dist/js/modules/address/address-finder.d.ts.map +1 -1
- package/dist/js/modules/address/factories.d.ts +3 -3
- package/dist/js/modules/address/factories.d.ts.map +1 -1
- package/dist/js/modules/address/google-address.d.ts +12 -1
- package/dist/js/modules/address/google-address.d.ts.map +1 -1
- package/dist/js/modules/address/index.d.ts +2 -2
- package/dist/js/modules/address/index.d.ts.map +1 -1
- package/dist/js/modules/address/loqate.d.ts +1 -1
- package/dist/js/modules/address/loqate.d.ts.map +1 -1
- package/dist/js/modules/address/place-kit.d.ts +1 -1
- package/dist/js/modules/address/place-kit.d.ts.map +1 -1
- package/dist/js/modules/captchas/captcha-eu.d.ts +1 -1
- package/dist/js/modules/captchas/captcha-eu.d.ts.map +1 -1
- package/dist/js/modules/captchas/factories.d.ts +11 -11
- package/dist/js/modules/captchas/factories.d.ts.map +1 -1
- package/dist/js/modules/captchas/friendly-captcha-v1.d.ts +1 -1
- package/dist/js/modules/captchas/friendly-captcha-v1.d.ts.map +1 -1
- package/dist/js/modules/captchas/friendly-captcha-v2.d.ts +1 -1
- package/dist/js/modules/captchas/friendly-captcha-v2.d.ts.map +1 -1
- package/dist/js/modules/captchas/hcaptcha.d.ts +1 -1
- package/dist/js/modules/captchas/hcaptcha.d.ts.map +1 -1
- package/dist/js/modules/captchas/index.d.ts +2 -2
- package/dist/js/modules/captchas/index.d.ts.map +1 -1
- package/dist/js/modules/captchas/recaptcha-enterprise.d.ts +1 -1
- package/dist/js/modules/captchas/recaptcha-enterprise.d.ts.map +1 -1
- package/dist/js/modules/captchas/recaptcha-v2-checkbox.d.ts +1 -1
- package/dist/js/modules/captchas/recaptcha-v2-checkbox.d.ts.map +1 -1
- package/dist/js/modules/captchas/recaptcha-v2-invisible.d.ts +1 -1
- package/dist/js/modules/captchas/recaptcha-v2-invisible.d.ts.map +1 -1
- package/dist/js/modules/captchas/recaptcha-v3.d.ts +1 -1
- package/dist/js/modules/captchas/recaptcha-v3.d.ts.map +1 -1
- package/dist/js/modules/captchas/snaptcha.d.ts +1 -1
- package/dist/js/modules/captchas/snaptcha.d.ts.map +1 -1
- package/dist/js/modules/captchas/turnstile.d.ts +1 -1
- package/dist/js/modules/captchas/turnstile.d.ts.map +1 -1
- package/dist/js/modules/fields/address-country.d.ts +2 -2
- package/dist/js/modules/fields/address-country.d.ts.map +1 -1
- package/dist/js/modules/fields/address-state.d.ts +2 -2
- package/dist/js/modules/fields/address-state.d.ts.map +1 -1
- package/dist/js/modules/fields/calculations.d.ts +2 -2
- package/dist/js/modules/fields/calculations.d.ts.map +1 -1
- package/dist/js/modules/fields/checkbox-radio.d.ts +2 -2
- package/dist/js/modules/fields/checkbox-radio.d.ts.map +1 -1
- package/dist/js/modules/fields/combobox.d.ts +2 -2
- package/dist/js/modules/fields/combobox.d.ts.map +1 -1
- package/dist/js/modules/fields/conditions/config.d.ts.map +1 -1
- package/dist/js/modules/fields/conditions/effects.d.ts +1 -0
- package/dist/js/modules/fields/conditions/effects.d.ts.map +1 -1
- package/dist/js/modules/fields/conditions/evaluator.d.ts +4 -1
- package/dist/js/modules/fields/conditions/evaluator.d.ts.map +1 -1
- package/dist/js/modules/fields/conditions/references.d.ts.map +1 -1
- package/dist/js/modules/fields/conditions/submission-context.d.ts +15 -0
- package/dist/js/modules/fields/conditions/submission-context.d.ts.map +1 -0
- package/dist/js/modules/fields/conditions/types.d.ts +3 -1
- package/dist/js/modules/fields/conditions/types.d.ts.map +1 -1
- package/dist/js/modules/fields/conditions/values.d.ts +1 -1
- package/dist/js/modules/fields/conditions/values.d.ts.map +1 -1
- package/dist/js/modules/fields/conditions.d.ts +2 -2
- package/dist/js/modules/fields/conditions.d.ts.map +1 -1
- package/dist/js/modules/fields/custom-google-maps.d.ts +2 -2
- package/dist/js/modules/fields/custom-google-maps.d.ts.map +1 -1
- package/dist/js/modules/fields/custom-link.d.ts +2 -2
- package/dist/js/modules/fields/custom-link.d.ts.map +1 -1
- package/dist/js/modules/fields/custom-maps.d.ts +2 -2
- package/dist/js/modules/fields/custom-maps.d.ts.map +1 -1
- package/dist/js/modules/fields/date-picker.d.ts +2 -2
- package/dist/js/modules/fields/date-picker.d.ts.map +1 -1
- package/dist/js/modules/fields/file-upload.d.ts +2 -2
- package/dist/js/modules/fields/file-upload.d.ts.map +1 -1
- package/dist/js/modules/fields/hidden.d.ts +2 -2
- package/dist/js/modules/fields/hidden.d.ts.map +1 -1
- package/dist/js/modules/fields/index.d.ts +2 -2
- package/dist/js/modules/fields/index.d.ts.map +1 -1
- package/dist/js/modules/fields/password-validation.d.ts +2 -2
- package/dist/js/modules/fields/password-validation.d.ts.map +1 -1
- package/dist/js/modules/fields/phone-country.d.ts +2 -2
- package/dist/js/modules/fields/phone-country.d.ts.map +1 -1
- package/dist/js/modules/fields/repeater.d.ts +2 -2
- package/dist/js/modules/fields/repeater.d.ts.map +1 -1
- package/dist/js/modules/fields/rich-text.d.ts +2 -2
- package/dist/js/modules/fields/rich-text.d.ts.map +1 -1
- package/dist/js/modules/fields/shared.d.ts +1 -0
- package/dist/js/modules/fields/shared.d.ts.map +1 -1
- package/dist/js/modules/fields/signature.d.ts +2 -2
- package/dist/js/modules/fields/signature.d.ts.map +1 -1
- package/dist/js/modules/fields/summary.d.ts +2 -2
- package/dist/js/modules/fields/summary.d.ts.map +1 -1
- package/dist/js/modules/fields/survey-likert.d.ts +2 -2
- package/dist/js/modules/fields/survey-likert.d.ts.map +1 -1
- package/dist/js/modules/fields/survey-rank.d.ts +2 -2
- package/dist/js/modules/fields/survey-rank.d.ts.map +1 -1
- package/dist/js/modules/fields/survey-rating.d.ts +2 -2
- package/dist/js/modules/fields/survey-rating.d.ts.map +1 -1
- package/dist/js/modules/fields/table.d.ts +2 -2
- package/dist/js/modules/fields/table.d.ts.map +1 -1
- package/dist/js/modules/fields/text-limit.d.ts +2 -2
- package/dist/js/modules/fields/text-limit.d.ts.map +1 -1
- package/dist/js/modules/fields/upload-manager.d.ts +2 -2
- package/dist/js/modules/fields/upload-manager.d.ts.map +1 -1
- package/dist/js/modules/loader.d.ts +10 -4
- package/dist/js/modules/loader.d.ts.map +1 -1
- package/dist/js/modules/payments/bpoint.d.ts +1 -1
- package/dist/js/modules/payments/bpoint.d.ts.map +1 -1
- package/dist/js/modules/payments/eway.d.ts +1 -1
- package/dist/js/modules/payments/eway.d.ts.map +1 -1
- package/dist/js/modules/payments/factories.d.ts +7 -7
- package/dist/js/modules/payments/factories.d.ts.map +1 -1
- package/dist/js/modules/payments/go-cardless.d.ts +1 -1
- package/dist/js/modules/payments/go-cardless.d.ts.map +1 -1
- package/dist/js/modules/payments/index.d.ts +2 -2
- package/dist/js/modules/payments/index.d.ts.map +1 -1
- package/dist/js/modules/payments/mollie.d.ts +1 -1
- package/dist/js/modules/payments/mollie.d.ts.map +1 -1
- package/dist/js/modules/payments/moneris.d.ts +1 -1
- package/dist/js/modules/payments/moneris.d.ts.map +1 -1
- package/dist/js/modules/payments/opayo.d.ts +1 -1
- package/dist/js/modules/payments/opayo.d.ts.map +1 -1
- package/dist/js/modules/payments/paddle.d.ts +1 -1
- package/dist/js/modules/payments/paddle.d.ts.map +1 -1
- package/dist/js/modules/payments/paypal.d.ts +1 -1
- package/dist/js/modules/payments/paypal.d.ts.map +1 -1
- package/dist/js/modules/payments/payway.d.ts +1 -1
- package/dist/js/modules/payments/payway.d.ts.map +1 -1
- package/dist/js/modules/payments/square.d.ts +1 -1
- package/dist/js/modules/payments/square.d.ts.map +1 -1
- package/dist/js/modules/payments/stripe.d.ts +1 -1
- package/dist/js/modules/payments/stripe.d.ts.map +1 -1
- package/dist/js/modules/payments/utils.d.ts +6 -0
- package/dist/js/modules/payments/utils.d.ts.map +1 -1
- package/dist/js/modules/registry.d.ts +6 -4
- package/dist/js/modules/registry.d.ts.map +1 -1
- package/dist/js/submit/pipeline.d.ts.map +1 -1
- package/dist/js/theme/browser-theme-state.generated.d.ts +24 -0
- package/dist/js/theme/browser-theme-state.generated.d.ts.map +1 -0
- package/dist/js/theme/theme-classes.d.ts +3 -3
- package/dist/js/theme/theme-classes.d.ts.map +1 -1
- package/dist/js/transport/forms-api.d.ts +5 -3
- package/dist/js/transport/forms-api.d.ts.map +1 -1
- package/dist/js/utils/async.d.ts +1 -0
- package/dist/js/utils/async.d.ts.map +1 -1
- package/dist/js/utils/country-from-ip.d.ts +2 -2
- package/dist/js/utils/country-from-ip.d.ts.map +1 -1
- package/dist/js/utils/csrf.d.ts +17 -0
- package/dist/js/utils/csrf.d.ts.map +1 -0
- package/dist/js/utils/field-references.parser.d.ts +1 -0
- package/dist/js/utils/field-references.parser.d.ts.map +1 -1
- package/dist/js/utils/field-references.resolver.d.ts.map +1 -1
- package/dist/js/utils/field-references.types.d.ts +1 -0
- package/dist/js/utils/field-references.types.d.ts.map +1 -1
- package/dist/js/utils/http.d.ts +3 -1
- package/dist/js/utils/http.d.ts.map +1 -1
- package/dist/js/utils/request-profile.d.ts +4 -0
- package/dist/js/utils/request-profile.d.ts.map +1 -0
- package/dist/js/utils/unload-warning.d.ts.map +1 -1
- package/dist/js/validation/rules/email.d.ts.map +1 -1
- package/dist/js/validation/rules/match.d.ts.map +1 -1
- package/dist/js/validation/rules/number.d.ts.map +1 -1
- package/dist/js/validation/rules/required.d.ts.map +1 -1
- package/dist/js/validation/rules/shared.d.ts.map +1 -1
- package/dist/js/validation/rules/url.d.ts.map +1 -1
- package/dist/js/validation/skip.d.ts +7 -0
- package/dist/js/validation/skip.d.ts.map +1 -0
- package/dist/js/validation/submit-readiness.d.ts.map +1 -1
- package/dist/js/validation/validator.d.ts.map +1 -1
- package/package.json +43 -12
- package/src/css/formie-base.css +104 -1
- package/src/css/theme/_messages.css +2 -0
- package/src/css/theme/_tokens.css +31 -25
- package/src/css/theme/forms/_form.css +5 -1
- package/src/index.ts +13 -8
- package/src/js/contracts/client.ts +5 -3
- package/src/js/contracts/common.ts +5 -6
- package/src/js/contracts/modules.ts +41 -12
- package/src/js/contracts/schema.ts +21 -43
- package/src/js/contracts/theme.ts +6 -1
- package/src/js/core/client-rendered-modules.ts +69 -0
- package/src/js/core/create-formie-client.ts +132 -86
- package/src/js/core/field-error-aria.ts +93 -0
- package/src/js/core/hydrate-modules.ts +54 -29
- package/src/js/core/submit-result-state.ts +51 -45
- package/src/js/core/submit-result-ui.ts +25 -68
- package/src/js/core/validation-focus.ts +20 -20
- package/src/js/modules/address/address-finder.ts +1 -1
- package/src/js/modules/address/factories.ts +15 -13
- package/src/js/modules/address/google-address.ts +102 -40
- package/src/js/modules/address/index.ts +2 -2
- package/src/js/modules/address/loqate.ts +1 -1
- package/src/js/modules/address/place-kit.ts +14 -8
- package/src/js/modules/captchas/captcha-eu.ts +2 -2
- package/src/js/modules/captchas/factories.ts +66 -42
- package/src/js/modules/captchas/friendly-captcha-v1.ts +12 -2
- package/src/js/modules/captchas/friendly-captcha-v2.ts +4 -2
- package/src/js/modules/captchas/hcaptcha.ts +5 -5
- package/src/js/modules/captchas/index.ts +2 -2
- package/src/js/modules/captchas/recaptcha-enterprise.ts +2 -2
- package/src/js/modules/captchas/recaptcha-v2-checkbox.ts +3 -3
- package/src/js/modules/captchas/recaptcha-v2-invisible.ts +2 -2
- package/src/js/modules/captchas/recaptcha-v3.ts +2 -2
- package/src/js/modules/captchas/snaptcha.ts +1 -1
- package/src/js/modules/captchas/turnstile.ts +3 -3
- package/src/js/modules/fields/address-country.ts +11 -9
- package/src/js/modules/fields/address-state.ts +94 -35
- package/src/js/modules/fields/calculations.ts +5 -3
- package/src/js/modules/fields/checkbox-radio.ts +24 -42
- package/src/js/modules/fields/combobox.ts +28 -9
- package/src/js/modules/fields/conditions/config.ts +17 -20
- package/src/js/modules/fields/conditions/effects.ts +25 -29
- package/src/js/modules/fields/conditions/evaluator.ts +21 -29
- package/src/js/modules/fields/conditions/references.ts +16 -5
- package/src/js/modules/fields/conditions/submission-context.ts +80 -0
- package/src/js/modules/fields/conditions/types.ts +3 -1
- package/src/js/modules/fields/conditions/values.ts +31 -6
- package/src/js/modules/fields/conditions.ts +65 -18
- package/src/js/modules/fields/custom-google-maps.ts +5 -3
- package/src/js/modules/fields/custom-link.ts +5 -3
- package/src/js/modules/fields/custom-maps.ts +5 -3
- package/src/js/modules/fields/date-picker.ts +40 -7
- package/src/js/modules/fields/file-upload.ts +62 -23
- package/src/js/modules/fields/hidden.ts +5 -3
- package/src/js/modules/fields/index.ts +2 -2
- package/src/js/modules/fields/password-validation.ts +5 -3
- package/src/js/modules/fields/phone-country.ts +35 -8
- package/src/js/modules/fields/repeater.ts +60 -7
- package/src/js/modules/fields/rich-text.ts +20 -4
- package/src/js/modules/fields/shared.ts +13 -0
- package/src/js/modules/fields/signature.ts +24 -4
- package/src/js/modules/fields/summary.ts +8 -20
- package/src/js/modules/fields/survey-likert.ts +5 -3
- package/src/js/modules/fields/survey-rank.ts +21 -3
- package/src/js/modules/fields/survey-rating.ts +17 -3
- package/src/js/modules/fields/table.ts +5 -3
- package/src/js/modules/fields/text-limit.ts +5 -3
- package/src/js/modules/fields/upload-manager.ts +244 -67
- package/src/js/modules/loader.ts +345 -281
- package/src/js/modules/payments/bpoint.ts +2 -2
- package/src/js/modules/payments/eway.ts +2 -2
- package/src/js/modules/payments/factories.ts +53 -52
- package/src/js/modules/payments/go-cardless.ts +1 -1
- package/src/js/modules/payments/index.ts +2 -2
- package/src/js/modules/payments/mollie.ts +1 -1
- package/src/js/modules/payments/moneris.ts +2 -2
- package/src/js/modules/payments/opayo.ts +21 -8
- package/src/js/modules/payments/paddle.ts +10 -6
- package/src/js/modules/payments/payment.ts +22 -18
- package/src/js/modules/payments/paypal.ts +1 -1
- package/src/js/modules/payments/payway.ts +2 -2
- package/src/js/modules/payments/square.ts +2 -2
- package/src/js/modules/payments/stripe.ts +5 -5
- package/src/js/modules/payments/utils.ts +14 -0
- package/src/js/modules/registry.ts +19 -11
- package/src/js/submit/pipeline.ts +10 -11
- package/src/js/theme/browser-theme-state.generated.ts +25 -0
- package/src/js/theme/theme-classes.ts +3 -3
- package/src/js/transport/forms-api.ts +66 -67
- package/src/js/utils/async.ts +34 -0
- package/src/js/utils/country-from-ip.ts +14 -9
- package/src/js/utils/csrf.ts +136 -0
- package/src/js/utils/field-references.parser.ts +13 -113
- package/src/js/utils/field-references.resolver.ts +27 -64
- package/src/js/utils/field-references.row-scope.ts +1 -1
- package/src/js/utils/field-references.types.ts +1 -0
- package/src/js/utils/http.ts +5 -5
- package/src/js/utils/request-profile.ts +10 -0
- package/src/js/utils/unload-warning.ts +12 -4
- package/src/js/validation/rules/email.ts +3 -5
- package/src/js/validation/rules/match.ts +2 -1
- package/src/js/validation/rules/minmax.ts +4 -43
- package/src/js/validation/rules/number.ts +3 -47
- package/src/js/validation/rules/pattern.ts +3 -8
- package/src/js/validation/rules/required.ts +14 -1
- package/src/js/validation/rules/shared.ts +5 -1
- package/src/js/validation/rules/url.ts +3 -10
- package/src/js/validation/skip.ts +9 -0
- package/src/js/validation/submit-readiness.ts +9 -2
- package/src/js/validation/validator.ts +49 -61
- package/dist/chunks/address-state-BwE01m2_.js +0 -344
- package/dist/chunks/calculations-Dv3hYb8_.js +0 -299
- package/dist/chunks/conditions-CPH1snCx.js +0 -424
- package/dist/chunks/country-from-ip-BwxKaBuJ.js +0 -26
- package/dist/chunks/field-references.resolver-CHwn0G0L.js +0 -145
- package/dist/chunks/friendly-captcha-v1-C_2reOXr.js +0 -31
- package/dist/chunks/google-address-BH3Zhi06.js +0 -161
- package/dist/chunks/http-D-JExro7.js +0 -27
- package/dist/chunks/opayo-CR0DtsLa.js +0 -179
- package/dist/chunks/rich-text-Bg_--4TG.js +0 -349
- package/dist/js/modules/fields/conditions/transforms.d.ts +0 -3
- package/dist/js/modules/fields/conditions/transforms.d.ts.map +0 -1
- package/src/js/modules/fields/conditions/transforms.ts +0 -206
- package/src/js/modules/payments/stub-payment-module.ts +0 -87
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { setFormBrowserRequestOptions } from '#utils/request-profile';
|
|
2
|
+
import type { BrowserModuleKind, ClientFormInstance } from '@verbb/formie-core';
|
|
3
|
+
import { hydrateFormieModules } from './hydrate-modules';
|
|
4
|
+
import { ModuleRegistry, clientRenderedModuleRegistry } from '#modules/registry';
|
|
5
|
+
|
|
6
|
+
// These modules are implemented by the shared core state engine or native
|
|
7
|
+
// framework controls. Explicit delegation preserves every declared occurrence.
|
|
8
|
+
const nativeModuleDelegations: Record<string, { kind: BrowserModuleKind; reason: string }> = {
|
|
9
|
+
conditions: { kind: 'core', reason: 'Core evaluates structured conditions and the adapter renders visibility.' },
|
|
10
|
+
repeater: { kind: 'field', reason: 'The adapter owns row markup and core owns row values.' },
|
|
11
|
+
signature: { kind: 'field', reason: 'The adapter owns its signature control and cleanup.' },
|
|
12
|
+
'file-upload': { kind: 'field', reason: 'The shared transport stages selected files and submits attachment capabilities.' },
|
|
13
|
+
'upload-manager': { kind: 'field', reason: 'Native file selection uses the shared staged upload transport.' },
|
|
14
|
+
'checkbox-radio': { kind: 'field', reason: 'Framework controls own checked state.' },
|
|
15
|
+
'text-limit': { kind: 'field', reason: 'Core validation enforces the structured minimum and maximum rules.' },
|
|
16
|
+
'date-picker': { kind: 'field', reason: 'The adapter renders the structured date input contract.' },
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export async function mountClientRenderedModules(root: Element, instance: ClientFormInstance, registry = clientRenderedModuleRegistry) {
|
|
20
|
+
for (const [moduleName, delegation] of Object.entries(nativeModuleDelegations)) {
|
|
21
|
+
const moduleId = `formie:${moduleName}`;
|
|
22
|
+
if (registry.get(moduleId)) continue;
|
|
23
|
+
registry.register({
|
|
24
|
+
moduleId,
|
|
25
|
+
version: 2,
|
|
26
|
+
surfaces: ['client-rendered'],
|
|
27
|
+
kind: delegation.kind,
|
|
28
|
+
match: () => true,
|
|
29
|
+
setup: async(context) => {
|
|
30
|
+
await context.emit('formie:browser:module:delegated', { moduleId, reason: delegation.reason, target: context.target });
|
|
31
|
+
return { destroy: () => undefined };
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const form = root instanceof HTMLFormElement ? root : root.querySelector('form');
|
|
36
|
+
const syncRequest = () => {
|
|
37
|
+
if (!form) return;
|
|
38
|
+
const state = instance.getState();
|
|
39
|
+
setFormBrowserRequestOptions(form, instance.getBrowserRequestOptions());
|
|
40
|
+
form.action = state.definition.submission.endpoint;
|
|
41
|
+
if (state.session.tokens.csrf) form.setAttribute('data-formie-csrf-param', state.session.tokens.csrf.name);
|
|
42
|
+
for (const [name, value] of Object.entries({ handle: state.definition.handle, ...(state.session.tokens.csrf ? { [state.session.tokens.csrf.name]: state.session.tokens.csrf.value } : {}) })) {
|
|
43
|
+
let input = form.querySelector<HTMLInputElement>(`input[type="hidden"][name="${CSS.escape(name)}"]`);
|
|
44
|
+
if (!input) { input = document.createElement('input'); input.type = 'hidden'; input.name = name; form.append(input); }
|
|
45
|
+
input.value = value;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
syncRequest();
|
|
49
|
+
const unsubscribeRequest = instance.subscribe(syncRequest);
|
|
50
|
+
const host = await hydrateFormieModules({ root, modules: instance.getState().definition.modules, surface: 'client-rendered', registry });
|
|
51
|
+
instance.setBrowserModuleGuard(host.assertReady);
|
|
52
|
+
instance.setBrowserModulePreparation(host.prepare);
|
|
53
|
+
const unsubscribe = instance.on('formie:submit:result', (payload) => {
|
|
54
|
+
const result = payload as import('@verbb/formie-core').ClientSubmitResult;
|
|
55
|
+
const completion = result.completion;
|
|
56
|
+
if (result.success && completion) {
|
|
57
|
+
if (completion.behavior === 'redirect' && typeof completion.url === 'string') {
|
|
58
|
+
if (completion.target === 'new-tab') window.open(completion.url, '_blank', 'noopener,noreferrer');
|
|
59
|
+
else window.location.assign(completion.url);
|
|
60
|
+
} else if (completion.behavior === 'reload') {
|
|
61
|
+
window.location.reload();
|
|
62
|
+
} else if (completion.behavior === 'reset') {
|
|
63
|
+
instance.reset();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
void host.result({ ok: result.success, outcome: result.outcome, version: result.version, submissionUid: result.submissionUid, errors: result.errors, session: result.session, completion: result.completion, meta: result as unknown as Record<string, unknown> });
|
|
67
|
+
});
|
|
68
|
+
return { ...host, get instances() { return host.instances; }, get failures() { return host.failures; }, destroy: async() => { unsubscribe(); unsubscribeRequest(); await host.destroy(); } };
|
|
69
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { setFormBrowserRequestOptions } from '#utils/request-profile';
|
|
2
|
+
import { assertBrowserModuleManifest } from '@verbb/formie-core';
|
|
1
3
|
import type {
|
|
2
4
|
FormieClient,
|
|
3
5
|
FormieFormInstance,
|
|
@@ -7,8 +9,8 @@ import { bindLegacyDomEventCompatibility } from '#compatibility/dom-adapter';
|
|
|
7
9
|
import { resolveLegacyCompatibilityOptions } from '#compatibility/event-map';
|
|
8
10
|
import { bindLegacyValidatorCompatibility } from '#compatibility/validator-adapter';
|
|
9
11
|
import type { FormAction, FormMode, FormTransport } from '#contracts/common';
|
|
10
|
-
import type {
|
|
11
|
-
import type { FormEndpointPayload,
|
|
12
|
+
import type { BrowserModuleDefinition, BrowserModuleInstance } from '#contracts/modules';
|
|
13
|
+
import type { FormEndpointPayload, BrowserModuleEntry, FormSubmitResult } from '#contracts/schema';
|
|
12
14
|
import type { ThemeClassMap } from '#contracts/theme';
|
|
13
15
|
import { dispatchFormieDomEvent } from '#core/dom-events';
|
|
14
16
|
import { getErrorAriaLivePreference } from '#core/error-aria-live';
|
|
@@ -20,16 +22,17 @@ import {
|
|
|
20
22
|
hasServerRenderedValidationErrors,
|
|
21
23
|
} from '#core/validation-focus';
|
|
22
24
|
import { clearSubmitFeedback, executeAjaxSubmitFlow, shouldKeepSubmitLoading } from '#core/submit-flow';
|
|
23
|
-
import { applySubmitResultUi } from '#core/submit-result-ui';
|
|
25
|
+
import { applySubmitResultUi, renderFieldErrors, renderFormErrors } from '#core/submit-result-ui';
|
|
24
26
|
import { applyPageState, clearSubmitLoading, setSubmitLoading } from '#core/submit-result-state';
|
|
25
27
|
import { EventBus } from '#events/event-bus';
|
|
26
28
|
import { ModuleRegistry } from '#modules/registry';
|
|
27
|
-
import { loadModulesFromManifest } from '#modules/loader';
|
|
29
|
+
import { loadModulesFromManifest, type BrowserModuleRuntime } from '#modules/loader';
|
|
28
30
|
import { registerThemeClassMap } from '#theme/theme-classes';
|
|
29
31
|
import { runSubmitPipeline } from '#submit/pipeline';
|
|
30
32
|
import { clearSubmissionOnUnload, requestGraphqlRender, requestRefreshTokens, requestRender, requestSetPage } from '#transport/forms-api';
|
|
31
33
|
import { ensureFormStartedAt } from '#utils/form-started-at';
|
|
32
34
|
import { createDebug } from '#utils/debug';
|
|
35
|
+
import { waitForElementRemoval } from '#utils/async';
|
|
33
36
|
import { createFormUnloadWarningGuard } from '#utils/unload-warning';
|
|
34
37
|
import { getValidationScope } from '#validation/scope';
|
|
35
38
|
import { FormieValidator } from '#validation/validator';
|
|
@@ -41,7 +44,7 @@ type InternalInstanceState = {
|
|
|
41
44
|
bus: EventBus;
|
|
42
45
|
form: HTMLFormElement | null;
|
|
43
46
|
validator: FormieValidator | null;
|
|
44
|
-
modules:
|
|
47
|
+
modules: BrowserModuleRuntime;
|
|
45
48
|
unbinds: Array<() => void>;
|
|
46
49
|
instance: FormieFormInstance;
|
|
47
50
|
};
|
|
@@ -73,12 +76,12 @@ function parseBooleanOption(value: string | undefined, defaultValue: boolean): b
|
|
|
73
76
|
}
|
|
74
77
|
|
|
75
78
|
function inferStaticCacheOnLoadFromDataset(dataset: DOMStringMap): boolean {
|
|
76
|
-
if (dataset.formieRefreshTokens != null
|
|
77
|
-
return parseBooleanOption(dataset.formieRefreshTokens,
|
|
79
|
+
if (dataset.formieRefreshTokens != null) {
|
|
80
|
+
return parseBooleanOption(dataset.formieRefreshTokens, true);
|
|
78
81
|
}
|
|
79
82
|
|
|
80
|
-
if (dataset.formieStaticCache != null
|
|
81
|
-
return parseBooleanOption(dataset.formieStaticCache,
|
|
83
|
+
if (dataset.formieStaticCache != null) {
|
|
84
|
+
return parseBooleanOption(dataset.formieStaticCache, true);
|
|
82
85
|
}
|
|
83
86
|
|
|
84
87
|
return false;
|
|
@@ -90,6 +93,7 @@ function inferOptionsFromElement(target: Element): FormMountOptions {
|
|
|
90
93
|
return {
|
|
91
94
|
mode: 'server-rendered',
|
|
92
95
|
transport: (dataset.formieTransport as FormTransport) || 'rest',
|
|
96
|
+
profile: dataset.formieRequestProfile as FormMountOptions['profile'],
|
|
93
97
|
formHandle: dataset.formieHandle,
|
|
94
98
|
endpoint: dataset.formieEndpoint,
|
|
95
99
|
staticCache: inferStaticCacheOnLoadFromDataset(dataset),
|
|
@@ -99,7 +103,10 @@ function inferOptionsFromElement(target: Element): FormMountOptions {
|
|
|
99
103
|
}
|
|
100
104
|
|
|
101
105
|
function normalizeMode(mode: FormMountOptions['mode'] | undefined): FormMode {
|
|
102
|
-
|
|
106
|
+
if (mode && mode !== 'server-rendered') {
|
|
107
|
+
throw new Error('@verbb/formie-browser enhances server-rendered HTML only. Use @verbb/formie-core for client-rendered forms.');
|
|
108
|
+
}
|
|
109
|
+
return 'server-rendered';
|
|
103
110
|
}
|
|
104
111
|
|
|
105
112
|
function normalizeTransport(transport: FormMountOptions['transport'] | undefined): FormTransport {
|
|
@@ -153,7 +160,7 @@ function resolveHeadlessEndpoint(baseOrEndpoint: string | undefined, actionPath:
|
|
|
153
160
|
return actionPath;
|
|
154
161
|
}
|
|
155
162
|
|
|
156
|
-
if (candidate.includes(
|
|
163
|
+
if (candidate.includes(actionPath)) {
|
|
157
164
|
return candidate;
|
|
158
165
|
}
|
|
159
166
|
|
|
@@ -206,7 +213,7 @@ function mergeSearchParams(sourceUrl: string | null, destinationUrl: string): st
|
|
|
206
213
|
}
|
|
207
214
|
}
|
|
208
215
|
|
|
209
|
-
function
|
|
216
|
+
function normalizeServerRenderedUrls(target: Element, form: HTMLFormElement, options: FormMountOptions): void {
|
|
210
217
|
const baseEndpoint = options.endpoint || (target as HTMLElement).dataset.formieEndpoint;
|
|
211
218
|
const submitAction = resolveHeadlessEndpoint(baseEndpoint, DEFAULT_HEADLESS_SUBMIT_ACTION);
|
|
212
219
|
const existingAction = form.getAttribute('action');
|
|
@@ -228,11 +235,7 @@ function normalizeHeadlessManagedUrls(target: Element, form: HTMLFormElement, op
|
|
|
228
235
|
});
|
|
229
236
|
}
|
|
230
237
|
|
|
231
|
-
|
|
232
|
-
if (transport === 'graphql' && mode !== 'server-rendered') {
|
|
233
|
-
throw new Error(`Formie ${mode} mode does not support GraphQL transport yet.`);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
238
|
+
|
|
236
239
|
|
|
237
240
|
function parseBooleanDatasetValue(value: string | undefined): boolean {
|
|
238
241
|
if (value == null) {
|
|
@@ -339,6 +342,7 @@ function parseJsonAttribute<T>(element: Element | null, attributeName: string):
|
|
|
339
342
|
try {
|
|
340
343
|
return JSON.parse(rawValue) as T;
|
|
341
344
|
} catch (error) {
|
|
345
|
+
if (attributeName === 'data-formie-modules') throw new Error('Invalid browser-module manifest JSON. Update Formie and its browser packages together.');
|
|
342
346
|
console.error(`[formie] Failed to parse ${attributeName}.`, error);
|
|
343
347
|
return null;
|
|
344
348
|
}
|
|
@@ -354,7 +358,8 @@ function getEmbeddedPayload(target: Element, form: HTMLFormElement | null): Form
|
|
|
354
358
|
// Server-rendered forms carry the same minimal payload shape that endpoint-
|
|
355
359
|
// rendered forms receive, which keeps mount behavior identical in both flows.
|
|
356
360
|
const modules = parseJsonAttribute<FormEndpointPayload['modules']>(payloadRoot, 'data-formie-modules');
|
|
357
|
-
const theme = parseJsonAttribute<FormEndpointPayload['theme']>(payloadRoot, 'data-formie-theme')
|
|
361
|
+
const theme = parseJsonAttribute<FormEndpointPayload['theme']>(payloadRoot, 'data-formie-theme-classes')
|
|
362
|
+
|| parseJsonAttribute<FormEndpointPayload['theme']>(payloadRoot, 'data-formie-theme');
|
|
358
363
|
|
|
359
364
|
if (!modules && !theme) {
|
|
360
365
|
return null;
|
|
@@ -425,6 +430,8 @@ function applyRefreshTokensToForm(form: HTMLFormElement, refreshTokens: FormEndp
|
|
|
425
430
|
csrfInput.name = refreshTokens.csrf.param;
|
|
426
431
|
csrfInput.value = refreshTokens.csrf.token;
|
|
427
432
|
csrfInput.setAttribute('autocomplete', 'off');
|
|
433
|
+
// Match server-rendered CSRF inputs so helpers can resolve custom token names.
|
|
434
|
+
csrfInput.setAttribute('data-formie-csrf', '');
|
|
428
435
|
form.prepend(csrfInput);
|
|
429
436
|
}
|
|
430
437
|
}
|
|
@@ -443,6 +450,19 @@ function applyRefreshTokensToForm(form: HTMLFormElement, refreshTokens: FormEndp
|
|
|
443
450
|
}
|
|
444
451
|
}
|
|
445
452
|
|
|
453
|
+
if (refreshTokens.uploadCreateToken) {
|
|
454
|
+
let uploadCreateInput = form.querySelector('input[name="uploadCreateToken"]') as HTMLInputElement | null;
|
|
455
|
+
|
|
456
|
+
if (!uploadCreateInput) {
|
|
457
|
+
uploadCreateInput = document.createElement('input');
|
|
458
|
+
uploadCreateInput.type = 'hidden';
|
|
459
|
+
uploadCreateInput.name = 'uploadCreateToken';
|
|
460
|
+
form.append(uploadCreateInput);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
uploadCreateInput.value = refreshTokens.uploadCreateToken;
|
|
464
|
+
}
|
|
465
|
+
|
|
446
466
|
if (refreshTokens.captchas && typeof refreshTokens.captchas === 'object') {
|
|
447
467
|
Object.values(refreshTokens.captchas).forEach((captchaEntry) => {
|
|
448
468
|
if (!captchaEntry || typeof captchaEntry !== 'object') {
|
|
@@ -478,7 +498,6 @@ async function ensureHtmlRender(target: Element, options: FormMountOptions): Pro
|
|
|
478
498
|
return options.payload;
|
|
479
499
|
}
|
|
480
500
|
|
|
481
|
-
ensureSupportedHeadlessTransport(transport, mode);
|
|
482
501
|
|
|
483
502
|
const hasForm = !!getFormFromTarget(target);
|
|
484
503
|
const formHandle = options.formHandle || (target as HTMLElement).dataset.formieHandle;
|
|
@@ -501,11 +520,11 @@ async function ensureHtmlRender(target: Element, options: FormMountOptions): Pro
|
|
|
501
520
|
? resolveGraphqlEndpoint(options, target)
|
|
502
521
|
: resolveHtmlRenderEndpoint(options, target);
|
|
503
522
|
const payload = transport === 'graphql'
|
|
504
|
-
? await requestGraphqlRender(endpoint, formHandle, renderOptions)
|
|
523
|
+
? await requestGraphqlRender(endpoint, formHandle, renderOptions, options)
|
|
505
524
|
: await requestRender(endpoint, formHandle, {
|
|
506
525
|
...renderOptions,
|
|
507
526
|
endpoint,
|
|
508
|
-
});
|
|
527
|
+
}, options);
|
|
509
528
|
|
|
510
529
|
if (payload?.html) {
|
|
511
530
|
(target as HTMLElement).innerHTML = payload.html;
|
|
@@ -519,7 +538,6 @@ async function refreshTokensAfterSubmitIfNeeded(target: Element, options: FormMo
|
|
|
519
538
|
return;
|
|
520
539
|
}
|
|
521
540
|
|
|
522
|
-
ensureSupportedHeadlessTransport(normalizeTransport(options.transport), normalizeMode(options.mode));
|
|
523
541
|
|
|
524
542
|
const formHandle = options.formHandle || (target as HTMLElement).dataset.formieHandle;
|
|
525
543
|
if (!formHandle) {
|
|
@@ -531,7 +549,7 @@ async function refreshTokensAfterSubmitIfNeeded(target: Element, options: FormMo
|
|
|
531
549
|
const endpoint = resolveRefreshTokensEndpoint(options, target);
|
|
532
550
|
const renderIdInput = form.querySelector('input[name="renderId"]') as HTMLInputElement | null;
|
|
533
551
|
const renderId = renderIdInput?.value || undefined;
|
|
534
|
-
const refreshTokens = await requestRefreshTokens(endpoint, formHandle, renderId);
|
|
552
|
+
const refreshTokens = await requestRefreshTokens(endpoint, formHandle, renderId, options, form?.querySelector<HTMLInputElement>('input[name="requestToken"]')?.value);
|
|
535
553
|
applyRefreshTokensToForm(form, refreshTokens);
|
|
536
554
|
dispatchFormieDomEvent(target, 'formie:refresh-tokens:refreshed', refreshTokens);
|
|
537
555
|
}
|
|
@@ -543,7 +561,10 @@ function bindFormEvents(
|
|
|
543
561
|
bus: EventBus,
|
|
544
562
|
validator: FormieValidator | null,
|
|
545
563
|
unbinds: Array<() => void>,
|
|
546
|
-
): void {
|
|
564
|
+
): () => void {
|
|
565
|
+
let captureBaseline = () => {};
|
|
566
|
+
form.dataset.formieRequestProfile = options.profile ?? 'same-origin-browser';
|
|
567
|
+
if (options.profile === 'cross-origin-public') form.dataset.formieSubmitMethod = 'ajax';
|
|
547
568
|
const submitMethod = String(
|
|
548
569
|
form.dataset.formieSubmitMethod || '',
|
|
549
570
|
).trim().toLowerCase();
|
|
@@ -567,6 +588,7 @@ function bindFormEvents(
|
|
|
567
588
|
return !hasInternalNavigation(form);
|
|
568
589
|
},
|
|
569
590
|
});
|
|
591
|
+
captureBaseline = unloadWarning.scheduleBaselineCapture;
|
|
570
592
|
const handleSubmitResult = (event: Event): void => {
|
|
571
593
|
if (!(event instanceof CustomEvent)) {
|
|
572
594
|
return;
|
|
@@ -578,7 +600,7 @@ function bindFormEvents(
|
|
|
578
600
|
return;
|
|
579
601
|
}
|
|
580
602
|
|
|
581
|
-
if (result.action === 'save') {
|
|
603
|
+
if (result.action === 'save' || result.outcome === 'revised') {
|
|
582
604
|
unloadWarning.scheduleBaselineCapture();
|
|
583
605
|
}
|
|
584
606
|
};
|
|
@@ -625,8 +647,7 @@ function bindFormEvents(
|
|
|
625
647
|
return;
|
|
626
648
|
}
|
|
627
649
|
|
|
628
|
-
//
|
|
629
|
-
// continuity in the background so refresh/recovery stays in sync.
|
|
650
|
+
// The server validates forward navigation before selecting the page.
|
|
630
651
|
event.preventDefault();
|
|
631
652
|
|
|
632
653
|
const currentTarget = event.currentTarget as HTMLAnchorElement | null;
|
|
@@ -637,7 +658,6 @@ function bindFormEvents(
|
|
|
637
658
|
return;
|
|
638
659
|
}
|
|
639
660
|
|
|
640
|
-
applyPageState(form, nextPageId);
|
|
641
661
|
dispatchFormieDomEvent(target, 'formie:page:navigate', {
|
|
642
662
|
pageId: nextPageId,
|
|
643
663
|
href,
|
|
@@ -645,6 +665,15 @@ function bindFormEvents(
|
|
|
645
665
|
|
|
646
666
|
try {
|
|
647
667
|
const response = await requestSetPage(href, form, nextPageId);
|
|
668
|
+
if (response.pageId) applyPageState(form, String(response.pageId));
|
|
669
|
+
if (!response.success) {
|
|
670
|
+
const { form: formErrors = [], ...fieldErrors } = response.errors ?? {};
|
|
671
|
+
clearSubmitFeedback(form);
|
|
672
|
+
renderFieldErrors(form, fieldErrors);
|
|
673
|
+
renderFormErrors(form, formErrors);
|
|
674
|
+
focusFirstValidationError(form);
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
648
677
|
|
|
649
678
|
dispatchFormieDomEvent(target, 'formie:page:navigate:after', {
|
|
650
679
|
pageId: nextPageId,
|
|
@@ -829,6 +858,8 @@ function bindFormEvents(
|
|
|
829
858
|
unbinds.push(() => {
|
|
830
859
|
form.removeEventListener('submit', submitHandler);
|
|
831
860
|
});
|
|
861
|
+
|
|
862
|
+
return captureBaseline;
|
|
832
863
|
}
|
|
833
864
|
|
|
834
865
|
async function refreshTokensIfNeeded(target: Element, options: FormMountOptions, form: HTMLFormElement | null): Promise<void> {
|
|
@@ -840,7 +871,6 @@ async function refreshTokensIfNeeded(target: Element, options: FormMountOptions,
|
|
|
840
871
|
return;
|
|
841
872
|
}
|
|
842
873
|
|
|
843
|
-
ensureSupportedHeadlessTransport(normalizeTransport(options.transport), normalizeMode(options.mode));
|
|
844
874
|
|
|
845
875
|
const formHandle = options.formHandle || (target as HTMLElement).dataset.formieHandle;
|
|
846
876
|
const endpoint = resolveRefreshTokensEndpoint(options, target);
|
|
@@ -853,7 +883,7 @@ async function refreshTokensIfNeeded(target: Element, options: FormMountOptions,
|
|
|
853
883
|
|
|
854
884
|
// Refresh-tokens-before-ready is the cache-safe path: forms can render from SSR/cache
|
|
855
885
|
// and still receive fresh transport tokens before the user submits anything.
|
|
856
|
-
const refreshTokens = await requestRefreshTokens(endpoint, formHandle, renderId);
|
|
886
|
+
const refreshTokens = await requestRefreshTokens(endpoint, formHandle, renderId, options, form?.querySelector<HTMLInputElement>('input[name="requestToken"]')?.value);
|
|
857
887
|
|
|
858
888
|
if (!refreshTokens || !form) {
|
|
859
889
|
return;
|
|
@@ -869,14 +899,14 @@ export function createFormieClient(): FormieClient {
|
|
|
869
899
|
const moduleRegistry = new ModuleRegistry();
|
|
870
900
|
const pendingVisibilityMounts = new Map<Element, () => void>();
|
|
871
901
|
const pendingUnmounts = new Map<Element, Promise<void>>();
|
|
872
|
-
const stageNames: Array<'prepare' | 'normalize' | 'validate' | '
|
|
902
|
+
const stageNames: Array<'prepare' | 'normalize' | 'validate' | 'challenge' | 'payment' | 'send' | 'result'> = [
|
|
873
903
|
'prepare',
|
|
874
904
|
'normalize',
|
|
875
905
|
'validate',
|
|
876
|
-
'
|
|
877
|
-
'
|
|
878
|
-
'
|
|
879
|
-
'
|
|
906
|
+
'challenge',
|
|
907
|
+
'payment',
|
|
908
|
+
'send',
|
|
909
|
+
'result',
|
|
880
910
|
];
|
|
881
911
|
|
|
882
912
|
const unmount = async(target: Element): Promise<void> => {
|
|
@@ -913,10 +943,7 @@ export function createFormieClient(): FormieClient {
|
|
|
913
943
|
state.validator?.destroy();
|
|
914
944
|
state.validator = null;
|
|
915
945
|
|
|
916
|
-
|
|
917
|
-
await moduleInstance.destroy();
|
|
918
|
-
}
|
|
919
|
-
state.modules = [];
|
|
946
|
+
await state.modules.destroy();
|
|
920
947
|
|
|
921
948
|
state.bus.clear();
|
|
922
949
|
instances.delete(target);
|
|
@@ -965,20 +992,37 @@ export function createFormieClient(): FormieClient {
|
|
|
965
992
|
mode: normalizeMode(options.mode ?? mergedFromDom.mode),
|
|
966
993
|
transport: normalizeTransport(options.transport ?? mergedFromDom.transport),
|
|
967
994
|
};
|
|
968
|
-
const
|
|
995
|
+
const renderPayload = await ensureHtmlRender(target, normalizedOptions);
|
|
996
|
+
const form = getFormFromTarget(target);
|
|
969
997
|
|
|
970
|
-
|
|
971
|
-
|
|
998
|
+
// Fetched HTML carries the plugin setting on the form, not its mount wrapper.
|
|
999
|
+
if (options.compatibility === undefined && form?.hasAttribute('data-formie-compatibility')) {
|
|
1000
|
+
normalizedOptions.compatibility = inferOptionsFromElement(form).compatibility;
|
|
972
1001
|
}
|
|
973
1002
|
|
|
974
|
-
const
|
|
975
|
-
|
|
1003
|
+
const compatibilityOptions = resolveLegacyCompatibilityOptions(normalizedOptions.compatibility);
|
|
1004
|
+
if (form) setFormBrowserRequestOptions(form, normalizedOptions);
|
|
976
1005
|
normalizedOptions.staticCache =
|
|
977
1006
|
options.staticCache ??
|
|
978
1007
|
(form
|
|
979
1008
|
? inferStaticCacheOnLoadFromDataset(form.dataset)
|
|
980
1009
|
: inferStaticCacheOnLoadFromDataset((target as HTMLElement).dataset));
|
|
981
|
-
|
|
1010
|
+
let embeddedPayload: FormEndpointPayload | null;
|
|
1011
|
+
try {
|
|
1012
|
+
embeddedPayload = getEmbeddedPayload(target, form);
|
|
1013
|
+
if (renderPayload?.modules) assertBrowserModuleManifest(renderPayload.modules);
|
|
1014
|
+
if (embeddedPayload?.modules) assertBrowserModuleManifest(embeddedPayload.modules);
|
|
1015
|
+
} catch (error) {
|
|
1016
|
+
// Unknown semantics must not fall back to an unguarded native send.
|
|
1017
|
+
if (form) {
|
|
1018
|
+
form.addEventListener('submit', (event) => { event.preventDefault(); event.stopImmediatePropagation(); }, true);
|
|
1019
|
+
const alert = document.createElement('div');
|
|
1020
|
+
alert.setAttribute('role', 'alert');
|
|
1021
|
+
alert.textContent = 'This form requires a compatible Formie browser package. Update Formie and its browser packages together.';
|
|
1022
|
+
form.prepend(alert);
|
|
1023
|
+
}
|
|
1024
|
+
throw error;
|
|
1025
|
+
}
|
|
982
1026
|
const payload = renderPayload || embeddedPayload
|
|
983
1027
|
? {
|
|
984
1028
|
...(renderPayload || {}),
|
|
@@ -987,14 +1031,13 @@ export function createFormieClient(): FormieClient {
|
|
|
987
1031
|
: null;
|
|
988
1032
|
const themeClassMap = payload?.theme as ThemeClassMap | undefined;
|
|
989
1033
|
const stateStore: Record<string, unknown> = {};
|
|
990
|
-
const moduleManifest =
|
|
991
|
-
|
|
992
|
-
});
|
|
1034
|
+
const moduleManifest = payload?.modules ?? { contractVersion: 2, surface: 'server-rendered' as const, entries: [] };
|
|
1035
|
+
assertBrowserModuleManifest(moduleManifest);
|
|
993
1036
|
debug.log('Resolved mount payload.', {
|
|
994
1037
|
target: getTargetDebugLabel(target),
|
|
995
1038
|
hasRenderPayload: !!renderPayload,
|
|
996
1039
|
hasEmbeddedPayload: !!embeddedPayload,
|
|
997
|
-
moduleCount: moduleManifest.length,
|
|
1040
|
+
moduleCount: moduleManifest.entries.length,
|
|
998
1041
|
});
|
|
999
1042
|
const resolvedThemeClassMap = registerThemeClassMap(target, themeClassMap, form);
|
|
1000
1043
|
|
|
@@ -1028,16 +1071,8 @@ export function createFormieClient(): FormieClient {
|
|
|
1028
1071
|
if (form) {
|
|
1029
1072
|
ensureFormStartedAt(form);
|
|
1030
1073
|
|
|
1031
|
-
if (normalizedOptions.themeConfig && typeof normalizedOptions.themeConfig === 'object') {
|
|
1032
|
-
form.setAttribute('data-formie-theme-config', JSON.stringify(normalizedOptions.themeConfig));
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
if (normalizedOptions.theme && normalizedOptions.theme !== 'formie') {
|
|
1036
|
-
form.setAttribute('data-formie-frontend-theme', normalizedOptions.theme);
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
1074
|
if (renderPayload || normalizedOptions.endpoint || (target as HTMLElement).dataset.formieEndpoint) {
|
|
1040
|
-
|
|
1075
|
+
normalizeServerRenderedUrls(target, form, normalizedOptions);
|
|
1041
1076
|
}
|
|
1042
1077
|
|
|
1043
1078
|
if (normalizedOptions.mode === 'server-rendered' && hasServerRenderedValidationErrors(form)) {
|
|
@@ -1054,6 +1089,13 @@ export function createFormieClient(): FormieClient {
|
|
|
1054
1089
|
});
|
|
1055
1090
|
}
|
|
1056
1091
|
|
|
1092
|
+
// Craft's async CSRF element initializes the session. Let it finish before
|
|
1093
|
+
// captcha modules request session-bound tokens, otherwise the concurrent
|
|
1094
|
+
// responses can race and replace each other's session cookie.
|
|
1095
|
+
if (form?.querySelector('craft-csrf-input')) {
|
|
1096
|
+
await waitForElementRemoval('craft-csrf-input', form);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1057
1099
|
// Modules run against the mounted DOM surface, not against server payload
|
|
1058
1100
|
// objects, so the loader resolves real targets before calling setup().
|
|
1059
1101
|
const modules = await loadModulesFromManifest(moduleManifest, {
|
|
@@ -1061,7 +1103,7 @@ export function createFormieClient(): FormieClient {
|
|
|
1061
1103
|
matchContext: {
|
|
1062
1104
|
root: target,
|
|
1063
1105
|
form,
|
|
1064
|
-
|
|
1106
|
+
surface: moduleManifest.surface,
|
|
1065
1107
|
},
|
|
1066
1108
|
setupContext: {
|
|
1067
1109
|
formId: id,
|
|
@@ -1069,6 +1111,7 @@ export function createFormieClient(): FormieClient {
|
|
|
1069
1111
|
form,
|
|
1070
1112
|
target,
|
|
1071
1113
|
scope: 'form',
|
|
1114
|
+
surface: moduleManifest.surface,
|
|
1072
1115
|
state: stateStore,
|
|
1073
1116
|
on: (eventName, callback) => {
|
|
1074
1117
|
return bus.on(eventName, callback);
|
|
@@ -1088,7 +1131,7 @@ export function createFormieClient(): FormieClient {
|
|
|
1088
1131
|
});
|
|
1089
1132
|
debug.log('Module setup complete.', {
|
|
1090
1133
|
target: getTargetDebugLabel(target),
|
|
1091
|
-
moduleInstances: modules.length,
|
|
1134
|
+
moduleInstances: modules.instances.length,
|
|
1092
1135
|
});
|
|
1093
1136
|
|
|
1094
1137
|
const instance: FormieFormInstance = {
|
|
@@ -1184,7 +1227,7 @@ export function createFormieClient(): FormieClient {
|
|
|
1184
1227
|
}
|
|
1185
1228
|
|
|
1186
1229
|
if (form) {
|
|
1187
|
-
bindFormEvents(target, form, normalizedOptions, bus, validator, unbinds);
|
|
1230
|
+
const captureBaseline = bindFormEvents(target, form, normalizedOptions, bus, validator, unbinds);
|
|
1188
1231
|
|
|
1189
1232
|
if (validator) {
|
|
1190
1233
|
unbinds.push(bindSubmitReadiness(form, validator, target));
|
|
@@ -1195,23 +1238,28 @@ export function createFormieClient(): FormieClient {
|
|
|
1195
1238
|
|
|
1196
1239
|
// Recapture after captcha/token hydration and field-module init settle so
|
|
1197
1240
|
// programmatic post-mount value changes do not leave a false dirty state.
|
|
1198
|
-
|
|
1199
|
-
window.setTimeout(
|
|
1200
|
-
|
|
1201
|
-
}, 350);
|
|
1241
|
+
captureBaseline();
|
|
1242
|
+
const baselineTimer = window.setTimeout(captureBaseline, 350);
|
|
1243
|
+
unbinds.push(() => window.clearTimeout(baselineTimer));
|
|
1202
1244
|
}
|
|
1203
1245
|
|
|
1204
1246
|
stageNames.forEach((stageName) => {
|
|
1205
|
-
// Stage fan-out keeps submit pipeline ownership centralized while still
|
|
1206
|
-
// letting modules participate before and after each stage.
|
|
1207
1247
|
const beforeDomUnbind = bus.on(`formie:stage:${stageName}:before`, async(payload) => {
|
|
1208
1248
|
dispatchFormieDomEvent(target, `formie:stage:${stageName}:before`, payload);
|
|
1209
1249
|
});
|
|
1210
1250
|
|
|
1211
1251
|
const beforeUnbind = bus.on(`formie:stage:${stageName}:before`, async(payload) => {
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1252
|
+
const hookStage = { core: 'prepare', field: 'prepare', address: 'prepare', captcha: 'challenge', payment: 'payment' } as const;
|
|
1253
|
+
const stagePayload = payload as Parameters<NonNullable<BrowserModuleInstance['beforeSubmit']>>[0] & { stage: string };
|
|
1254
|
+
|
|
1255
|
+
if (stageName === 'prepare') {
|
|
1256
|
+
try { modules.assertReady(); }
|
|
1257
|
+
catch { stagePayload.abort('A required form feature could not start. Reload the page or contact the site administrator.'); return; }
|
|
1258
|
+
}
|
|
1259
|
+
for (const moduleInstance of modules.instances) {
|
|
1260
|
+
if (moduleInstance.beforeSubmit && hookStage[moduleInstance.kind ?? 'core'] === stageName) {
|
|
1261
|
+
const { form, action, formData, abort, isAborted, abortReason } = stagePayload;
|
|
1262
|
+
await moduleInstance.beforeSubmit({ form, action, formData, abort, isAborted, abortReason });
|
|
1215
1263
|
}
|
|
1216
1264
|
}
|
|
1217
1265
|
});
|
|
@@ -1220,19 +1268,7 @@ export function createFormieClient(): FormieClient {
|
|
|
1220
1268
|
dispatchFormieDomEvent(target, `formie:stage:${stageName}:after`, payload);
|
|
1221
1269
|
});
|
|
1222
1270
|
|
|
1223
|
-
|
|
1224
|
-
const stagePayload = payload as {
|
|
1225
|
-
result?: FormSubmitResult;
|
|
1226
|
-
} & Parameters<NonNullable<FormieModuleInstance['onAfterStage']>>[0];
|
|
1227
|
-
|
|
1228
|
-
for (const moduleInstance of modules) {
|
|
1229
|
-
if (moduleInstance.onAfterStage) {
|
|
1230
|
-
await moduleInstance.onAfterStage(stagePayload, stagePayload.result);
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
});
|
|
1234
|
-
|
|
1235
|
-
unbinds.push(beforeDomUnbind, beforeUnbind, afterDomUnbind, afterUnbind);
|
|
1271
|
+
unbinds.push(beforeDomUnbind, beforeUnbind, afterDomUnbind);
|
|
1236
1272
|
});
|
|
1237
1273
|
|
|
1238
1274
|
const submitBeforeUnbind = bus.on('formie:submit:before', async(payload) => {
|
|
@@ -1241,6 +1277,16 @@ export function createFormieClient(): FormieClient {
|
|
|
1241
1277
|
|
|
1242
1278
|
const submitAfterUnbind = bus.on('formie:submit:after', async(payload) => {
|
|
1243
1279
|
dispatchFormieDomEvent(target, 'formie:submit:after', payload);
|
|
1280
|
+
|
|
1281
|
+
if (!form) return;
|
|
1282
|
+
const result = payload as FormSubmitResult;
|
|
1283
|
+
if (result.code === 'PREFLIGHT_COMPLETE') return;
|
|
1284
|
+
const context = {
|
|
1285
|
+
form,
|
|
1286
|
+
action: result.action ?? 'submit' as const,
|
|
1287
|
+
formData: new FormData(form),
|
|
1288
|
+
};
|
|
1289
|
+
for (const moduleInstance of modules.instances) await moduleInstance.afterSubmit?.(context, result);
|
|
1244
1290
|
});
|
|
1245
1291
|
|
|
1246
1292
|
const submitFinalBeforeUnbind = bus.on('formie:submit:final:before', async(payload) => {
|
|
@@ -1435,7 +1481,7 @@ export function createFormieClient(): FormieClient {
|
|
|
1435
1481
|
return;
|
|
1436
1482
|
}
|
|
1437
1483
|
|
|
1438
|
-
const refreshTokens = await requestRefreshTokens(endpoint, formHandle, renderId);
|
|
1484
|
+
const refreshTokens = await requestRefreshTokens(endpoint, formHandle, renderId, options, form?.querySelector<HTMLInputElement>('input[name="requestToken"]')?.value);
|
|
1439
1485
|
|
|
1440
1486
|
if (!refreshTokens) {
|
|
1441
1487
|
return;
|
|
@@ -1446,7 +1492,7 @@ export function createFormieClient(): FormieClient {
|
|
|
1446
1492
|
};
|
|
1447
1493
|
|
|
1448
1494
|
const registerModule = (
|
|
1449
|
-
moduleDefinition:
|
|
1495
|
+
moduleDefinition: BrowserModuleDefinition,
|
|
1450
1496
|
options?: Parameters<ModuleRegistry['register']>[1],
|
|
1451
1497
|
): boolean => {
|
|
1452
1498
|
return moduleRegistry.register(moduleDefinition, options);
|
|
@@ -1456,7 +1502,7 @@ export function createFormieClient(): FormieClient {
|
|
|
1456
1502
|
moduleRegistry.unregister(moduleId);
|
|
1457
1503
|
};
|
|
1458
1504
|
|
|
1459
|
-
const getRegisteredModules = ():
|
|
1505
|
+
const getRegisteredModules = (): BrowserModuleDefinition[] => {
|
|
1460
1506
|
return moduleRegistry.getAll();
|
|
1461
1507
|
};
|
|
1462
1508
|
|