@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
package/src/js/modules/loader.ts
CHANGED
|
@@ -1,331 +1,395 @@
|
|
|
1
|
-
import type
|
|
2
|
-
|
|
3
|
-
FormieModuleInstance,
|
|
4
|
-
ModuleMatchContext,
|
|
5
|
-
ModuleSetupContext,
|
|
6
|
-
} from '#contracts/modules';
|
|
7
|
-
import type { FormModuleManifest, FormModuleTarget, FormModuleTargetType } from '#contracts/schema';
|
|
1
|
+
import { assertBrowserModuleManifest, type BrowserModuleManifest, type BrowserModuleEntry } from '@verbb/formie-core';
|
|
2
|
+
import type { BrowserModuleDefinition, BrowserModuleInstance, BrowserModuleFailure, BrowserModuleHydrationReport, ModuleMatchContext, ModuleSetupContext } from '#contracts/modules';
|
|
8
3
|
import { builtinAddressModuleLoaders } from '#modules/address';
|
|
9
4
|
import { builtinCaptchaModuleLoaders } from '#modules/captchas';
|
|
10
5
|
import { builtinFieldModuleLoaders } from '#modules/fields';
|
|
11
6
|
import { builtinPaymentModuleLoaders } from '#modules/payments';
|
|
12
7
|
import { ModuleRegistry } from '#modules/registry';
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
8
|
+
|
|
9
|
+
export type BrowserModuleRuntime = BrowserModuleHydrationReport & {
|
|
10
|
+
assertReady: () => void;
|
|
11
|
+
destroy: () => Promise<void>;
|
|
12
|
+
updateManifest: (manifest: BrowserModuleManifest) => Promise<void>;
|
|
13
|
+
};
|
|
19
14
|
|
|
20
15
|
type ModuleLoadContext = {
|
|
21
16
|
registry: ModuleRegistry;
|
|
22
17
|
setupContext: ModuleSetupContext;
|
|
23
|
-
matchContext:
|
|
18
|
+
matchContext: Pick<ModuleMatchContext, 'root' | 'form' | 'surface'>;
|
|
24
19
|
};
|
|
25
20
|
|
|
26
|
-
type
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
...builtinAddressModuleLoaders,
|
|
31
|
-
...builtinCaptchaModuleLoaders,
|
|
32
|
-
...builtinPaymentModuleLoaders,
|
|
21
|
+
type ModuleFailureDetails = {
|
|
22
|
+
name: string;
|
|
23
|
+
message: string;
|
|
24
|
+
stack?: string;
|
|
33
25
|
};
|
|
34
26
|
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const importModuleFromSrc = new Function('src', 'return import(src);') as (src: string) => Promise<unknown>;
|
|
38
|
-
|
|
39
|
-
async function emitModuleLifecycleEvent(
|
|
40
|
-
emit: ModuleSetupContext['emit'],
|
|
41
|
-
moduleId: string,
|
|
42
|
-
phase: ModuleLifecycleEventPhase,
|
|
43
|
-
detail: Record<string, unknown>,
|
|
44
|
-
): Promise<void> {
|
|
45
|
-
// Emit one stable global lifecycle event plus the scoped module event.
|
|
46
|
-
// Consumers can subscribe globally and filter by `detail.moduleId`, or bind
|
|
47
|
-
// directly to the scoped module id when they need a narrow hook.
|
|
48
|
-
await emit(getGlobalModuleLifecycleEventName(phase), detail);
|
|
49
|
-
await emit(getScopedModuleLifecycleEventName(moduleId, phase), detail);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function isModuleDefinition(definition: unknown): definition is FormieModuleDefinition {
|
|
53
|
-
return !!definition && typeof definition === 'object' &&
|
|
54
|
-
typeof (definition as FormieModuleDefinition).id === 'string' &&
|
|
55
|
-
typeof (definition as FormieModuleDefinition).setup === 'function' &&
|
|
56
|
-
typeof (definition as FormieModuleDefinition).match === 'function';
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async function resolveBuiltinDefinition(moduleId: string, ctx: ModuleLoadContext): Promise<FormieModuleDefinition | null> {
|
|
60
|
-
const loader = builtinModuleLoaders[moduleId];
|
|
27
|
+
const builtinLoaders = { ...builtinFieldModuleLoaders, ...builtinAddressModuleLoaders, ...builtinCaptchaModuleLoaders, ...builtinPaymentModuleLoaders };
|
|
28
|
+
const pendingDefinitions = new Map<string, Promise<BrowserModuleDefinition>>();
|
|
61
29
|
|
|
62
|
-
|
|
63
|
-
|
|
30
|
+
function normalizeFailureDetails(error: unknown): ModuleFailureDetails {
|
|
31
|
+
if (error instanceof Error) {
|
|
32
|
+
return {
|
|
33
|
+
name: error.name || 'Error',
|
|
34
|
+
message: error.message || 'No error message was provided.',
|
|
35
|
+
stack: error.stack,
|
|
36
|
+
};
|
|
64
37
|
}
|
|
65
38
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (!builtinModuleLoadCache.has(moduleId)) {
|
|
69
|
-
builtinModuleLoadCache.set(moduleId, (async() => {
|
|
70
|
-
try {
|
|
71
|
-
const definition = await loader();
|
|
72
|
-
|
|
73
|
-
if (!isModuleDefinition(definition)) {
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
ctx.registry.register(definition);
|
|
78
|
-
|
|
79
|
-
return definition;
|
|
80
|
-
} catch (error) {
|
|
81
|
-
console.error('[formie] Failed to load builtin module:', moduleId, error);
|
|
82
|
-
debug.warn('Failed loading builtin module.', { moduleId, error });
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
})());
|
|
39
|
+
if (typeof error === 'string') {
|
|
40
|
+
return { name: 'Error', message: error };
|
|
86
41
|
}
|
|
87
42
|
|
|
88
|
-
return builtinModuleLoadCache.get(moduleId) || null;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async function resolveDefinitionFromSrc(src: string): Promise<FormieModuleDefinition | null> {
|
|
92
43
|
try {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
};
|
|
97
|
-
const definition = imported?.default || imported?.formieModule || null;
|
|
98
|
-
|
|
99
|
-
if (!isModuleDefinition(definition)) {
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return definition;
|
|
104
|
-
} catch (error) {
|
|
105
|
-
console.error('[formie] Failed to load module from src:', src, error);
|
|
106
|
-
debug.warn('Failed loading module from src.', { src, error });
|
|
107
|
-
return null;
|
|
44
|
+
return { name: 'Error', message: JSON.stringify(error) || String(error) };
|
|
45
|
+
} catch {
|
|
46
|
+
return { name: 'Error', message: String(error) };
|
|
108
47
|
}
|
|
109
48
|
}
|
|
110
49
|
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
50
|
+
function buildTechnicalDetails(surface: BrowserModuleManifest['surface'], failures: Iterable<BrowserModuleFailure>, details: Map<string, ModuleFailureDetails>): string {
|
|
51
|
+
const requiredFailures = [...failures].filter((failure) => failure.required);
|
|
52
|
+
const lines = [
|
|
53
|
+
'Formie browser module diagnostics',
|
|
54
|
+
`Surface: ${surface}`,
|
|
55
|
+
`Browser: ${typeof navigator === 'undefined' ? 'Unavailable' : navigator.userAgent}`,
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
// Query parameters can contain resume or edit capabilities. Keep the useful
|
|
59
|
+
// CP path without copying those credentials into the diagnostic output.
|
|
60
|
+
if (surface === 'cp-edit' && typeof location !== 'undefined') {
|
|
61
|
+
lines.push(`Page: ${location.origin}${location.pathname}`);
|
|
120
62
|
}
|
|
121
63
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
64
|
+
requiredFailures.forEach((failure, index) => {
|
|
65
|
+
const failureDetails = details.get(failure.key);
|
|
66
|
+
lines.push(
|
|
67
|
+
'',
|
|
68
|
+
`Failure ${index + 1}`,
|
|
69
|
+
`Module: ${failure.moduleId}`,
|
|
70
|
+
`Key: ${failure.key}`,
|
|
71
|
+
`Code: ${failure.code}`,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// Raw exceptions can disclose implementation details, so only expose
|
|
75
|
+
// them in Craft's authenticated submission-editing surface.
|
|
76
|
+
if (surface === 'cp-edit' && failureDetails) {
|
|
77
|
+
lines.push(`Error: ${failureDetails.name}: ${failureDetails.message}`);
|
|
78
|
+
|
|
79
|
+
if (failureDetails.stack) {
|
|
80
|
+
lines.push('Stack:', failureDetails.stack);
|
|
81
|
+
}
|
|
134
82
|
}
|
|
135
|
-
}
|
|
83
|
+
});
|
|
136
84
|
|
|
137
|
-
return
|
|
85
|
+
return lines.join('\n');
|
|
138
86
|
}
|
|
139
87
|
|
|
140
|
-
function
|
|
141
|
-
if (
|
|
142
|
-
|
|
88
|
+
async function copyTechnicalDetails(value: string): Promise<void> {
|
|
89
|
+
if (navigator.clipboard?.writeText) {
|
|
90
|
+
await navigator.clipboard.writeText(value);
|
|
91
|
+
return;
|
|
143
92
|
}
|
|
144
93
|
|
|
145
|
-
|
|
146
|
-
|
|
94
|
+
const textarea = document.createElement('textarea');
|
|
95
|
+
textarea.value = value;
|
|
96
|
+
textarea.setAttribute('readonly', '');
|
|
97
|
+
textarea.style.position = 'fixed';
|
|
98
|
+
textarea.style.opacity = '0';
|
|
99
|
+
document.body.appendChild(textarea);
|
|
100
|
+
textarea.select();
|
|
147
101
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
102
|
+
try {
|
|
103
|
+
if (!document.execCommand('copy')) {
|
|
104
|
+
throw new Error('The browser did not allow clipboard access.');
|
|
105
|
+
}
|
|
106
|
+
} finally {
|
|
107
|
+
textarea.remove();
|
|
151
108
|
}
|
|
152
|
-
|
|
153
|
-
return Array.from(root.querySelectorAll(selector));
|
|
154
109
|
}
|
|
155
110
|
|
|
156
|
-
function
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
// a concrete DOM surface instead of re-querying ownership from options.
|
|
164
|
-
if (scope === 'selector') {
|
|
165
|
-
return queryTargets(root, targetId).map((element) => {
|
|
166
|
-
return { scope, element };
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (scope === 'field') {
|
|
171
|
-
return queryTargets(root, `[data-formie-field-handle="${escapeSelectorValue(targetId)}"]`).map((element) => {
|
|
172
|
-
return { scope, element };
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (scope === 'page') {
|
|
177
|
-
return queryTargets(root, `[data-formie-page-id="${escapeSelectorValue(targetId)}"]`).map((element) => {
|
|
178
|
-
return { scope, element };
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (scope === 'button') {
|
|
183
|
-
return queryTargets(root, `[data-formie-action="${escapeSelectorValue(targetId)}"]`).map((element) => {
|
|
184
|
-
return { scope, element };
|
|
185
|
-
});
|
|
111
|
+
async function resolveDefinition(moduleId: string, registry: ModuleRegistry): Promise<BrowserModuleDefinition> {
|
|
112
|
+
const registered = registry.get(moduleId);
|
|
113
|
+
if (registered) return registered;
|
|
114
|
+
const loader = moduleId.startsWith('formie:') && Object.prototype.hasOwnProperty.call(builtinLoaders, moduleId.slice(7)) ? builtinLoaders[moduleId.slice(7)] : undefined;
|
|
115
|
+
if (!loader) throw new Error(`Browser module ${moduleId} is not registered.`);
|
|
116
|
+
if (!pendingDefinitions.has(moduleId)) {
|
|
117
|
+
pendingDefinitions.set(moduleId, loader().catch((error) => { pendingDefinitions.delete(moduleId); throw error; }));
|
|
186
118
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}];
|
|
119
|
+
const definition = await pendingDefinitions.get(moduleId)!;
|
|
120
|
+
if (definition.moduleId !== moduleId) throw new Error(`Module definition does not match ${moduleId}.`);
|
|
121
|
+
registry.register(definition);
|
|
122
|
+
return definition;
|
|
192
123
|
}
|
|
193
124
|
|
|
194
|
-
function resolveTargets(
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
125
|
+
function resolveTargets(entry: BrowserModuleEntry, root: Element, form: HTMLFormElement | null): Element[] {
|
|
126
|
+
return [...new Set(entry.targets.flatMap((target) => {
|
|
127
|
+
if (target.type === 'form') return [form || root];
|
|
128
|
+
const selector = target.type === 'selector'
|
|
129
|
+
? target.selector
|
|
130
|
+
: target.type === 'field'
|
|
131
|
+
? `[data-formie-field-uid="${CSS.escape(target.uid)}"]`
|
|
132
|
+
: target.type === 'page'
|
|
133
|
+
? `[data-formie-page-id="${CSS.escape(target.id)}"]`
|
|
134
|
+
: `[data-formie-action="${CSS.escape(target.action)}"]`;
|
|
135
|
+
return [...(root.matches(selector) ? [root] : []), ...root.querySelectorAll(selector)];
|
|
136
|
+
}))].filter((target) => !target.closest('[hidden], [data-formie-hidden="true"], [data-formie-conditionally-hidden], [data-formie-page-hidden]'));
|
|
203
137
|
}
|
|
204
138
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
139
|
+
/** Reconcile declaration key + DOM occurrence, including targets added by repeaters. */
|
|
140
|
+
export async function loadModulesFromManifest(manifest: BrowserModuleManifest, ctx: ModuleLoadContext): Promise<BrowserModuleRuntime> {
|
|
141
|
+
assertBrowserModuleManifest(manifest);
|
|
142
|
+
const surface = manifest.surface;
|
|
143
|
+
if (ctx.matchContext.surface !== surface || ctx.setupContext.surface !== surface) {
|
|
144
|
+
throw new Error(`Browser module manifest surface ${surface} cannot mount as ${ctx.matchContext.surface}.`);
|
|
145
|
+
}
|
|
146
|
+
const { root, form } = ctx.setupContext;
|
|
147
|
+
const mounted = new Map<string, Map<Element, { instance: BrowserModuleInstance; config: string; moduleId: string; required: boolean }>>();
|
|
148
|
+
const failures = new Map<string, BrowserModuleFailure>();
|
|
149
|
+
const failureDetails = new Map<string, ModuleFailureDetails>();
|
|
150
|
+
const instances: BrowserModuleInstance[] = [];
|
|
151
|
+
let disposed = false;
|
|
152
|
+
let running: Promise<void> = Promise.resolve();
|
|
153
|
+
let queued = false;
|
|
154
|
+
const diagnose = async(entry: BrowserModuleEntry, error: unknown) => {
|
|
155
|
+
const diagnostic: BrowserModuleFailure = { key: entry.key, moduleId: entry.moduleId, required: entry.required, surface, code: 'MODULE_UNAVAILABLE', message: 'A form feature could not start. Reload the page or contact the site administrator.' };
|
|
156
|
+
failures.set(entry.key, diagnostic);
|
|
157
|
+
failureDetails.set(entry.key, normalizeFailureDetails(error));
|
|
158
|
+
console.error('[formie] Browser module failure', diagnostic, error);
|
|
159
|
+
await ctx.setupContext.emit('formie:browser:module:error', diagnostic);
|
|
160
|
+
};
|
|
161
|
+
const dispose = async(instance: BrowserModuleInstance) => {
|
|
162
|
+
try { await instance.destroy(); }
|
|
163
|
+
catch (error) {
|
|
164
|
+
console.error('[formie] Browser module disposal failed', error);
|
|
165
|
+
await ctx.setupContext.emit('formie:browser:module:error', { key: instance.key, moduleId: instance.moduleId, surface, code: 'MODULE_DISPOSE_FAILED', message: 'A form feature could not clean up. Reload the page before continuing.' });
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
const blocked = () => [...failures.values()].some((entry) => entry.required);
|
|
169
|
+
const showFailure = () => {
|
|
170
|
+
if (!form) return;
|
|
171
|
+
let message = form.querySelector<HTMLElement>('[data-formie-module-error]');
|
|
172
|
+
|
|
173
|
+
if (!message) {
|
|
174
|
+
const description = document.createElement('p');
|
|
175
|
+
description.textContent = surface === 'cp-edit'
|
|
176
|
+
? 'Reload the page. If the problem continues, copy the technical details below.'
|
|
177
|
+
: 'Reload the page or contact the site administrator.';
|
|
178
|
+
|
|
179
|
+
const report = document.createElement('pre');
|
|
180
|
+
report.dataset.formieModuleErrorDetails = 'true';
|
|
181
|
+
report.tabIndex = 0;
|
|
182
|
+
|
|
183
|
+
if (surface === 'cp-edit') {
|
|
184
|
+
message = document.createElement('pk-alert');
|
|
185
|
+
message.className = 'formie-module-error';
|
|
186
|
+
message.setAttribute('variant', 'error');
|
|
187
|
+
message.setAttribute('announce', 'assertive');
|
|
188
|
+
message.setAttribute('heading', 'A required form feature could not start.');
|
|
189
|
+
message.setAttribute('details-label', 'Details');
|
|
190
|
+
message.setAttribute('copy-label', 'Copy details');
|
|
191
|
+
message.setAttribute('copied-label', 'Details copied.');
|
|
192
|
+
message.setAttribute('copyable', '');
|
|
193
|
+
report.slot = 'details';
|
|
194
|
+
message.append(description, report);
|
|
195
|
+
} else {
|
|
196
|
+
message = document.createElement('div');
|
|
197
|
+
message.className = 'formie-module-error formie-module-error--fallback';
|
|
198
|
+
|
|
199
|
+
const notice = document.createElement('div');
|
|
200
|
+
notice.className = 'formie-module-error__notice';
|
|
201
|
+
notice.setAttribute('role', 'alert');
|
|
202
|
+
|
|
203
|
+
const indicator = document.createElement('span');
|
|
204
|
+
indicator.className = 'formie-module-error__indicator';
|
|
205
|
+
indicator.setAttribute('aria-hidden', 'true');
|
|
206
|
+
indicator.textContent = '!';
|
|
207
|
+
|
|
208
|
+
const content = document.createElement('div');
|
|
209
|
+
content.className = 'formie-module-error__content';
|
|
210
|
+
|
|
211
|
+
const title = document.createElement('strong');
|
|
212
|
+
title.className = 'formie-module-error__title';
|
|
213
|
+
title.textContent = 'A required form feature could not start.';
|
|
214
|
+
|
|
215
|
+
content.append(title, description);
|
|
216
|
+
notice.append(indicator, content);
|
|
217
|
+
|
|
218
|
+
const disclosure = document.createElement('details');
|
|
219
|
+
disclosure.className = 'formie-module-error__details';
|
|
220
|
+
|
|
221
|
+
const summary = document.createElement('summary');
|
|
222
|
+
summary.textContent = 'Details';
|
|
223
|
+
|
|
224
|
+
const actions = document.createElement('div');
|
|
225
|
+
actions.className = 'formie-module-error__actions';
|
|
226
|
+
|
|
227
|
+
const copyButton = document.createElement('button');
|
|
228
|
+
copyButton.className = 'formie-module-error__copy';
|
|
229
|
+
copyButton.type = 'button';
|
|
230
|
+
copyButton.textContent = 'Copy details';
|
|
231
|
+
|
|
232
|
+
const copyStatus = document.createElement('span');
|
|
233
|
+
copyStatus.className = 'formie-module-error__copy-status';
|
|
234
|
+
copyStatus.setAttribute('role', 'status');
|
|
235
|
+
copyStatus.setAttribute('aria-live', 'polite');
|
|
236
|
+
|
|
237
|
+
copyButton.addEventListener('click', async() => {
|
|
238
|
+
try {
|
|
239
|
+
await copyTechnicalDetails(report.textContent || '');
|
|
240
|
+
copyStatus.textContent = 'Details copied.';
|
|
241
|
+
} catch {
|
|
242
|
+
copyStatus.textContent = 'Copy failed. Select the details above and copy them manually.';
|
|
243
|
+
const range = document.createRange();
|
|
244
|
+
range.selectNodeContents(report);
|
|
245
|
+
window.getSelection()?.removeAllRanges();
|
|
246
|
+
window.getSelection()?.addRange(range);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
213
249
|
|
|
214
|
-
|
|
215
|
-
|
|
250
|
+
actions.append(copyButton, copyStatus);
|
|
251
|
+
disclosure.append(summary, report, actions);
|
|
252
|
+
message.append(notice, disclosure);
|
|
253
|
+
}
|
|
216
254
|
|
|
217
|
-
|
|
218
|
-
debug.warn('Skipping manifest item (definition not resolved).', {
|
|
219
|
-
moduleId: item.id,
|
|
220
|
-
src: item.src,
|
|
221
|
-
});
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
255
|
+
message.dataset.formieModuleError = 'true';
|
|
224
256
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (targets.length === 0 && definition.kind === 'address') {
|
|
233
|
-
console.warn(
|
|
234
|
-
`[formie] Address module "${item.id}" skipped: no target element found for ` +
|
|
235
|
-
`fieldHandle="${item.targets?.find((target) => target.targetType === 'field')?.targetId ?? '?'}". ` +
|
|
236
|
-
'Check that the Address field exists in the rendered form.',
|
|
237
|
-
);
|
|
257
|
+
// CP edit roots live inside Craft's padded content pane. Insert beside
|
|
258
|
+
// that root so the alert follows the same alignment as the fields.
|
|
259
|
+
if (root !== form && form.contains(root)) {
|
|
260
|
+
root.before(message);
|
|
261
|
+
} else {
|
|
262
|
+
form.prepend(message);
|
|
263
|
+
}
|
|
238
264
|
}
|
|
239
265
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
266
|
+
const report = message.querySelector<HTMLElement>('[data-formie-module-error-details]');
|
|
267
|
+
const details = buildTechnicalDetails(surface, failures.values(), failureDetails);
|
|
268
|
+
// This report lives inside the observed root; unchanged text must not
|
|
269
|
+
// schedule another reconciliation while a module remains unavailable.
|
|
270
|
+
if (report && report.textContent !== details) report.textContent = details;
|
|
271
|
+
};
|
|
272
|
+
const guard = (event: Event) => {
|
|
273
|
+
if (!blocked()) return;
|
|
274
|
+
event.preventDefault(); event.stopImmediatePropagation(); showFailure();
|
|
275
|
+
};
|
|
276
|
+
const reconcile = async() => {
|
|
277
|
+
const activeKeys = new Set(manifest.entries.map((entry) => entry.key));
|
|
278
|
+
for (const key of failures.keys()) {
|
|
279
|
+
if (activeKeys.has(key)) continue;
|
|
280
|
+
failures.delete(key);
|
|
281
|
+
failureDetails.delete(key);
|
|
282
|
+
}
|
|
283
|
+
const inactiveInstances: BrowserModuleInstance[] = [];
|
|
284
|
+
for (const [key, records] of mounted) {
|
|
285
|
+
if (activeKeys.has(key)) continue;
|
|
286
|
+
inactiveInstances.push(...Array.from(records.values(), ({ instance }) => instance));
|
|
287
|
+
mounted.delete(key);
|
|
288
|
+
failures.delete(key);
|
|
289
|
+
failureDetails.delete(key);
|
|
290
|
+
}
|
|
291
|
+
for (const instance of inactiveInstances.reverse()) {
|
|
292
|
+
await dispose(instance);
|
|
293
|
+
instances.splice(instances.indexOf(instance), 1);
|
|
294
|
+
}
|
|
295
|
+
for (const entry of manifest.entries) {
|
|
296
|
+
if (disposed) continue;
|
|
297
|
+
const failure = failures.get(entry.key);
|
|
298
|
+
if (failure) failures.set(entry.key, { ...failure, required: entry.required });
|
|
299
|
+
let targets: Element[];
|
|
300
|
+
try { targets = resolveTargets(entry, root, form); }
|
|
301
|
+
catch (error) { if (!failures.has(entry.key)) await diagnose(entry, error); continue; }
|
|
302
|
+
const records = mounted.get(entry.key) ?? new Map();
|
|
303
|
+
mounted.set(entry.key, records);
|
|
304
|
+
for (const [target, record] of Array.from(records.entries()).reverse()) {
|
|
305
|
+
if (!targets.includes(target)) {
|
|
306
|
+
await dispose(record.instance); records.delete(target);
|
|
307
|
+
instances.splice(instances.indexOf(record.instance), 1);
|
|
254
308
|
}
|
|
255
|
-
debug.log('Module target did not match predicate.', {
|
|
256
|
-
moduleId: definition.id,
|
|
257
|
-
scope: target.scope,
|
|
258
|
-
});
|
|
259
|
-
continue;
|
|
260
309
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
scope:
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
instance
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
310
|
+
// Resolve even absent targets: unknown required declarations cannot disappear silently.
|
|
311
|
+
let definition: BrowserModuleDefinition;
|
|
312
|
+
try { definition = await resolveDefinition(entry.moduleId, ctx.registry); }
|
|
313
|
+
catch (error) { if (!failures.has(entry.key)) await diagnose(entry, error); continue; }
|
|
314
|
+
let entryFailed = false;
|
|
315
|
+
let recovered = false;
|
|
316
|
+
for (const target of targets) {
|
|
317
|
+
if (disposed) return;
|
|
318
|
+
const config = JSON.stringify([entry.moduleId, entry.config, entry.required]);
|
|
319
|
+
const existing = records.get(target);
|
|
320
|
+
if (existing?.config === config) continue;
|
|
321
|
+
const setup = { ...ctx.setupContext, target, entryKey: entry.key, surface, scope: entry.targets[0]?.type ?? 'form', options: entry.config };
|
|
322
|
+
try {
|
|
323
|
+
if (existing) {
|
|
324
|
+
if (existing.instance.update && existing.moduleId === entry.moduleId && existing.required === entry.required) { await existing.instance.update(setup); existing.config = config; continue; }
|
|
325
|
+
await dispose(existing.instance); records.delete(target);
|
|
326
|
+
instances.splice(instances.indexOf(existing.instance), 1);
|
|
327
|
+
}
|
|
328
|
+
if (definition.surfaces && !definition.surfaces.includes(surface)) throw new Error(`Module ${entry.moduleId} does not support ${surface}.`);
|
|
329
|
+
if (definition.kind !== entry.kind) throw new Error(`Module ${entry.moduleId} is registered as ${definition.kind}, not ${entry.kind}.`);
|
|
330
|
+
if (!definition.match({ ...ctx.matchContext, target, scope: setup.scope, manifestItem: entry })) {
|
|
331
|
+
throw new Error(`Module ${entry.moduleId} does not support the rendered target.`);
|
|
332
|
+
}
|
|
333
|
+
const instance = await definition.setup(setup);
|
|
334
|
+
if (!instance) throw new Error(`Module ${entry.moduleId} did not initialize.`);
|
|
335
|
+
if (disposed || !root.contains(target) && target !== root) { await dispose(instance); continue; }
|
|
336
|
+
instance.key = entry.key; instance.moduleId = entry.moduleId; instance.kind = entry.kind; instance.target = target;
|
|
337
|
+
const ready = instance.assertReady;
|
|
338
|
+
instance.assertReady = () => {
|
|
339
|
+
try { ready?.(); }
|
|
340
|
+
catch (error) { void diagnose(entry, error); if (entry.required) throw new Error('A required form feature could not start.'); }
|
|
341
|
+
};
|
|
342
|
+
const before = instance.beforeSubmit;
|
|
343
|
+
const after = instance.afterSubmit;
|
|
344
|
+
instance.beforeSubmit = async(context) => {
|
|
345
|
+
try { await before?.(context); }
|
|
346
|
+
catch (error) { await diagnose(entry, error); if (entry.required) context.abort('A required form feature could not complete. Reload the page or contact the site administrator.'); }
|
|
347
|
+
};
|
|
348
|
+
instance.afterSubmit = async(context, result) => {
|
|
349
|
+
try { await after?.(context, result); }
|
|
350
|
+
catch (error) { await diagnose(entry, error); }
|
|
351
|
+
};
|
|
352
|
+
records.set(target, { instance, config, moduleId: entry.moduleId, required: entry.required }); instances.push(instance); recovered = true;
|
|
353
|
+
await ctx.setupContext.emit('formie:browser:module:mount', { key: entry.key, moduleId: entry.moduleId, target });
|
|
354
|
+
} catch (error) { entryFailed = true; if (!failures.has(entry.key)) await diagnose(entry, error); }
|
|
295
355
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
instanceCreated: !!instance,
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
if (instance) {
|
|
303
|
-
debug.log('Module instance created.', {
|
|
304
|
-
moduleId: definition.id,
|
|
305
|
-
scope: target.scope,
|
|
306
|
-
});
|
|
307
|
-
instances.push({
|
|
308
|
-
...instance,
|
|
309
|
-
destroy: async() => {
|
|
310
|
-
debug.log('Destroying module instance.', {
|
|
311
|
-
moduleId: definition.id,
|
|
312
|
-
scope: target.scope,
|
|
313
|
-
});
|
|
314
|
-
await emitModuleLifecycleEvent(ctx.setupContext.emit, moduleEventName, 'before-destroy', lifecycleDetail);
|
|
315
|
-
await instance.destroy();
|
|
316
|
-
await emitModuleLifecycleEvent(ctx.setupContext.emit, moduleEventName, 'after-destroy', lifecycleDetail);
|
|
317
|
-
debug.log('Module instance destroyed.', {
|
|
318
|
-
moduleId: definition.id,
|
|
319
|
-
scope: target.scope,
|
|
320
|
-
});
|
|
321
|
-
},
|
|
322
|
-
});
|
|
356
|
+
if (!entryFailed && (recovered || targets.length === 0)) {
|
|
357
|
+
failures.delete(entry.key);
|
|
358
|
+
failureDetails.delete(entry.key);
|
|
323
359
|
}
|
|
324
360
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
361
|
+
if (blocked()) showFailure(); else form?.querySelector('[data-formie-module-error]')?.remove();
|
|
362
|
+
};
|
|
363
|
+
const schedule = () => {
|
|
364
|
+
if (queued || disposed) return;
|
|
365
|
+
queued = true;
|
|
366
|
+
running = running.then(async() => { queued = false; if (!disposed) await reconcile(); });
|
|
367
|
+
void running.catch((error) => console.error('[formie] Module reconciliation failed', error));
|
|
368
|
+
};
|
|
369
|
+
const observer = new MutationObserver(schedule);
|
|
370
|
+
const runtime: BrowserModuleRuntime = {
|
|
371
|
+
get instances() { return instances; },
|
|
372
|
+
get failures() { return [...failures.values()]; },
|
|
373
|
+
assertReady: () => { if (blocked()) throw new Error('A required form feature could not start. Reload the page or contact the site administrator.'); },
|
|
374
|
+
destroy: async() => {
|
|
375
|
+
disposed = true; observer.disconnect(); form?.removeEventListener('submit', guard, true);
|
|
376
|
+
await running;
|
|
377
|
+
for (const records of Array.from(mounted.values()).reverse()) {
|
|
378
|
+
for (const { instance } of Array.from(records.values()).reverse()) await dispose(instance);
|
|
379
|
+
}
|
|
380
|
+
mounted.clear(); instances.splice(0); failures.clear(); failureDetails.clear();
|
|
381
|
+
form?.querySelector('[data-formie-module-error]')?.remove();
|
|
382
|
+
},
|
|
383
|
+
updateManifest: async(next) => {
|
|
384
|
+
assertBrowserModuleManifest(next);
|
|
385
|
+
if (next.surface !== surface) throw new Error('A mounted browser module runtime cannot change surfaces.');
|
|
386
|
+
manifest = next;
|
|
387
|
+
running = running.then(reconcile);
|
|
388
|
+
await running;
|
|
389
|
+
},
|
|
390
|
+
};
|
|
391
|
+
form?.addEventListener('submit', guard, true);
|
|
392
|
+
await reconcile();
|
|
393
|
+
observer.observe(root, { childList: true, subtree: true, attributes: true, attributeFilter: ['hidden', 'data-formie-hidden', 'data-formie-conditionally-hidden', 'data-formie-page-hidden', 'data-formie-field-uid', 'data-formie-page-id', 'data-formie-action'] });
|
|
394
|
+
return runtime;
|
|
331
395
|
}
|