From f9215892e7c60f7f26c7487952bbda07e4c72ebb Mon Sep 17 00:00:00 2001 From: Joan Miquel Torres Date: Tue, 23 Jun 2026 17:57:45 +0200 Subject: [PATCH 01/85] =?UTF-8?q?Revert=20Babel=208=20upgrade=20=E2=80=94?= =?UTF-8?q?=20incompatible=20with=20@rollup/plugin-babel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @rollup/plugin-babel@7.1.0 only supports @babel/core@^7.0.0. Downgrade back to Babel 7.29.x until the plugin ecosystem catches up. --- package-lock.json | 3157 +++++++++++++-------------------------------- package.json | 6 +- 2 files changed, 899 insertions(+), 2264 deletions(-) diff --git a/package-lock.json b/package-lock.json index 623b765a..b3a4c14d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,9 @@ "version": "0.18.0", "license": "MIT", "devDependencies": { - "@babel/core": "^8.0.1", - "@babel/plugin-proposal-decorators": "^8.0.2", - "@babel/preset-env": "^8.0.2", + "@babel/core": "^7.29.7", + "@babel/plugin-proposal-decorators": "^7.29.7", + "@babel/preset-env": "^7.29.7", "@playwright/test": "^1.61.0", "@rollup/plugin-babel": "^7.1.0", "@rollup/plugin-terser": "^1.0.0", @@ -43,180 +43,46 @@ } }, "node_modules/@babel/compat-data": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-8.0.0.tgz", - "integrity": "sha512-DOjnob/cXOUgDOozCDeq/aK2p5y8dUIVdf6tNhEV1HQRd6I8aQ4f4fbtHRVEvb6lP3BGomrKHiS8ICAASSVQSw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "license": "MIT", "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-8.0.1.tgz", - "integrity": "sha512-5FgxM4dLQpMJHSiVATk8foW263dVHQHBVpXYiimNECVWG01f4nFyEbQixeT6Mwvg7TayREJ2gpKl3o2RoMdnqw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/generator": "^8.0.0", - "@babel/helper-compilation-targets": "^8.0.0", - "@babel/helpers": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/template": "^8.0.0", - "@babel/traverse": "^8.0.0", - "@babel/types": "^8.0.0", - "@types/gensync": "^1.0.5", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", - "empathic": "^2.0.1", + "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "import-meta-resolve": "^4.2.0", "json5": "^2.2.3", - "obug": "^2.1.1", - "semver": "^7.7.3" + "semver": "^6.3.1" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/@babel/code-frame": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-8.0.0.tgz", - "integrity": "sha512-dYYg153EyN2Ekbqw2zAsbd6/JR+9N2SEoC7YV2GyyqMM7x9bLDTjBD6XBhSMLH0wtIVyJj03jWNriQhaN+eoCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^8.0.0", - "js-tokens": "^10.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/core/node_modules/@babel/generator": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0.tgz", - "integrity": "sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "@types/jsesc": "^2.5.0", - "jsesc": "^3.0.2" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/core/node_modules/@babel/helper-globals": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-8.0.0.tgz", - "integrity": "sha512-lLozHOM6sWWlxNo8CYqHy4MBZeTvHXNgVPBfPOGsjPKUzHC2Az9QwB6gxdQmpwHl6GlQtbGgS+lj5887guDiLw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/core/node_modules/@babel/helper-string-parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", - "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/core/node_modules/@babel/helper-validator-identifier": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.0.tgz", - "integrity": "sha512-kXxQVZHNOctSJJsqzmcbPSCEkM6oHNnDIkua7g9RCO9xRHj2eCiKvRx2KPdfWR9QxcGWnK/oArrtunmie3rL9g==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/core/node_modules/@babel/parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0.tgz", - "integrity": "sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^8.0.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/core/node_modules/@babel/template": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-8.0.0.tgz", - "integrity": "sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/core/node_modules/@babel/traverse": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-8.0.0.tgz", - "integrity": "sha512-bxTj/W2VclGE6CctlfQOpxg8MPDzXArRqkOBePw8EHfebcjF7fETWSS3BriEECo+UiU/Yblq+xUtSImFu7cTbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/generator": "^8.0.0", - "@babel/helper-globals": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/template": "^8.0.0", - "@babel/types": "^8.0.0", - "obug": "^2.1.1" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/core/node_modules/@babel/types": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0.tgz", - "integrity": "sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^8.0.0", - "@babel/helper-validator-identifier": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/core/node_modules/js-tokens": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", - "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", - "dev": true, - "license": "MIT" - }, "node_modules/@babel/generator": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", @@ -234,286 +100,118 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-8.0.0.tgz", - "integrity": "sha512-NSpMkMsvvZqzThJ0p1B02cbtA2ObEyfBvq950bmNkyxsxvcxwhvvCB036rKhlEnuBBo30bOrk13u3FzlKSoRrw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure/node_modules/@babel/helper-string-parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", - "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure/node_modules/@babel/helper-validator-identifier": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.2.tgz", - "integrity": "sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure/node_modules/@babel/types": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0.tgz", - "integrity": "sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^8.0.0", - "@babel/helper-validator-identifier": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, "node_modules/@babel/helper-compilation-targets": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-8.0.0.tgz", - "integrity": "sha512-JwculLABZvyPvyLBpwU/E/IbH2uM3mnxNtIJpxnIfb24y1PrdVxK5Dqjle4DpgqpGRnwgC7G8IkzPdSXZrO1Ew==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^8.0.0", - "@babel/helper-validator-option": "^8.0.0", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", - "lru-cache": "^11.0.0", - "semver": "^7.7.3" + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-8.0.1.tgz", - "integrity": "sha512-++t3ZktzlLmASAxIlxeXQK9Z2YwUafYGYcvGBFevqOqt16HozVHStUoQvWD09fzAZOb/uJGpUTBuGK41AJAuOA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", + "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^8.0.0", - "@babel/helper-member-expression-to-functions": "^8.0.0", - "@babel/helper-optimise-call-expression": "^8.0.0", - "@babel/helper-replace-supers": "^8.0.1", - "@babel/helper-skip-transparent-expression-wrappers": "^8.0.0", - "@babel/traverse": "^8.0.0", - "semver": "^7.7.3" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/traverse": "^7.29.7", + "semver": "^6.3.1" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/code-frame": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-8.0.0.tgz", - "integrity": "sha512-dYYg153EyN2Ekbqw2zAsbd6/JR+9N2SEoC7YV2GyyqMM7x9bLDTjBD6XBhSMLH0wtIVyJj03jWNriQhaN+eoCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^8.0.0", - "js-tokens": "^10.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/generator": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0.tgz", - "integrity": "sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "@types/jsesc": "^2.5.0", - "jsesc": "^3.0.2" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-globals": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-8.0.0.tgz", - "integrity": "sha512-lLozHOM6sWWlxNo8CYqHy4MBZeTvHXNgVPBfPOGsjPKUzHC2Az9QwB6gxdQmpwHl6GlQtbGgS+lj5887guDiLw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-member-expression-to-functions": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-8.0.0.tgz", - "integrity": "sha512-xkXrMbtk87Gk7+oKBVmBc6EORg/Qwx++AHESldmHkpvG8wgccdhJJFwrzqlF382Fk8wfXhJHWE/g/43QvEGNPQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^8.0.0", - "@babel/types": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-optimise-call-expression": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-8.0.0.tgz", - "integrity": "sha512-3W6satvtPuCUkUx63S2jMoW9EQNYkADgs1HTfufmL7gCmAulHMKupA/12WNz4A0GMMFn/YnWWwqOT9IZrJHQjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-string-parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", - "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-validator-identifier": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.2.tgz", - "integrity": "sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0.tgz", - "integrity": "sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ==", + "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^8.0.0" - }, - "bin": { - "parser": "bin/babel-parser.js" + "@babel/types": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/template": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-8.0.0.tgz", - "integrity": "sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==", + "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/traverse": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-8.0.0.tgz", - "integrity": "sha512-bxTj/W2VclGE6CctlfQOpxg8MPDzXArRqkOBePw8EHfebcjF7fETWSS3BriEECo+UiU/Yblq+xUtSImFu7cTbw==", + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.29.7.tgz", + "integrity": "sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/generator": "^8.0.0", - "@babel/helper-globals": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/template": "^8.0.0", - "@babel/types": "^8.0.0", - "obug": "^2.1.1" + "@babel/helper-annotate-as-pure": "^7.29.7", + "regexpu-core": "^6.3.1", + "semver": "^6.3.1" }, "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/types": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0.tgz", - "integrity": "sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^8.0.0", - "@babel/helper-validator-identifier": "^8.0.0" + "node": ">=6.9.0" }, - "engines": { - "node": "^22.18.0 || >=24.11.0" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/js-tokens": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", - "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-8.0.1.tgz", - "integrity": "sha512-PydTbcVTiIfVweHMeY1u3MslaD/ZzvnaTNhJp+7ghofelLWshF66Ckc/ZsjStfvRQIKQ4uVG0yEJucyDtyrWgw==", + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^8.0.0", - "regexpu-core": "^6.3.1", - "semver": "^7.7.3" + "@babel/types": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" - }, - "peerDependencies": { - "@babel/core": "^8.0.0" + "node": ">=6.9.0" } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-1.0.0.tgz", - "integrity": "sha512-9jzVaTeZyXRDKTgUnNzcPQMO8y0ga3o+Z4fKjNet9Fcx7slgKa83qRbz0EwROSd6qO6CoEe/HQszqSPKb5lhkw==", + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", + "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^8.0.0", - "@babel/helper-plugin-utils": "^8.0.0", - "lodash.debounce": "^4.0.8" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "debug": "^4.4.3", + "lodash.debounce": "^4.0.8", + "resolve": "^1.22.11" }, "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/@babel/helper-globals": { @@ -526,10 +224,10 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-module-imports": { + "node_modules/@babel/helper-member-expression-to-functions": { "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", - "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", + "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", "dev": true, "license": "MIT", "dependencies": { @@ -540,2490 +238,1433 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-8.0.1.tgz", - "integrity": "sha512-UgAhl1kqiW5ciE0yCXqqvnb4H2n3IELJ7lIIQRezwDPilPEZX5i+Rvbja9MFTkwUn2biEiSMeV31aUzR4Lwakw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^8.0.0", - "@babel/helper-validator-identifier": "^8.0.0", - "@babel/traverse": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - }, - "peerDependencies": { - "@babel/core": "^8.0.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/code-frame": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-8.0.0.tgz", - "integrity": "sha512-dYYg153EyN2Ekbqw2zAsbd6/JR+9N2SEoC7YV2GyyqMM7x9bLDTjBD6XBhSMLH0wtIVyJj03jWNriQhaN+eoCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^8.0.0", - "js-tokens": "^10.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/generator": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0.tgz", - "integrity": "sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "@types/jsesc": "^2.5.0", - "jsesc": "^3.0.2" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/helper-globals": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-8.0.0.tgz", - "integrity": "sha512-lLozHOM6sWWlxNo8CYqHy4MBZeTvHXNgVPBfPOGsjPKUzHC2Az9QwB6gxdQmpwHl6GlQtbGgS+lj5887guDiLw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/helper-module-imports": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-8.0.0.tgz", - "integrity": "sha512-NZ7mSS93o4ndX4KrbD7W8Sf3QT8Qe24PrnFyUcuOPDzK6faqDFKjY9RG7he7+I7FdiQ4llpnosFqzrXa+Vy3Ew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^8.0.0", - "@babel/types": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/helper-string-parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", - "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/helper-validator-identifier": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.2.tgz", - "integrity": "sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0.tgz", - "integrity": "sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^8.0.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/template": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-8.0.0.tgz", - "integrity": "sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/traverse": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-8.0.0.tgz", - "integrity": "sha512-bxTj/W2VclGE6CctlfQOpxg8MPDzXArRqkOBePw8EHfebcjF7fETWSS3BriEECo+UiU/Yblq+xUtSImFu7cTbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/generator": "^8.0.0", - "@babel/helper-globals": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/template": "^8.0.0", - "@babel/types": "^8.0.0", - "obug": "^2.1.1" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/types": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0.tgz", - "integrity": "sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^8.0.0", - "@babel/helper-validator-identifier": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/js-tokens": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", - "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-8.0.1.tgz", - "integrity": "sha512-3PKFgjTyPlhFhorfP+SjKQxLViIL++zWjFOO4hGriYU+Bsm983DxEM1JmDRJVWXV0O9npu+xXRqz7Pbd3mh70g==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - }, - "peerDependencies": { - "@babel/core": "^8.0.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-8.0.1.tgz", - "integrity": "sha512-baAKuLEMmu6BCSY3tuiU7qglM1qOZt6F1SrFScA241oNqksxkxfEZEKztlGRmoVns9AQ5UgArH7RsUEjxWnzgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^8.0.0", - "@babel/helper-wrap-function": "^8.0.0", - "@babel/traverse": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - }, - "peerDependencies": { - "@babel/core": "^8.0.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/code-frame": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-8.0.0.tgz", - "integrity": "sha512-dYYg153EyN2Ekbqw2zAsbd6/JR+9N2SEoC7YV2GyyqMM7x9bLDTjBD6XBhSMLH0wtIVyJj03jWNriQhaN+eoCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^8.0.0", - "js-tokens": "^10.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/generator": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0.tgz", - "integrity": "sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "@types/jsesc": "^2.5.0", - "jsesc": "^3.0.2" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-globals": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-8.0.0.tgz", - "integrity": "sha512-lLozHOM6sWWlxNo8CYqHy4MBZeTvHXNgVPBfPOGsjPKUzHC2Az9QwB6gxdQmpwHl6GlQtbGgS+lj5887guDiLw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-string-parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", - "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-validator-identifier": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.2.tgz", - "integrity": "sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0.tgz", - "integrity": "sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^8.0.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/template": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-8.0.0.tgz", - "integrity": "sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/traverse": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-8.0.0.tgz", - "integrity": "sha512-bxTj/W2VclGE6CctlfQOpxg8MPDzXArRqkOBePw8EHfebcjF7fETWSS3BriEECo+UiU/Yblq+xUtSImFu7cTbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/generator": "^8.0.0", - "@babel/helper-globals": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/template": "^8.0.0", - "@babel/types": "^8.0.0", - "obug": "^2.1.1" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/types": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0.tgz", - "integrity": "sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^8.0.0", - "@babel/helper-validator-identifier": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/js-tokens": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", - "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/helper-replace-supers": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-8.0.1.tgz", - "integrity": "sha512-B1SZADIcy3tmH8CmWvj4SHi/oAPom4UL3uknTc2QRNsPVLFk/sPnZvQL/8kj7Y5omvjMqie0vklvs6XM4OLW5Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^8.0.0", - "@babel/helper-optimise-call-expression": "^8.0.0", - "@babel/traverse": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - }, - "peerDependencies": { - "@babel/core": "^8.0.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/@babel/code-frame": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-8.0.0.tgz", - "integrity": "sha512-dYYg153EyN2Ekbqw2zAsbd6/JR+9N2SEoC7YV2GyyqMM7x9bLDTjBD6XBhSMLH0wtIVyJj03jWNriQhaN+eoCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^8.0.0", - "js-tokens": "^10.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/@babel/generator": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0.tgz", - "integrity": "sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "@types/jsesc": "^2.5.0", - "jsesc": "^3.0.2" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/@babel/helper-globals": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-8.0.0.tgz", - "integrity": "sha512-lLozHOM6sWWlxNo8CYqHy4MBZeTvHXNgVPBfPOGsjPKUzHC2Az9QwB6gxdQmpwHl6GlQtbGgS+lj5887guDiLw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/@babel/helper-member-expression-to-functions": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-8.0.0.tgz", - "integrity": "sha512-xkXrMbtk87Gk7+oKBVmBc6EORg/Qwx++AHESldmHkpvG8wgccdhJJFwrzqlF382Fk8wfXhJHWE/g/43QvEGNPQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^8.0.0", - "@babel/types": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/@babel/helper-optimise-call-expression": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-8.0.0.tgz", - "integrity": "sha512-3W6satvtPuCUkUx63S2jMoW9EQNYkADgs1HTfufmL7gCmAulHMKupA/12WNz4A0GMMFn/YnWWwqOT9IZrJHQjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/@babel/helper-string-parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", - "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/@babel/helper-validator-identifier": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.2.tgz", - "integrity": "sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/@babel/parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0.tgz", - "integrity": "sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^8.0.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/@babel/template": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-8.0.0.tgz", - "integrity": "sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/@babel/traverse": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-8.0.0.tgz", - "integrity": "sha512-bxTj/W2VclGE6CctlfQOpxg8MPDzXArRqkOBePw8EHfebcjF7fETWSS3BriEECo+UiU/Yblq+xUtSImFu7cTbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/generator": "^8.0.0", - "@babel/helper-globals": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/template": "^8.0.0", - "@babel/types": "^8.0.0", - "obug": "^2.1.1" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/@babel/types": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0.tgz", - "integrity": "sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^8.0.0", - "@babel/helper-validator-identifier": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/js-tokens": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", - "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-8.0.0.tgz", - "integrity": "sha512-xmCA9kP3IhySsqhzwIdWGlDN/1A4cCKNBO/uwZx/3YzmDoMePwno2Q5/Bq0q+tYaKbeF940YiKV/kaW8Mzvpjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^8.0.0", - "@babel/types": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers/node_modules/@babel/code-frame": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-8.0.0.tgz", - "integrity": "sha512-dYYg153EyN2Ekbqw2zAsbd6/JR+9N2SEoC7YV2GyyqMM7x9bLDTjBD6XBhSMLH0wtIVyJj03jWNriQhaN+eoCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^8.0.0", - "js-tokens": "^10.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers/node_modules/@babel/generator": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0.tgz", - "integrity": "sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "@types/jsesc": "^2.5.0", - "jsesc": "^3.0.2" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers/node_modules/@babel/helper-globals": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-8.0.0.tgz", - "integrity": "sha512-lLozHOM6sWWlxNo8CYqHy4MBZeTvHXNgVPBfPOGsjPKUzHC2Az9QwB6gxdQmpwHl6GlQtbGgS+lj5887guDiLw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers/node_modules/@babel/helper-string-parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", - "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers/node_modules/@babel/helper-validator-identifier": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.2.tgz", - "integrity": "sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers/node_modules/@babel/parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0.tgz", - "integrity": "sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^8.0.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers/node_modules/@babel/template": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-8.0.0.tgz", - "integrity": "sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers/node_modules/@babel/traverse": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-8.0.0.tgz", - "integrity": "sha512-bxTj/W2VclGE6CctlfQOpxg8MPDzXArRqkOBePw8EHfebcjF7fETWSS3BriEECo+UiU/Yblq+xUtSImFu7cTbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/generator": "^8.0.0", - "@babel/helper-globals": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/template": "^8.0.0", - "@babel/types": "^8.0.0", - "obug": "^2.1.1" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers/node_modules/@babel/types": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0.tgz", - "integrity": "sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^8.0.0", - "@babel/helper-validator-identifier": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers/node_modules/js-tokens": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", - "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-8.0.0.tgz", - "integrity": "sha512-U4Dybxh4WESWHt5XhBeExi4DrY0/DNK1aHpQbsrQXCUbFHuMweT0TpLEWKvaraV2Y6fS+ZXunsZ8zIuZIgvF2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-8.0.0.tgz", - "integrity": "sha512-Qpm8+wi5xfDkBfollanwriCcKniFfBmMmaKB01GVM6VGzKXo1fdxosZp04qEr5HM+LKhwr3hG1yRy8+ORsficA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^8.0.0", - "@babel/traverse": "^8.0.0", - "@babel/types": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-wrap-function/node_modules/@babel/code-frame": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-8.0.0.tgz", - "integrity": "sha512-dYYg153EyN2Ekbqw2zAsbd6/JR+9N2SEoC7YV2GyyqMM7x9bLDTjBD6XBhSMLH0wtIVyJj03jWNriQhaN+eoCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^8.0.0", - "js-tokens": "^10.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-wrap-function/node_modules/@babel/generator": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0.tgz", - "integrity": "sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "@types/jsesc": "^2.5.0", - "jsesc": "^3.0.2" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-wrap-function/node_modules/@babel/helper-globals": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-8.0.0.tgz", - "integrity": "sha512-lLozHOM6sWWlxNo8CYqHy4MBZeTvHXNgVPBfPOGsjPKUzHC2Az9QwB6gxdQmpwHl6GlQtbGgS+lj5887guDiLw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-wrap-function/node_modules/@babel/helper-string-parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", - "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-wrap-function/node_modules/@babel/helper-validator-identifier": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.2.tgz", - "integrity": "sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-wrap-function/node_modules/@babel/parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0.tgz", - "integrity": "sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^8.0.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-wrap-function/node_modules/@babel/template": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-8.0.0.tgz", - "integrity": "sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-wrap-function/node_modules/@babel/traverse": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-8.0.0.tgz", - "integrity": "sha512-bxTj/W2VclGE6CctlfQOpxg8MPDzXArRqkOBePw8EHfebcjF7fETWSS3BriEECo+UiU/Yblq+xUtSImFu7cTbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/generator": "^8.0.0", - "@babel/helper-globals": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/template": "^8.0.0", - "@babel/types": "^8.0.0", - "obug": "^2.1.1" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-wrap-function/node_modules/@babel/types": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0.tgz", - "integrity": "sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^8.0.0", - "@babel/helper-validator-identifier": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helper-wrap-function/node_modules/js-tokens": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", - "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/helpers": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-8.0.0.tgz", - "integrity": "sha512-wfbi91pM3py96oIiJEz7qIpyXDytgr9zQC1HEWwlGNVRAEmItuU/0a41ZUKu1sJGyhhOIpc4t5vk4PYzt8wpsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^8.0.0", - "@babel/types": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helpers/node_modules/@babel/code-frame": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-8.0.0.tgz", - "integrity": "sha512-dYYg153EyN2Ekbqw2zAsbd6/JR+9N2SEoC7YV2GyyqMM7x9bLDTjBD6XBhSMLH0wtIVyJj03jWNriQhaN+eoCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^8.0.0", - "js-tokens": "^10.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helpers/node_modules/@babel/helper-string-parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", - "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helpers/node_modules/@babel/helper-validator-identifier": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.0.tgz", - "integrity": "sha512-kXxQVZHNOctSJJsqzmcbPSCEkM6oHNnDIkua7g9RCO9xRHj2eCiKvRx2KPdfWR9QxcGWnK/oArrtunmie3rL9g==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helpers/node_modules/@babel/parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0.tgz", - "integrity": "sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^8.0.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helpers/node_modules/@babel/template": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-8.0.0.tgz", - "integrity": "sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helpers/node_modules/@babel/types": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0.tgz", - "integrity": "sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^8.0.0", - "@babel/helper-validator-identifier": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/helpers/node_modules/js-tokens": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", - "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", - "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, "license": "MIT", "dependencies": { + "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-8.0.1.tgz", - "integrity": "sha512-Ytgjjne4RnG3Oig7ik+NfY4ebRY30BPptVkkyu1f72eINJXRM3/bkU++tIc5aPvyLmo4KH20avq0xJ2o+9aEnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - }, - "peerDependencies": { - "@babel/core": "^8.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-8.0.1.tgz", - "integrity": "sha512-X7pAMBhuKluA7UfwZNvKN0XVVu/AGeo84Z75eJl85rcb8J2aBzLK92btahM1X5h0oi0QIrbe0qIMA/0+4Buk7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - }, - "peerDependencies": { - "@babel/core": "^8.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-8.0.1.tgz", - "integrity": "sha512-DJviKTxYfH0hFwnMiW4dnPyMGzS3Hrr4zUfXl1zwQ0QiGlGlNYklLoPSYEQr8S7nau0/K7NdQjTh0qbYuyFjCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - }, - "peerDependencies": { - "@babel/core": "^8.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-8.0.1.tgz", - "integrity": "sha512-DmR/N+B9+4PbURFj4+zdnWj49/PFAnK2bn8+E4ZAmwn3J5QCxnbG7Ep6aRfz9M8Aw+rBro0kIJQycvzFpl4buQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1", - "@babel/helper-skip-transparent-expression-wrappers": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - }, - "peerDependencies": { - "@babel/core": "^8.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-8.0.1.tgz", - "integrity": "sha512-x8bi0LFVD2xkULjfNn+hCMg16yAFHAM9fS/ThSFeYBi+0MP9K6qcY2BZb4urUwC7PYtEy5wPe6TKjOEjXrCGFA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1", - "@babel/helper-skip-transparent-expression-wrappers": "^8.0.0", - "@babel/plugin-transform-optional-chaining": "^8.0.1" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - }, - "peerDependencies": { - "@babel/core": "^8.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-8.0.1.tgz", - "integrity": "sha512-P8+RN2n7ts2s1vnE+lXdHYf+dhnmcGSen/kWzBsVluT9Sey5AqmcRXYWlHqgQxaNlKTD5YMa1tf5z4d1v8W88w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" - }, "engines": { - "node": "^22.18.0 || >=24.11.0" - }, - "peerDependencies": { - "@babel/core": "^8.0.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/plugin-proposal-decorators": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-8.0.2.tgz", - "integrity": "sha512-+C6O6KKXU7BBq1GNaIkFJxrALUVGRcr+WeWm4OcuRl3h+l/CmNfcTLMrT2Lm3uvGBimBH/8pEBRrXJFLoO67Gg==", + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^8.0.1", - "@babel/helper-plugin-utils": "^8.0.1", - "@babel/plugin-syntax-decorators": "^8.0.1" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-syntax-decorators": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-8.0.1.tgz", - "integrity": "sha512-NI+0S/6MvR6GlcQFwjDZ+WIc2qvG6TXN534lYs9llNldwW4b7Dh6KTtk030FA0xWdYGs4t1lWo+OEWN8wGB+Nw==", + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", + "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/types": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" - }, - "peerDependencies": { - "@babel/core": "^8.0.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-8.0.1.tgz", - "integrity": "sha512-o/gr7kRlq3PKLLuYth4udOsrC7geBerti+QtwPeyxMOsEQO1d8kDHqk9r2PtMx2y9i8FG7tzyTerfv1yMLSMsQ==", + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" - }, "engines": { - "node": "^22.18.0 || >=24.11.0" - }, - "peerDependencies": { - "@babel/core": "^8.0.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-8.0.1.tgz", - "integrity": "sha512-kqnSMF1YHBzuiQrl68675i5Ma1oljvo+SJsNEZFZVBu5BUrVIZm9KId3ui2PdtLK2sv2zM8sJnjPDfgLxQlEqQ==", + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.29.7.tgz", + "integrity": "sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1", - "@babel/helper-remap-async-to-generator": "^8.0.1", - "@babel/traverse": "^8.0.0" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-wrap-function": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-transform-async-generator-functions/node_modules/@babel/code-frame": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-8.0.0.tgz", - "integrity": "sha512-dYYg153EyN2Ekbqw2zAsbd6/JR+9N2SEoC7YV2GyyqMM7x9bLDTjBD6XBhSMLH0wtIVyJj03jWNriQhaN+eoCw==", + "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^8.0.0", - "js-tokens": "^10.0.0" + "@babel/types": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/plugin-transform-async-generator-functions/node_modules/@babel/generator": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0.tgz", - "integrity": "sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==", + "node_modules/@babel/helper-replace-supers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", + "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "@types/jsesc": "^2.5.0", - "jsesc": "^3.0.2" + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-transform-async-generator-functions/node_modules/@babel/helper-globals": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-8.0.0.tgz", - "integrity": "sha512-lLozHOM6sWWlxNo8CYqHy4MBZeTvHXNgVPBfPOGsjPKUzHC2Az9QwB6gxdQmpwHl6GlQtbGgS+lj5887guDiLw==", + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, "license": "MIT", "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/plugin-transform-async-generator-functions/node_modules/@babel/helper-string-parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", - "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/plugin-transform-async-generator-functions/node_modules/@babel/helper-validator-identifier": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.2.tgz", - "integrity": "sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==", + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/plugin-transform-async-generator-functions/node_modules/@babel/parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0.tgz", - "integrity": "sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ==", + "node_modules/@babel/helper-wrap-function": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.29.7.tgz", + "integrity": "sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^8.0.0" - }, - "bin": { - "parser": "bin/babel-parser.js" + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/plugin-transform-async-generator-functions/node_modules/@babel/template": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-8.0.0.tgz", - "integrity": "sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==", + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/plugin-transform-async-generator-functions/node_modules/@babel/traverse": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-8.0.0.tgz", - "integrity": "sha512-bxTj/W2VclGE6CctlfQOpxg8MPDzXArRqkOBePw8EHfebcjF7fETWSS3BriEECo+UiU/Yblq+xUtSImFu7cTbw==", + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/generator": "^8.0.0", - "@babel/helper-globals": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/template": "^8.0.0", - "@babel/types": "^8.0.0", - "obug": "^2.1.1" + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-transform-async-generator-functions/node_modules/@babel/types": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0.tgz", - "integrity": "sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==", + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.29.7.tgz", + "integrity": "sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^8.0.0", - "@babel/helper-validator-identifier": "^8.0.0" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-transform-async-generator-functions/node_modules/js-tokens": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", - "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-8.0.1.tgz", - "integrity": "sha512-e1jmmEU4p2Lx64sA1+EF8e8/RxPuegzbXcEbmFp5alDyLE+f2ViUpZ77bRWMXzihTwgVVmn/TOpqDbAuS5g1Ew==", + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.29.7.tgz", + "integrity": "sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^8.0.0", - "@babel/helper-plugin-utils": "^8.0.1", - "@babel/helper-remap-async-to-generator": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-transform-async-to-generator/node_modules/@babel/code-frame": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-8.0.0.tgz", - "integrity": "sha512-dYYg153EyN2Ekbqw2zAsbd6/JR+9N2SEoC7YV2GyyqMM7x9bLDTjBD6XBhSMLH0wtIVyJj03jWNriQhaN+eoCw==", + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.29.7.tgz", + "integrity": "sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^8.0.0", - "js-tokens": "^10.0.0" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-transform-async-to-generator/node_modules/@babel/generator": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0.tgz", - "integrity": "sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==", + "node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.7.tgz", + "integrity": "sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "@types/jsesc": "^2.5.0", - "jsesc": "^3.0.2" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator/node_modules/@babel/helper-globals": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-8.0.0.tgz", - "integrity": "sha512-lLozHOM6sWWlxNo8CYqHy4MBZeTvHXNgVPBfPOGsjPKUzHC2Az9QwB6gxdQmpwHl6GlQtbGgS+lj5887guDiLw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-transform-async-to-generator/node_modules/@babel/helper-module-imports": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-8.0.0.tgz", - "integrity": "sha512-NZ7mSS93o4ndX4KrbD7W8Sf3QT8Qe24PrnFyUcuOPDzK6faqDFKjY9RG7he7+I7FdiQ4llpnosFqzrXa+Vy3Ew==", + "node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^8.0.0", - "@babel/types": "^8.0.0" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator/node_modules/@babel/helper-string-parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", - "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator/node_modules/@babel/helper-validator-identifier": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.2.tgz", - "integrity": "sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/plugin-transform-async-to-generator/node_modules/@babel/parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0.tgz", - "integrity": "sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ==", + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.29.7.tgz", + "integrity": "sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^8.0.0" - }, - "bin": { - "parser": "bin/babel-parser.js" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-transform-optional-chaining": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" } }, - "node_modules/@babel/plugin-transform-async-to-generator/node_modules/@babel/template": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-8.0.0.tgz", - "integrity": "sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==", + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/plugin-transform-async-to-generator/node_modules/@babel/traverse": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-8.0.0.tgz", - "integrity": "sha512-bxTj/W2VclGE6CctlfQOpxg8MPDzXArRqkOBePw8EHfebcjF7fETWSS3BriEECo+UiU/Yblq+xUtSImFu7cTbw==", + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.29.7.tgz", + "integrity": "sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/generator": "^8.0.0", - "@babel/helper-globals": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/template": "^8.0.0", - "@babel/types": "^8.0.0", - "obug": "^2.1.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-transform-async-to-generator/node_modules/@babel/types": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0.tgz", - "integrity": "sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==", + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.29.7.tgz", + "integrity": "sha512-EtU0Hi3GvrTqD56xKmZvV/uCXK2ZbwVNPNLAquVItcAZpUhkXwWlo3Fmj0c2LxgSf2I8IDULeAepwNP1OefLXg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^8.0.0", - "@babel/helper-validator-identifier": "^8.0.0" + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-syntax-decorators": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-async-to-generator/node_modules/js-tokens": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", - "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-8.0.1.tgz", - "integrity": "sha512-0V97/gcf7LIgPieEiK1YT0eXa18XJFSLOTZjzEZhA9SJIqZhD/IwGUrCitBzXSmnGCP7hchwC6svHtJ/Eidcpg==", + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" - }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-8.0.1.tgz", - "integrity": "sha512-HxiQvKsSCs2jOmMhjDrooHaZYOy6W8bqwXp/zjdgPjsNrda6tK9/CH3a/cVIeg6ge3hSS02ALqvqgIo4rTsuSg==", + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.29.7.tgz", + "integrity": "sha512-9MTTLbF39X6sqM92JPEsoI7++26hjZvzkxKZy64aMhWLH2mPkJ/Q3AV4QLmls3R14FpSpkOwQQfUh962JGQxxg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-8.0.1.tgz", - "integrity": "sha512-tORnYiVhIHnKj90TgbSZXrO24f9oEpA6MgFxpIDSKKlHv7AzBIRhkMlYevanueLNYaQXqZWarfCgXM4bWTfNiw==", + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.29.7.tgz", + "integrity": "sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^8.0.1", - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-8.0.1.tgz", - "integrity": "sha512-NEVK+L0Le8h8tJ+IK0CGS5y9Yi1ZHxLj6M5PeanhMFuq9aSo0XI+Wtmbuyop6fTNukOm7ORNntf/kwid891vqQ==", + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^8.0.1", - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-classes": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-8.0.1.tgz", - "integrity": "sha512-phwyCES8kIMAdVOFw25ztmgAvkl2G+TvUv7azUYyrlR1Qoo3eLJC/MU3MGUKFZ4BWtsJ1NTJM1lKRLzKbswg7w==", + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^8.0.0", - "@babel/helper-compilation-targets": "^8.0.0", - "@babel/helper-globals": "^8.0.0", - "@babel/helper-plugin-utils": "^8.0.1", - "@babel/helper-replace-supers": "^8.0.1", - "@babel/traverse": "^8.0.0" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/@babel/code-frame": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-8.0.0.tgz", - "integrity": "sha512-dYYg153EyN2Ekbqw2zAsbd6/JR+9N2SEoC7YV2GyyqMM7x9bLDTjBD6XBhSMLH0wtIVyJj03jWNriQhaN+eoCw==", + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz", + "integrity": "sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^8.0.0", - "js-tokens": "^10.0.0" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/@babel/generator": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0.tgz", - "integrity": "sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==", + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.7.tgz", + "integrity": "sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "@types/jsesc": "^2.5.0", - "jsesc": "^3.0.2" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-remap-async-to-generator": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-globals": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-8.0.0.tgz", - "integrity": "sha512-lLozHOM6sWWlxNo8CYqHy4MBZeTvHXNgVPBfPOGsjPKUzHC2Az9QwB6gxdQmpwHl6GlQtbGgS+lj5887guDiLw==", + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.29.7.tgz", + "integrity": "sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==", "dev": true, "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-remap-async-to-generator": "^7.29.7" + }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-string-parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", - "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.29.7.tgz", + "integrity": "sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==", "dev": true, "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-validator-identifier": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.2.tgz", - "integrity": "sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==", + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.7.tgz", + "integrity": "sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==", "dev": true, "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/@babel/parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0.tgz", - "integrity": "sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ==", + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.29.7.tgz", + "integrity": "sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^8.0.0" - }, - "bin": { - "parser": "bin/babel-parser.js" + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/@babel/template": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-8.0.0.tgz", - "integrity": "sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==", + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.29.7.tgz", + "integrity": "sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0" + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/@babel/traverse": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-8.0.0.tgz", - "integrity": "sha512-bxTj/W2VclGE6CctlfQOpxg8MPDzXArRqkOBePw8EHfebcjF7fETWSS3BriEECo+UiU/Yblq+xUtSImFu7cTbw==", + "node_modules/@babel/plugin-transform-classes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz", + "integrity": "sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^8.0.0", - "@babel/generator": "^8.0.0", - "@babel/helper-globals": "^8.0.0", - "@babel/parser": "^8.0.0", - "@babel/template": "^8.0.0", - "@babel/types": "^8.0.0", - "obug": "^2.1.1" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/@babel/types": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0.tgz", - "integrity": "sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==", + "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^8.0.0", - "@babel/helper-validator-identifier": "^8.0.0" + "@babel/types": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/js-tokens": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", - "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", - "dev": true, - "license": "MIT" - }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-8.0.1.tgz", - "integrity": "sha512-i4l3OGLO8DUDcwdnyraOvILbhqdUf4QgfzhVxSOSzRy49XKXrY7pwaSg9gDSKmhZfNPrEMciBSJSciQh/CjB1A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.29.7.tgz", + "integrity": "sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/template": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-8.0.1.tgz", - "integrity": "sha512-RtR8uLDl0QcCmqMNIkM8gmDeYZ3rS0ZH+sa+I6sfc09yFoqfp9AEPgBstq9KyfVb0lFCVSRFfJXCI70FIl5ccw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.29.7.tgz", + "integrity": "sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-8.0.1.tgz", - "integrity": "sha512-czOUoSaZljJ92yu+bYlXqb/UBN8K9daNCob/B6/7nthSvfGP6YhCnfqD64XWfyb2dN4ypxALNplApoJrsMd4fw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.29.7.tgz", + "integrity": "sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^8.0.1", - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-8.0.1.tgz", - "integrity": "sha512-kNnVLkxFUEcTtCyB5PFVQ5Xoy88Bk1lU/ZgDu97CW8eNhRH2Wsiy8Sq5l5dFnwtIUYjzsXHU77jUy1W5AtGSIw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.29.7.tgz", + "integrity": "sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-8.0.1.tgz", - "integrity": "sha512-Tv43P47o6fuHgBL7HLHQg3WKXohW9CEUGjLtnCDW27yJLK0zKUdTTqREbZbycNHA83hewMjde5tF6ekrHu9bAA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.7.tgz", + "integrity": "sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^8.0.1", - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0" } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-8.0.1.tgz", - "integrity": "sha512-AS9GlgKc43tJNRu7yOvLaTko4qmdOb+8M69uNS8i421WLO20eVez7LdG5khKdi8E0LIQpYzzzdGIrdXWnO753g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.29.7.tgz", + "integrity": "sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-explicit-resource-management": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-8.0.1.tgz", - "integrity": "sha512-VzDIYwBlLCpV6mJfloRdJm8HmYnMqs7O+bGha8yfg2kP7jAdxeCw6yZBVBeaKKQUThtSU52iy+3lB7DhYsbOBA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.29.7.tgz", + "integrity": "sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1", - "@babel/plugin-transform-destructuring": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-8.0.1.tgz", - "integrity": "sha512-DsZvUUklUmDQ7d2vp+VjqgUWD51mGxhZZ1FPdPP9Hcj0vsgGUKX+zEBGp/vzB1O5PZUxWT/Euq5fu39M9dm9wg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.29.7.tgz", + "integrity": "sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-8.0.1.tgz", - "integrity": "sha512-bFzznm46bvWGaTYKle3iolbBJ+oPBfUjwCPesxlFE3SQ7DaY9EHf/8Y5ZzrodKJi8JDdcAyaVWaDUSVyhULh0g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.29.7.tgz", + "integrity": "sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-8.0.1.tgz", - "integrity": "sha512-rpeXtgELjpIBQH/+YmyFlD9timPEVCyqY+TNednzoeoTYvXSBEeUvYnYE+BK8rB8m6hHiNK7aL9QWKhGifEJCw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.29.7.tgz", + "integrity": "sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1", - "@babel/helper-skip-transparent-expression-wrappers": "^8.0.0" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of/node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-8.0.1.tgz", - "integrity": "sha512-H1L/JfPf3CqmubuaiZaquXKQ8MRs4YWSsgRllkTviM8TafcCNnlvc4/fJZ3rXP8HmFM+/Bg+TlsPehUI9BtDFA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.29.7.tgz", + "integrity": "sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^8.0.0", - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-8.0.1.tgz", - "integrity": "sha512-Mowp8X0J6p7ZehLU82B5e65te2uuSeDHyxrEROwEAS2VKXNXssfw5ZMqhY7k9iXTsOv1Xs/49G3lDCj9Vvw8qQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.29.7.tgz", + "integrity": "sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-literals": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-8.0.1.tgz", - "integrity": "sha512-ai7kfPRcfyUV1EszXoF1PvL3IuJoCuH08WSEPoRcJTWfZZ55VL/rcfvbVY16QLA3jjbzzSneQSoCtD3L6OyUjw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.29.7.tgz", + "integrity": "sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-8.0.1.tgz", - "integrity": "sha512-Emvtr5zkEGyCNAmt+qKD5EUh8G0RbxV9EZWrDdX0LuVy5tBq1B3fOIslvVF9aCJmpnwS/AvAT53b9LxAZyXlng==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.29.7.tgz", + "integrity": "sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-8.0.1.tgz", - "integrity": "sha512-3Axi9abnyGsm/hh6DsKPZ1Cr9fTtKqS7w0Ig5g12mU269YclpH8pV3xMln2vPLexXgUp6S6L+I06d9/YOLfRKA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.29.7.tgz", + "integrity": "sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-8.0.1.tgz", - "integrity": "sha512-FDdhET8y1YFDNRuoynqSf23WTzbBBpbIB2oRrlFX7YYm9uWtFvJDSD1r/epBSjfPkOjeaaLgRW9xNnt3JGx46A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.29.7.tgz", + "integrity": "sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^8.0.1", - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-8.0.1.tgz", - "integrity": "sha512-PMuzulWrrzFNmY3lXSk/tV9NRb7y0eZZLJY4UEo2TKszroxvUZHAPPi+T9FDyrQhod+TQA+t+8/QYaaMpiEuhA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", + "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^8.0.1", - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-8.0.1.tgz", - "integrity": "sha512-0NEHanXmnFEnfT2dLKTXnu7m8GXFsnxRgteBC2aH21hYMBwAgxu5dcTdi/Eg+ToI1HbZe0CHwz4XRLgRNQhYoQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.7.tgz", + "integrity": "sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^8.0.1", - "@babel/helper-plugin-utils": "^8.0.1", - "@babel/helper-validator-identifier": "^8.0.0" + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs/node_modules/@babel/helper-validator-identifier": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.2.tgz", - "integrity": "sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-8.0.1.tgz", - "integrity": "sha512-XKTa2J2MdkmbVEeChq9f7Or0VYcsF0NyVBgytRyeN9F+J+ETAB2SHhfkG4toz/ssuU0i+h/QgJ6ddo5YakSQcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.29.7.tgz", + "integrity": "sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^8.0.1", - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-8.0.1.tgz", - "integrity": "sha512-zCHu+Jr2gTdJE48lN9SV/kXueCW2M79mKtKJc/ttfzzr/jvgdQdCd17RADMqFRQc/25MLxdtjTmlD0HSAMOlIQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.7.tgz", + "integrity": "sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^8.0.1", - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0" } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-8.0.1.tgz", - "integrity": "sha512-QSQxVg1x4PuOuhWUs4Y9u+x9Y+ER8z6G3tC+bDLBzvoOrNLJrEBQLRnwrTP8e5klihAw6Z+e9X5RjdAKcAGapA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.29.7.tgz", + "integrity": "sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-8.0.1.tgz", - "integrity": "sha512-AgCJAmQLF7+PtsK79wJqr4xJ2StHCXlz7JL5CVFP4HejJx25Tk6yl1ZrXvi0cKh3VGDVnfVxefxnrpsBirgpyQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.29.7.tgz", + "integrity": "sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-8.0.1.tgz", - "integrity": "sha512-it2DmUyLIA1GQUXlFDEnI+/G89mTgxndnAiZYpW8xYR6LboblfirMqiWJeTna5uypQJg7viTT4D1iEURRtFcfw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.29.7.tgz", + "integrity": "sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-8.0.1.tgz", - "integrity": "sha512-VmxkDu6bBdbxRzqn6E93hYucug4OVa6svSO19W//vVzNUGAmQzk3QRyHyyEtfcjSLR3NWfRsWwVM9zExLmd+2w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.29.7.tgz", + "integrity": "sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^8.0.0", - "@babel/helper-plugin-utils": "^8.0.1", - "@babel/plugin-transform-destructuring": "^8.0.1", - "@babel/plugin-transform-parameters": "^8.0.1" + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7", + "@babel/plugin-transform-parameters": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-8.0.1.tgz", - "integrity": "sha512-fDkPXRTRKGm25bAq01q82UM4ypPqdVXCwphUUm4t1dL01fGIG0v8KRvT+4BjhMAtRxtPuI34t5Vs7yjRgs3ZgQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.29.7.tgz", + "integrity": "sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1", - "@babel/helper-replace-supers": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-8.0.1.tgz", - "integrity": "sha512-b2OQ74uGliyATcasTjxGy2O/86UI/n+EN4juB4EMfEwTi9j9uq70PuP0L8fW77vfRY66gO/YoTo/WbIdQ/Si1g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.29.7.tgz", + "integrity": "sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-8.0.1.tgz", - "integrity": "sha512-WtRS1c94lZGpGHxYLXMEWeoMVcuv8nkiyr8BTs6OYZv7N3Y9xVE8nbdFIl4lDJH6aH8/pLhqAQOL69d/WI9WdA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz", + "integrity": "sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1", - "@babel/helper-skip-transparent-expression-wrappers": "^8.0.0" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining/node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-8.0.1.tgz", - "integrity": "sha512-IIwRqroW0CYQwR6+3pnmu27z+H98poScWdnov8z6osumMeEsFxAFBBsDS2CFk2jFpPlGqVr89jK/HXO6i5DzxQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.29.7.tgz", + "integrity": "sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-8.0.1.tgz", - "integrity": "sha512-TrFCGcXaVDh6S5IRhmLSRTY9H80VTCMQWnZtzBRg4RWg3KCLmdmsmj4M15kZAPZfoPkWL/SJb4em3Py/vOiX8g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.29.7.tgz", + "integrity": "sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^8.0.1", - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-8.0.1.tgz", - "integrity": "sha512-e+yfOqSYBZaf3PARpiQkjZrpWYgmcFLhK+1tevh2CpHR1O9/36IdyPnAZusESX5nzVV/XZTDAtQBRLa8HPT5Dw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.29.7.tgz", + "integrity": "sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^8.0.0", - "@babel/helper-create-class-features-plugin": "^8.0.1", - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-8.0.1.tgz", - "integrity": "sha512-Z/qx4cxUtYR1nt7XWRutObPxDks98fEYsjWbVeKEqZH6y3AGknmgzCqmHf2FHWZCl1DfoPeuJY+3hZ+35D+2tg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.29.7.tgz", + "integrity": "sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-8.0.2.tgz", - "integrity": "sha512-aFfsjCRYducRV4dPnpsBbdRkLjboca9FVDg6HZCgy0Ahvk2ZQ/2exmCRC5qS9P6rsWwrmIheNaIM6A1j2F8KMA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.7.tgz", + "integrity": "sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-8.0.1.tgz", - "integrity": "sha512-02ITRDBesPdTYU0oShAzERwEPzozOUQSXlz3qrt8JGuhalBJQv9z5NjgHJPC9sS3Fsam8gDtfAEpBnqZwUIdjQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.29.7.tgz", + "integrity": "sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^8.0.1", - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0" } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-8.0.1.tgz", - "integrity": "sha512-+aykZi7ZP3U84veqfJXm3HhPZGddWFi64g7jr0ni6tb1zel+1ey+SL+IRKPoZXFyFqvYEsoqrmx4PyEJRlHl/Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.29.7.tgz", + "integrity": "sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-8.0.1.tgz", - "integrity": "sha512-JddANd9yPVH8dYgVoNkqAH5BftnsDxFpG51Zas7sc6F3poz5QWcejHNGO8a/57IX5ByjGSzEmYk9Z7ZMa5MWaw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.29.7.tgz", + "integrity": "sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-spread": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-8.0.1.tgz", - "integrity": "sha512-O9Bw9FyxlSw1SlMg3S82/GKNZ0x77RPbHezotEy1JTlIM/vk6WO8jW1iF+iTiKLOXNvi+b+LZ9t77Gi+Q0FhGg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.7.tgz", + "integrity": "sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1", - "@babel/helper-skip-transparent-expression-wrappers": "^8.0.0" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread/node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-8.0.1.tgz", - "integrity": "sha512-IsVP6WrZZQdaG2zLmeKwWiI+ua2NB5L1+f77C2/8z2NCDz7uxlIA/lnwocYOJk9PXcOC2sZgRls3LN4XpNduzQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.29.7.tgz", + "integrity": "sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-8.0.1.tgz", - "integrity": "sha512-JXvtj5+BJA9Qv3prDzW2z2DkGTJNmG0BObTdUD03STiu1Jr4fNQkQy3hYZgPL46a2RjcuhwBMYf49BOuJ98gnA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.29.7.tgz", + "integrity": "sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-8.0.1.tgz", - "integrity": "sha512-+wJoxgxP2gtey0UMUOMhzMMji2XHO/Uu6MXUh/r5Yhc2jngKzK/wFxY2WNe4UCaRcMvCb4gcnB8wIgFXJsocXg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.29.7.tgz", + "integrity": "sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-8.0.1.tgz", - "integrity": "sha512-TAXJepIJ6vZphytTwcf+LuXi2M2ZWI43VCqNw+1ZZLPP/38Z1A8j4Mahvg8kqDgMOSM/cakk+hedTJCiw3jQuQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.29.7.tgz", + "integrity": "sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-8.0.1.tgz", - "integrity": "sha512-zjBN9tSMSuomNDfurL69Gf7+v4D2t5uI1mSZaYJDo88SKpbduhCXqtxH7Tx66iCF6caWYwnBzSM0tnCozmQq5Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.29.7.tgz", + "integrity": "sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^8.0.1", - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-8.0.1.tgz", - "integrity": "sha512-v0oO83cvT5lwbcIVRShpx4vaHD8AvM9IBowsQuTeP+kGmhh3recJQs33Bl6dlo3/2g9amlznLbFGn4VJbPCJqA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.29.7.tgz", + "integrity": "sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^8.0.1", - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-8.0.1.tgz", - "integrity": "sha512-MlQeyS0K7gh0XNeLBMS/3Z07HjDOKhA7xm2L18GyxOXyiFHI9E+ZuQ4mFYmcLjluXsE/Wf6dABIqZvKpKw0Z3w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.29.7.tgz", + "integrity": "sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^8.0.1", - "@babel/helper-plugin-utils": "^8.0.1" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0" } }, "node_modules/@babel/preset-env": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-8.0.2.tgz", - "integrity": "sha512-CUGLn9hNBCF/eXnwdFAWERbniCcXCRvnKwLV9fegeUEIqv7YlU2MepsWMMM54GcILx5XYMnRh+JAL+K5G+mK6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^8.0.0", - "@babel/helper-compilation-targets": "^8.0.0", - "@babel/helper-plugin-utils": "^8.0.1", - "@babel/helper-validator-option": "^8.0.0", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^8.0.1", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^8.0.1", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^8.0.1", - "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^8.0.1", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^8.0.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^8.0.1", - "@babel/plugin-transform-arrow-functions": "^8.0.1", - "@babel/plugin-transform-async-generator-functions": "^8.0.1", - "@babel/plugin-transform-async-to-generator": "^8.0.1", - "@babel/plugin-transform-block-scoped-functions": "^8.0.1", - "@babel/plugin-transform-block-scoping": "^8.0.1", - "@babel/plugin-transform-class-properties": "^8.0.1", - "@babel/plugin-transform-class-static-block": "^8.0.1", - "@babel/plugin-transform-classes": "^8.0.1", - "@babel/plugin-transform-computed-properties": "^8.0.1", - "@babel/plugin-transform-destructuring": "^8.0.1", - "@babel/plugin-transform-dotall-regex": "^8.0.1", - "@babel/plugin-transform-duplicate-keys": "^8.0.1", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^8.0.1", - "@babel/plugin-transform-dynamic-import": "^8.0.1", - "@babel/plugin-transform-explicit-resource-management": "^8.0.1", - "@babel/plugin-transform-exponentiation-operator": "^8.0.1", - "@babel/plugin-transform-export-namespace-from": "^8.0.1", - "@babel/plugin-transform-for-of": "^8.0.1", - "@babel/plugin-transform-function-name": "^8.0.1", - "@babel/plugin-transform-json-strings": "^8.0.1", - "@babel/plugin-transform-literals": "^8.0.1", - "@babel/plugin-transform-logical-assignment-operators": "^8.0.1", - "@babel/plugin-transform-member-expression-literals": "^8.0.1", - "@babel/plugin-transform-modules-amd": "^8.0.1", - "@babel/plugin-transform-modules-commonjs": "^8.0.1", - "@babel/plugin-transform-modules-systemjs": "^8.0.1", - "@babel/plugin-transform-modules-umd": "^8.0.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^8.0.1", - "@babel/plugin-transform-new-target": "^8.0.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^8.0.1", - "@babel/plugin-transform-numeric-separator": "^8.0.1", - "@babel/plugin-transform-object-rest-spread": "^8.0.1", - "@babel/plugin-transform-object-super": "^8.0.1", - "@babel/plugin-transform-optional-catch-binding": "^8.0.1", - "@babel/plugin-transform-optional-chaining": "^8.0.1", - "@babel/plugin-transform-parameters": "^8.0.1", - "@babel/plugin-transform-private-methods": "^8.0.1", - "@babel/plugin-transform-private-property-in-object": "^8.0.1", - "@babel/plugin-transform-property-literals": "^8.0.1", - "@babel/plugin-transform-regenerator": "^8.0.2", - "@babel/plugin-transform-regexp-modifiers": "^8.0.1", - "@babel/plugin-transform-reserved-words": "^8.0.1", - "@babel/plugin-transform-shorthand-properties": "^8.0.1", - "@babel/plugin-transform-spread": "^8.0.1", - "@babel/plugin-transform-sticky-regex": "^8.0.1", - "@babel/plugin-transform-template-literals": "^8.0.1", - "@babel/plugin-transform-typeof-symbol": "^8.0.1", - "@babel/plugin-transform-unicode-escapes": "^8.0.1", - "@babel/plugin-transform-unicode-property-regex": "^8.0.1", - "@babel/plugin-transform-unicode-regex": "^8.0.1", - "@babel/plugin-transform-unicode-sets-regex": "^8.0.1", - "@babel/preset-modules": "^0.2.0", - "babel-plugin-polyfill-corejs3": "^1.0.0", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.7.tgz", + "integrity": "sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.29.7", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.29.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.29.7", + "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.29.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.29.7", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.29.7", + "@babel/plugin-syntax-import-attributes": "^7.29.7", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.29.7", + "@babel/plugin-transform-async-generator-functions": "^7.29.7", + "@babel/plugin-transform-async-to-generator": "^7.29.7", + "@babel/plugin-transform-block-scoped-functions": "^7.29.7", + "@babel/plugin-transform-block-scoping": "^7.29.7", + "@babel/plugin-transform-class-properties": "^7.29.7", + "@babel/plugin-transform-class-static-block": "^7.29.7", + "@babel/plugin-transform-classes": "^7.29.7", + "@babel/plugin-transform-computed-properties": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7", + "@babel/plugin-transform-dotall-regex": "^7.29.7", + "@babel/plugin-transform-duplicate-keys": "^7.29.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.7", + "@babel/plugin-transform-dynamic-import": "^7.29.7", + "@babel/plugin-transform-explicit-resource-management": "^7.29.7", + "@babel/plugin-transform-exponentiation-operator": "^7.29.7", + "@babel/plugin-transform-export-namespace-from": "^7.29.7", + "@babel/plugin-transform-for-of": "^7.29.7", + "@babel/plugin-transform-function-name": "^7.29.7", + "@babel/plugin-transform-json-strings": "^7.29.7", + "@babel/plugin-transform-literals": "^7.29.7", + "@babel/plugin-transform-logical-assignment-operators": "^7.29.7", + "@babel/plugin-transform-member-expression-literals": "^7.29.7", + "@babel/plugin-transform-modules-amd": "^7.29.7", + "@babel/plugin-transform-modules-commonjs": "^7.29.7", + "@babel/plugin-transform-modules-systemjs": "^7.29.7", + "@babel/plugin-transform-modules-umd": "^7.29.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.7", + "@babel/plugin-transform-new-target": "^7.29.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.29.7", + "@babel/plugin-transform-numeric-separator": "^7.29.7", + "@babel/plugin-transform-object-rest-spread": "^7.29.7", + "@babel/plugin-transform-object-super": "^7.29.7", + "@babel/plugin-transform-optional-catch-binding": "^7.29.7", + "@babel/plugin-transform-optional-chaining": "^7.29.7", + "@babel/plugin-transform-parameters": "^7.29.7", + "@babel/plugin-transform-private-methods": "^7.29.7", + "@babel/plugin-transform-private-property-in-object": "^7.29.7", + "@babel/plugin-transform-property-literals": "^7.29.7", + "@babel/plugin-transform-regenerator": "^7.29.7", + "@babel/plugin-transform-regexp-modifiers": "^7.29.7", + "@babel/plugin-transform-reserved-words": "^7.29.7", + "@babel/plugin-transform-shorthand-properties": "^7.29.7", + "@babel/plugin-transform-spread": "^7.29.7", + "@babel/plugin-transform-sticky-regex": "^7.29.7", + "@babel/plugin-transform-template-literals": "^7.29.7", + "@babel/plugin-transform-typeof-symbol": "^7.29.7", + "@babel/plugin-transform-unicode-escapes": "^7.29.7", + "@babel/plugin-transform-unicode-property-regex": "^7.29.7", + "@babel/plugin-transform-unicode-regex": "^7.29.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.29.7", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.15", + "babel-plugin-polyfill-corejs3": "^0.14.0", + "babel-plugin-polyfill-regenerator": "^0.6.6", "core-js-compat": "^3.48.0", - "semver": "^7.7.3" + "semver": "^6.3.1" }, "engines": { - "node": "^22.18.0 || >=24.11.0" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/preset-modules": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.2.0.tgz", - "integrity": "sha512-yz0RBN2fx4fjCeFcTWsWgL7PxSRltvTa0Qg14HkWCU3qS8MO7ZSJlBVbGceynd5C9NsJwwUHNQD3dc6tYO+jqQ==", + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^8.0.1", - "@babel/plugin-transform-dotall-regex": "^8.0.1", - "@babel/plugin-transform-unicode-property-regex": "^8.0.1", - "@babel/types": "^8.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", "esutils": "^2.0.2" }, "peerDependencies": { - "@babel/core": "^8.0.0" - } - }, - "node_modules/@babel/preset-modules/node_modules/@babel/helper-string-parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", - "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/preset-modules/node_modules/@babel/helper-validator-identifier": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.2.tgz", - "integrity": "sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/preset-modules/node_modules/@babel/types": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0.tgz", - "integrity": "sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^8.0.0", - "@babel/helper-validator-identifier": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, "node_modules/@babel/template": { @@ -3085,6 +1726,17 @@ "@jridgewell/trace-mapping": "^0.3.24" } }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -3968,13 +2620,6 @@ "@types/node": "*" } }, - "node_modules/@types/gensync": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/gensync/-/gensync-1.0.5.tgz", - "integrity": "sha512-MbsRCT7mTikHwKZ0X+LVUTLRrZZRLipTuXEO9qOYO+zmjMVk81axyClMROf6uoPD9MRVu46bx8zoR0Ad9q3NAg==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", @@ -3986,13 +2631,6 @@ "@types/node": "*" } }, - "node_modules/@types/jsesc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@types/jsesc/-/jsesc-2.5.1.tgz", - "integrity": "sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/minimatch": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", @@ -4073,21 +2711,46 @@ "dev": true, "license": "MIT" }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", + "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-define-polyfill-provider": "^0.6.8", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-1.0.0.tgz", - "integrity": "sha512-yIkslVjbmml2Xjb6XhFW7lISXHsqk6cesxTdDsXoMom4Lnb99DbD3OQbSOoM5Z+ASh8YXYaLAsRQrU2Jeh3Qig==", + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", + "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^1.0.0", + "@babel/helper-define-polyfill-provider": "^0.6.8", "core-js-compat": "^3.48.0" }, - "engines": { - "node": "^20.19.0 || >=22.12.0" + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", + "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8" }, "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-walk": { @@ -4114,9 +2777,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", - "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", + "version": "2.10.38", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz", + "integrity": "sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4153,9 +2816,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "version": "4.28.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz", + "integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==", "dev": true, "funding": [ { @@ -4173,11 +2836,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" + "baseline-browser-mapping": "^2.10.38", + "caniuse-lite": "^1.0.30001799", + "electron-to-chromium": "^1.5.376", + "node-releases": "^2.0.48", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -4225,9 +2888,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001776", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001776.tgz", - "integrity": "sha512-sg01JDPzZ9jGshqKSckOQthXnYwOEP50jeVFhaSFbZcOy05TiuuaffDOfcwtCisJ9kNQuLBFibYywv2Bgm9osw==", + "version": "1.0.30001799", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", + "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", "dev": true, "funding": [ { @@ -4442,9 +3105,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.307", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.307.tgz", - "integrity": "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg==", + "version": "1.5.377", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.377.tgz", + "integrity": "sha512-cH1jZgJHoezfTnKfKwnScpHywTFVnJUNITDPREFdhNjiuD502+QFpG0Qk7G8jhsV/f+CEAFlIrzP1fT+IMb92g==", "dev": true, "license": "ISC" }, @@ -4455,16 +3118,6 @@ "dev": true, "license": "MIT" }, - "node_modules/empathic": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.1.tgz", - "integrity": "sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - } - }, "node_modules/entities": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", @@ -4879,17 +3532,6 @@ "dev": true, "license": "MIT" }, - "node_modules/import-meta-resolve": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", - "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -5121,13 +3763,13 @@ "license": "MIT" }, "node_modules/lru-cache": { - "version": "11.5.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", - "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" } }, "node_modules/magic-string": { @@ -5242,11 +3884,14 @@ "optional": true }, "node_modules/node-releases": { - "version": "2.0.36", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", - "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "version": "2.0.48", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.48.tgz", + "integrity": "sha512-1uz8041X6LoI6ZSdZacM9lVY28vuzDlSKitnpbSNK0RfKoIJkX29NBPVEFXhnuSuEOA9Ww0xnPJ+ILWbGAv8DA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/object-assign": { "version": "4.1.1", @@ -5258,20 +3903,6 @@ "node": ">=0.10.0" } }, - "node_modules/obug": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", - "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==", - "dev": true, - "funding": [ - "https://github.com/sponsors/sxzz", - "https://opencollective.com/debug" - ], - "license": "MIT", - "engines": { - "node": ">=12.20.0" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5870,16 +4501,13 @@ } }, "node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" } }, "node_modules/serialize-javascript": { @@ -6286,6 +4914,13 @@ "node": ">=10" } }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, "node_modules/yargs": { "version": "18.0.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", diff --git a/package.json b/package.json index a1b61c3d..26a1da3b 100644 --- a/package.json +++ b/package.json @@ -47,9 +47,9 @@ }, "homepage": "https://smarkform.bitifet.net", "devDependencies": { - "@babel/core": "^8.0.1", - "@babel/plugin-proposal-decorators": "^8.0.2", - "@babel/preset-env": "^8.0.2", + "@babel/core": "^7.29.7", + "@babel/plugin-proposal-decorators": "^7.29.7", + "@babel/preset-env": "^7.29.7", "@playwright/test": "^1.61.0", "@rollup/plugin-babel": "^7.1.0", "@rollup/plugin-terser": "^1.0.0", From 71841c585ae65e08e79c99443bacfc24e9fcbb7f Mon Sep 17 00:00:00 2001 From: Joan Miquel Torres Date: Tue, 23 Jun 2026 17:57:56 +0200 Subject: [PATCH 02/85] Sync docs/_data/package.json with root --- docs/_data/package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/_data/package.json b/docs/_data/package.json index 6168e87c..26a1da3b 100644 --- a/docs/_data/package.json +++ b/docs/_data/package.json @@ -50,19 +50,19 @@ "@babel/core": "^7.29.7", "@babel/plugin-proposal-decorators": "^7.29.7", "@babel/preset-env": "^7.29.7", - "@playwright/test": "^1.60.0", - "@rollup/plugin-babel": "^7.0.0", + "@playwright/test": "^1.61.0", + "@rollup/plugin-babel": "^7.1.0", "@rollup/plugin-terser": "^1.0.0", - "concurrently": "^9.2.0", + "concurrently": "^10.0.3", "jsonc": "^2.0.0", "minimatch": "^10.2.5", "parse5": "^8.0.1", "pug": "^3.0.4", - "rollup": "^4.60.4", + "rollup": "^4.62.2", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-copy": "^3.5.0", "rollup-plugin-del": "^1.0.1", - "sass": "^1.100.0" + "sass": "^1.101.0" }, "browserslist": "> 0.25%, not dead", "files": [ From 7221a255f7b855eea6e70482477d765882f3468a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 23:20:18 +0000 Subject: [PATCH 03/85] feat: add mask() method to input field components for masking library integration - Add mask(callback) method to SmarkField (via input.type.js): - Changes type to "text" for non-text inputs (required by iMask.js etc.) - Stores original type in _originalType for reference - Calls callback(targetFieldNode) and stores result as _maskInstance - Delegates to inner field for singletons, returns `me` for chaining - Update _setTargetFieldValue() to dispatch "input" event when mask is active, so masking libraries re-process the new value on programmatic import() - Update export() to use _maskInstance.unmaskedValue when available, so derived types (number, date, etc.) still parse/return the correct semantic type - Add comprehensive tests in test/mask.tests.js (13 scenarios) Agent-Logs-Url: https://github.com/bitifet/SmarkForm/sessions/72c9bcc2-72c5-42a0-8d32-9fe27015b3da Co-authored-by: bitifet <1643647+bitifet@users.noreply.github.com> --- dist/SmarkForm.esm.js | 2 +- dist/SmarkForm.esm.js.map | 2 +- dist/SmarkForm.umd.js | 2 +- dist/SmarkForm.umd.js.map | 2 +- src/types/input.type.js | 30 +++ test/mask.tests.js | 382 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 416 insertions(+), 4 deletions(-) create mode 100644 test/mask.tests.js diff --git a/dist/SmarkForm.esm.js b/dist/SmarkForm.esm.js index 868e504b..b3ab2e0c 100644 --- a/dist/SmarkForm.esm.js +++ b/dist/SmarkForm.esm.js @@ -1 +1 @@ -function e(e,t,n){if("function"==typeof e?e===t:e.has(t))return arguments.length<3?t:n;throw new TypeError("Private element is not present on this object")}function t(e){if(Object(e)!==e)throw TypeError("right-hand side of 'in' should be an object, got "+(null!==e?typeof e:"null"));return e}function n(e,t){(function(e,t){if(t.has(e))throw new TypeError("Cannot initialize the same private elements twice on an object")})(e,t),t.add(e)}function r(e,t,n){return(t=l(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0;E--){var _;void 0!==(b=r(w[E],o,f,u,l,c,d,h,p))&&(i(l,b),0===l?_=b:1===l?(_=b.init,v=b.get||h.get,N=b.set||h.set,h={get:v,set:N}):h=b,void 0!==_&&(void 0===g?g=_:"function"==typeof g?g=[g,_]:g.push(_)))}if(0===l||1===l){if(void 0===g)g=function(e,t){return t};else if("function"!=typeof g){var S=g;g=function(e,t){for(var n=t,r=0;r3,v=h>=5,N=r;if(v?(g=e,0!=(h-=5)&&(m=i=i||[]),b&&!a&&(a=function(n){return t(n)===e}),N=a):(g=e.prototype,0!==h&&(m=o=o||[])),0!==h&&!b){var w=v?d:l,E=w.get(y)||0;if(!0===E||3===E&&4!==h||4===E&&3!==h)throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: "+y);!E&&h>2?w.set(y,h):w.set(y,!0)}c(s,g,f,y,h,v,b,m,N)}}return u(s,o),u(s,i),s}function u(e,t){t&&e.push(function(e){for(var n=0;n0){for(var r=[],o=t,a=t.name,s=n.length-1;s>=0;s--){var l={v:!1};try{var c=n[s](o,{kind:"class",name:a,addInitializer:e(r,l)})}finally{l.v=!0}void 0!==c&&(i(10,c),o=c)}return[o,function(){for(var e=0;e=0;t--)r.push(e.children[t]);for(;r.length;){const e=r.pop();if(e.matches(t))n.push(e);else for(let t=e.children.length-1;t>=0;t--)r.push(e.children[t])}return n}function u(e,t){let n=e.parentNode;const r=t>=0?1:-1;for(;n;){if(n.scrollHeight>n.clientHeight*r){var o=n.scrollHeight-n.clientHeight*r;if(t<=o*r)return void(n.scrollTop+=t);n.scrollTop=o,t-=o}n=n.parentNode}}function p(){return Math.random().toString(36).substring(2)}function f(e){try{return JSON.parse(e)}catch(e){}}function g(e){if(5===e.length&&":"===e[2]){const t=parseInt(e.substring(0,2),10),n=parseInt(e.substring(3,5),10);if(t>=0&&t<=23&&n>=0&&n<=59)return e+":00"}if(8===e.length&&":"===e[2]&&":"===e[5]){const t=parseInt(e.substring(0,2),10),n=parseInt(e.substring(3,5),10),r=parseInt(e.substring(6,8),10);if(t>=0&&t<=23&&n>=0&&n<=59&&r>=0&&r<=59)return e}if(6===e.length){const t=parseInt(e.substring(0,2),10),n=parseInt(e.substring(2,4),10),r=parseInt(e.substring(4,6),10);if(t>=0&&t<=23&&n>=0&&n<=59&&r>=0&&r<=59)return[e.substring(0,2),e.substring(2,4),e.substring(4,6)].join(":")}if(4===e.length){const t=parseInt(e.substring(0,2),10),n=parseInt(e.substring(2,4),10);if(t>=0&&t<=23&&n>=0&&n<=59)return[e.substring(0,2),e.substring(2,4),"00"].join(":")}return null}function m(e){if(15===e.length&&"T"===e[8]){const t=[e.substring(0,4),e.substring(4,6),e.substring(6,8)].join("-"),n=[e.substring(9,11),e.substring(11,13),e.substring(13,15)].join(":");return new Date(`${t}T${n}`)}if(13===e.length&&"T"===e[8]){const t=[e.substring(0,4),e.substring(4,6),e.substring(6,8)].join("-"),n=[e.substring(9,11),e.substring(11,13),"00"].join(":");return new Date(`${t}T${n}`)}if(19===e.length&&"-"===e[4]&&"-"===e[7]&&"T"===e[10]&&":"===e[13]&&":"===e[16])return new Date(e);if(16===e.length&&"-"===e[4]&&"-"===e[7]&&"T"===e[10]&&":"===e[13])return new Date(e+":00");return e.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?$/)?new Date(e):NaN}function h(e){let t=null==e?void 0:e.parentElement;for(;t;){if("DETAILS"===t.tagName&&!t.open){const n=t.querySelector(":scope > summary");if(!n||!n.contains(e))return!0}t=t.parentElement}return!1}function y(e,t,n,r){const o=e.tagName,i=e.getAttribute("type");if("INPUT"!=o||(i||t).toLowerCase()!=t){const e=new Error(r);throw e.code=n,e}i||(e.type=t)}const b=Symbol("Events"),v=Symbol("onEvents"),N=Symbol("allEvents"),w=/^on(?:Before|After)Action_/,E=/^onLocal_/,_=/^on_/,S=/^onAll_/,x={keydown:{bubbles:!0},keyup:{bubbles:!0},keypress:{bubbles:!0},beforeinput:{bubbles:!0},input:{bubbles:!0},change:{bubbles:!0},focus:{bubbles:!1},blur:{bubbles:!1},focusin:{bubbles:!0},focusout:{bubbles:!0},click:{bubbles:!0},dblclick:{bubbles:!0},contextmenu:{bubbles:!0},mousedown:{bubbles:!0},mouseup:{bubbles:!0},mousemove:{bubbles:!0},mouseenter:{bubbles:!1},mouseleave:{bubbles:!1},mouseover:{bubbles:!0},mouseout:{bubbles:!0},focusenter:{bubbles:!0,synthetic:!0},focusleave:{bubbles:!0,synthetic:!0}};function T(e,t,n){return e.has(t)||e.set(t,[]),e.get(t).push(n.bind(this)),this}async function k(e,t){const n=e?[e,...e.parents]:[],r=t?[t,...t.parents]:[],o=function(e,t){var n;const r=new Set(t);return null!==(n=e.find(e=>r.has(e)))&&void 0!==n?n:null}(n,r);for(const e of n){if(e===o)break;await e.emit("focusleave",{type:"focusleave",context:e})}for(const e of r){if(e===o)break;await e.emit("focusenter",{type:"focusenter",context:e})}}const A=Symbol("smarkform_legacy_prevent");var I={disEnhance(e){"form"!==e.targetNode.tagName.toLowerCase()||e.targetNode[A]||(e.targetNode[A]=!0,e.targetNode.addEventListener("submit",function(e){e.preventDefault()}))}};const O=["type"],L=new Map,C=new Set;function D(e){const t=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT);let n=t.currentNode;for(;n;)n.hasAttribute("id")&&(n.setAttribute("data-id",n.getAttribute("id")),n.removeAttribute("id")),n=t.nextNode()}function P(e){try{return new URL(e).origin!==location.origin}catch(e){return!1}}function j(e){try{return new URL(e).origin}catch(e){return""}}function F(e,t,n){if("string"==typeof e)return e;if(null!==e&&"object"==typeof e){if(Object.prototype.hasOwnProperty.call(e,t))return e[t];if(Object.prototype.hasOwnProperty.call(e,"*"))return e["*"]}return n}async function $(e,t,n){const r=t.type,o=r.indexOf("#"),s=r.slice(0,o),l=r.slice(o+1);if(!l)throw n.renderError("MIXIN_TYPE_MISSING_FRAGMENT",`Mixin type reference "${r}" must include a non-empty #templateId fragment.`,e);let c,d;if(s){var u;d=new URL(s,document.baseURI).href;const t=null!==(u=n.root.options.allowExternalMixins)&&void 0!==u?u:"block",o=j(d),i=F(t,o,"block");if("block"===i)throw n.renderError("MIXIN_EXTERNAL_FETCH_BLOCKED",`Mixin type "${r}" references an external URL but allowExternalMixins is "block" (the default). Set allowExternalMixins to "same-origin", "allow", or a per-origin policy object on the root SmarkForm instance to permit external mixin loading.`,e);if("same-origin"===i&&P(d))throw n.renderError("MIXIN_CROSS_ORIGIN_FETCH_BLOCKED",`Mixin type "${r}" references a cross-origin URL (${o}) but allowExternalMixins is "same-origin". Set allowExternalMixins to "allow" or add the origin to the per-origin policy object to permit cross-origin mixin loading.`,e);L.has(d)||L.set(d,fetch(d).then(e=>{if(!e.ok)throw Object.assign(new Error(`Failed to fetch mixin source: ${d} (HTTP ${e.status})`),{code:"MIXIN_FETCH_ERROR"});return e.text()}).then(e=>(new DOMParser).parseFromString(e,"text/html"))),c=await L.get(d)}else c=document,d=document.baseURI;const p=`${d}#${l}`,g=n._mixinChain||new Set;if(g.has(p))throw n.renderError("MIXIN_CIRCULAR_DEPENDENCY",`Circular mixin dependency detected: "${p}" is already being expanded in this rendering chain.`,e);const m=c.getElementById(l);if(!m||"template"!==m.tagName.toLowerCase())throw n.renderError("MIXIN_TEMPLATE_NOT_FOUND",`Mixin template #${l} not found in ${s||"the current document"}.`,e);const h=[...m.content.childNodes].filter(e=>e.nodeType===Node.ELEMENT_NODE),y=h.filter(e=>"script"===e.tagName.toLowerCase()),b=h.filter(e=>"style"===e.tagName.toLowerCase()),v=h.filter(e=>{const t=e.tagName.toLowerCase();return"script"!==t&&"style"!==t});if(1!==v.length)throw n.renderError("MIXIN_TEMPLATE_INVALID_ROOT",`Mixin template #${l} must contain exactly one root element (found ${v.length}).`,e);const N=v[0],w=f(N.getAttribute("data-smark"))||{};if(void 0!==w.name)throw n.renderError("MIXIN_TEMPLATE_ROOT_HAS_NAME",`Mixin template #${l} root element must not specify a "name" in its data-smark options. The name must be set on the placeholder (usage site).`,e);const E=N.cloneNode(!0),_=[...e.children].filter(e=>e.hasAttribute("data-for"));_.length>0&&function(e,t){for(const n of t){const t=n.getAttribute("data-for"),r=e.querySelector(`[id="${CSS.escape(t)}"]`);if(!r)continue;const o=n.cloneNode(!0);D(o),o.removeAttribute("data-for"),r.replaceWith(o)}}(E,_),function(e,t,n){if(e.querySelector("script"))throw t.renderError("MIXIN_NESTED_SCRIPT_DISALLOWED","Mixin template root subtree must not contain \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested + + +
+ + + + +``` \ No newline at end of file From 3f7637dd6c4480b5b709aaac5c7b826f5aec2955 Mon Sep 17 00:00:00 2001 From: Joan Miquel Torres Date: Mon, 27 Apr 2026 18:19:30 +0200 Subject: [PATCH 05/85] refactor: make input masking permanent, update docs and tests accordingly --- AGENTS.md | 2 +- dist/SmarkForm.esm.js | 2 +- dist/SmarkForm.esm.js.map | 2 +- dist/SmarkForm.umd.js | 2 +- dist/SmarkForm.umd.js.map | 2 +- docs/_advanced_concepts/field_masking.md | 13 ++----------- src/types/input.type.js | 1 - test/mask.tests.js | 9 +++------ 8 files changed, 10 insertions(+), 23 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 5a88be5e..18db91c8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -58,7 +58,7 @@ The `mask(callback)` method (see `src/types/input.type.js`) enables integration - `export()`: Returns `_maskInstance.unmaskedValue` when available, else `nodeFld.value` - `import()`: Dispatches `input` event when `_maskInstance` exists, so masks stay synchronized - Singleton handling: delegates to inner field, `_maskInstance` lives on inner field -- `_originalType` restoration: original input type is restored when field is destroyed +- Masking is permanent: the field's input type is set to text and not restored; this is intentional and simplifies the implementation **Configuration file locations**: - Tests: `test/mask.tests.js` diff --git a/dist/SmarkForm.esm.js b/dist/SmarkForm.esm.js index b3ab2e0c..1f34fd0e 100644 --- a/dist/SmarkForm.esm.js +++ b/dist/SmarkForm.esm.js @@ -1 +1 @@ -function e(e,t,n){if("function"==typeof e?e===t:e.has(t))return arguments.length<3?t:n;throw new TypeError("Private element is not present on this object")}function t(e){if(Object(e)!==e)throw TypeError("right-hand side of 'in' should be an object, got "+(null!==e?typeof e:"null"));return e}function n(e,t){(function(e,t){if(t.has(e))throw new TypeError("Cannot initialize the same private elements twice on an object")})(e,t),t.add(e)}function r(e,t,n){return(t=l(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0;E--){var _;void 0!==(b=r(w[E],o,f,u,l,c,d,h,p))&&(i(l,b),0===l?_=b:1===l?(_=b.init,v=b.get||h.get,N=b.set||h.set,h={get:v,set:N}):h=b,void 0!==_&&(void 0===g?g=_:"function"==typeof g?g=[g,_]:g.push(_)))}if(0===l||1===l){if(void 0===g)g=function(e,t){return t};else if("function"!=typeof g){var S=g;g=function(e,t){for(var n=t,r=0;r3,v=h>=5,N=r;if(v?(g=e,0!=(h-=5)&&(m=i=i||[]),b&&!a&&(a=function(n){return t(n)===e}),N=a):(g=e.prototype,0!==h&&(m=o=o||[])),0!==h&&!b){var w=v?d:l,E=w.get(y)||0;if(!0===E||3===E&&4!==h||4===E&&3!==h)throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: "+y);!E&&h>2?w.set(y,h):w.set(y,!0)}c(s,g,f,y,h,v,b,m,N)}}return u(s,o),u(s,i),s}function u(e,t){t&&e.push(function(e){for(var n=0;n0){for(var r=[],o=t,a=t.name,s=n.length-1;s>=0;s--){var l={v:!1};try{var c=n[s](o,{kind:"class",name:a,addInitializer:e(r,l)})}finally{l.v=!0}void 0!==c&&(i(10,c),o=c)}return[o,function(){for(var e=0;e=0;t--)r.push(e.children[t]);for(;r.length;){const e=r.pop();if(e.matches(t))n.push(e);else for(let t=e.children.length-1;t>=0;t--)r.push(e.children[t])}return n}function u(e,t){let n=e.parentNode;const r=t>=0?1:-1;for(;n;){if(n.scrollHeight>n.clientHeight*r){var o=n.scrollHeight-n.clientHeight*r;if(t<=o*r)return void(n.scrollTop+=t);n.scrollTop=o,t-=o}n=n.parentNode}}function p(){return Math.random().toString(36).substring(2)}function f(e){try{return JSON.parse(e)}catch(e){}}function g(e){if(5===e.length&&":"===e[2]){const t=parseInt(e.substring(0,2),10),n=parseInt(e.substring(3,5),10);if(t>=0&&t<=23&&n>=0&&n<=59)return e+":00"}if(8===e.length&&":"===e[2]&&":"===e[5]){const t=parseInt(e.substring(0,2),10),n=parseInt(e.substring(3,5),10),r=parseInt(e.substring(6,8),10);if(t>=0&&t<=23&&n>=0&&n<=59&&r>=0&&r<=59)return e}if(6===e.length){const t=parseInt(e.substring(0,2),10),n=parseInt(e.substring(2,4),10),r=parseInt(e.substring(4,6),10);if(t>=0&&t<=23&&n>=0&&n<=59&&r>=0&&r<=59)return[e.substring(0,2),e.substring(2,4),e.substring(4,6)].join(":")}if(4===e.length){const t=parseInt(e.substring(0,2),10),n=parseInt(e.substring(2,4),10);if(t>=0&&t<=23&&n>=0&&n<=59)return[e.substring(0,2),e.substring(2,4),"00"].join(":")}return null}function m(e){if(15===e.length&&"T"===e[8]){const t=[e.substring(0,4),e.substring(4,6),e.substring(6,8)].join("-"),n=[e.substring(9,11),e.substring(11,13),e.substring(13,15)].join(":");return new Date(`${t}T${n}`)}if(13===e.length&&"T"===e[8]){const t=[e.substring(0,4),e.substring(4,6),e.substring(6,8)].join("-"),n=[e.substring(9,11),e.substring(11,13),"00"].join(":");return new Date(`${t}T${n}`)}if(19===e.length&&"-"===e[4]&&"-"===e[7]&&"T"===e[10]&&":"===e[13]&&":"===e[16])return new Date(e);if(16===e.length&&"-"===e[4]&&"-"===e[7]&&"T"===e[10]&&":"===e[13])return new Date(e+":00");return e.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?$/)?new Date(e):NaN}function h(e){let t=null==e?void 0:e.parentElement;for(;t;){if("DETAILS"===t.tagName&&!t.open){const n=t.querySelector(":scope > summary");if(!n||!n.contains(e))return!0}t=t.parentElement}return!1}function y(e,t,n,r){const o=e.tagName,i=e.getAttribute("type");if("INPUT"!=o||(i||t).toLowerCase()!=t){const e=new Error(r);throw e.code=n,e}i||(e.type=t)}const b=Symbol("Events"),v=Symbol("onEvents"),N=Symbol("allEvents"),w=/^on(?:Before|After)Action_/,E=/^onLocal_/,_=/^on_/,S=/^onAll_/,x={keydown:{bubbles:!0},keyup:{bubbles:!0},keypress:{bubbles:!0},beforeinput:{bubbles:!0},input:{bubbles:!0},change:{bubbles:!0},focus:{bubbles:!1},blur:{bubbles:!1},focusin:{bubbles:!0},focusout:{bubbles:!0},click:{bubbles:!0},dblclick:{bubbles:!0},contextmenu:{bubbles:!0},mousedown:{bubbles:!0},mouseup:{bubbles:!0},mousemove:{bubbles:!0},mouseenter:{bubbles:!1},mouseleave:{bubbles:!1},mouseover:{bubbles:!0},mouseout:{bubbles:!0},focusenter:{bubbles:!0,synthetic:!0},focusleave:{bubbles:!0,synthetic:!0}};function T(e,t,n){return e.has(t)||e.set(t,[]),e.get(t).push(n.bind(this)),this}async function k(e,t){const n=e?[e,...e.parents]:[],r=t?[t,...t.parents]:[],o=function(e,t){var n;const r=new Set(t);return null!==(n=e.find(e=>r.has(e)))&&void 0!==n?n:null}(n,r);for(const e of n){if(e===o)break;await e.emit("focusleave",{type:"focusleave",context:e})}for(const e of r){if(e===o)break;await e.emit("focusenter",{type:"focusenter",context:e})}}const I=Symbol("smarkform_legacy_prevent");var A={disEnhance(e){"form"!==e.targetNode.tagName.toLowerCase()||e.targetNode[I]||(e.targetNode[I]=!0,e.targetNode.addEventListener("submit",function(e){e.preventDefault()}))}};const O=["type"],L=new Map,C=new Set;function D(e){const t=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT);let n=t.currentNode;for(;n;)n.hasAttribute("id")&&(n.setAttribute("data-id",n.getAttribute("id")),n.removeAttribute("id")),n=t.nextNode()}function P(e){try{return new URL(e).origin!==location.origin}catch(e){return!1}}function j(e){try{return new URL(e).origin}catch(e){return""}}function F(e,t,n){if("string"==typeof e)return e;if(null!==e&&"object"==typeof e){if(Object.prototype.hasOwnProperty.call(e,t))return e[t];if(Object.prototype.hasOwnProperty.call(e,"*"))return e["*"]}return n}async function $(e,t,n){const r=t.type,o=r.indexOf("#"),s=r.slice(0,o),l=r.slice(o+1);if(!l)throw n.renderError("MIXIN_TYPE_MISSING_FRAGMENT",`Mixin type reference "${r}" must include a non-empty #templateId fragment.`,e);let c,d;if(s){var u;d=new URL(s,document.baseURI).href;const t=null!==(u=n.root.options.allowExternalMixins)&&void 0!==u?u:"block",o=j(d),i=F(t,o,"block");if("block"===i)throw n.renderError("MIXIN_EXTERNAL_FETCH_BLOCKED",`Mixin type "${r}" references an external URL but allowExternalMixins is "block" (the default). Set allowExternalMixins to "same-origin", "allow", or a per-origin policy object on the root SmarkForm instance to permit external mixin loading.`,e);if("same-origin"===i&&P(d))throw n.renderError("MIXIN_CROSS_ORIGIN_FETCH_BLOCKED",`Mixin type "${r}" references a cross-origin URL (${o}) but allowExternalMixins is "same-origin". Set allowExternalMixins to "allow" or add the origin to the per-origin policy object to permit cross-origin mixin loading.`,e);L.has(d)||L.set(d,fetch(d).then(e=>{if(!e.ok)throw Object.assign(new Error(`Failed to fetch mixin source: ${d} (HTTP ${e.status})`),{code:"MIXIN_FETCH_ERROR"});return e.text()}).then(e=>(new DOMParser).parseFromString(e,"text/html"))),c=await L.get(d)}else c=document,d=document.baseURI;const p=`${d}#${l}`,g=n._mixinChain||new Set;if(g.has(p))throw n.renderError("MIXIN_CIRCULAR_DEPENDENCY",`Circular mixin dependency detected: "${p}" is already being expanded in this rendering chain.`,e);const m=c.getElementById(l);if(!m||"template"!==m.tagName.toLowerCase())throw n.renderError("MIXIN_TEMPLATE_NOT_FOUND",`Mixin template #${l} not found in ${s||"the current document"}.`,e);const h=[...m.content.childNodes].filter(e=>e.nodeType===Node.ELEMENT_NODE),y=h.filter(e=>"script"===e.tagName.toLowerCase()),b=h.filter(e=>"style"===e.tagName.toLowerCase()),v=h.filter(e=>{const t=e.tagName.toLowerCase();return"script"!==t&&"style"!==t});if(1!==v.length)throw n.renderError("MIXIN_TEMPLATE_INVALID_ROOT",`Mixin template #${l} must contain exactly one root element (found ${v.length}).`,e);const N=v[0],w=f(N.getAttribute("data-smark"))||{};if(void 0!==w.name)throw n.renderError("MIXIN_TEMPLATE_ROOT_HAS_NAME",`Mixin template #${l} root element must not specify a "name" in its data-smark options. The name must be set on the placeholder (usage site).`,e);const E=N.cloneNode(!0),_=[...e.children].filter(e=>e.hasAttribute("data-for"));_.length>0&&function(e,t){for(const n of t){const t=n.getAttribute("data-for"),r=e.querySelector(`[id="${CSS.escape(t)}"]`);if(!r)continue;const o=n.cloneNode(!0);D(o),o.removeAttribute("data-for"),r.replaceWith(o)}}(E,_),function(e,t,n){if(e.querySelector("script"))throw t.renderError("MIXIN_NESTED_SCRIPT_DISALLOWED","Mixin template root subtree must not contain \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested +

@@ -32,30 +37,46 @@ A credit card field demonstrates spaces between every 4 digits β€” a pattern fam

-``` - -```javascript -const form = new SmarkForm('#payment'); -await form.rendered; - -const cardField = form.find('/cardNumber'); -cardField.mask(node => { - // Using iMask.js (loaded via CDN): https://cdn.jsdelivr.net/npm/imask - return new IMask(node, { - mask: '0000 0000 0000 0000', - blocks: { - '0000': {mask: IMask.Number} - } - }); +{%- endcapture %}{% raw %}{% endraw %} + +{% raw %}{% endraw %} +{% capture mask_cc_js -%} +const form = new SmarkForm("#payment"); +form.rendered.then(async () => { + const cardField = form.find("/cardNumber"); + cardField.mask(node => { + // Using iMask.js (loaded via CDN): https://cdn.jsdelivr.net/npm/imask + return new IMask(node, { + mask: "0000 0000 0000 0000", + blocks: { + "0000": {mask: IMask.Number} + } + }); + }); }); -``` +{%- endcapture %}{% raw %}{% endraw %} + +{% raw %}{% endraw %} +{% capture mask_cc_notes -%} +**Try it!** Edit the code and see it live. You can change the mask or placeholder to experiment. +{%- endcapture %}{% raw %}{% endraw %} + +{% include components/sampletabs_tpl.md + formId="mask-cc" + htmlSource=mask_cc_html + jsHead=mask_cc_js + notes=mask_cc_notes + showEditor=true +%} ## Price Example (Non-Text Field Type) This shows how to use a `number` field type β€” SmarkForm converts it to `text` for masking but exports a proper JavaScript number. -```html -
+{% raw %}{% endraw %} +{% capture mask_price_html -%} + +

@@ -64,75 +85,125 @@ This shows how to use a `number` field type β€” SmarkForm converts it to `text`

-``` - -```javascript -const form = new SmarkForm('#product'); -await form.rendered; - -const priceField = form.find('/price'); -priceField.mask(node => { - const imask = new IMask(node, { - mask: Number, - scale: 100, // 2 decimal places - thousandsSeparator: ' ' // Add space separator every 3 digits - }); - // Export returns a proper JavaScript number, not a formatted string - return imask; +{%- endcapture %}{% raw %}{% endraw %} + +{% raw %}{% endraw %} +{% capture mask_price_js -%} +const form = new SmarkForm("#product"); +form.rendered.then(async () => { + const priceField = form.find("/price"); + priceField.mask(node => { + const imask = new IMask(node, { + mask: Number, + scale: 100, // 2 decimal places + thousandsSeparator: " " // Add space separator every 3 digits + }); + // Export returns a proper JavaScript number, not a formatted string + return imask; + }); }); -``` +{%- endcapture %}{% raw %}{% endraw %} + +{% raw %}{% endraw %} +{% capture mask_price_notes -%} +**Try it!** Change the mask options (for example, scale for decimals) and see the export value change accordingly. +{%- endcapture %}{% raw %}{% endraw %} + +{% include components/sampletabs_tpl.md + formId="mask-price" + htmlSource=mask_price_html + jsHead=mask_price_js + notes=mask_price_notes + showEditor=true +%} ## Singleton Masking When masking is applied to a singleton component (e.g., a number field wrapped in a type converter), the method delegates to the inner field. The `_maskInstance` lives on the inner field while the outer component remains chainable. -```html -
+{% raw %}{% endraw %} +{% capture mask_singleton_html -%} + +
-``` +{%- endcapture %}{% raw %}{% endraw %} + +{% raw %}{% endraw %} +{% capture mask_singleton_js -%} +const form = new SmarkForm("#singleton"); +form.rendered.then(async () => { + // Mask on the singleton β€” returns the outer component for chaining + const quantityField = form.find("/quantity"); + quantityField.mask(node => new IMask(node, { mask: "0[.]00" })); + // The inner field has the _maskInstance + const innerField = quantityField.children[""]; + console.log("Inner field has mask:", innerField._maskInstance !== undefined); + console.log("Outer field has mask:", quantityField._maskInstance === undefined); +}); +{%- endcapture %}{% raw %}{% endraw %} -```javascript -const form = new SmarkForm('div'); -await form.rendered; - -// Mask on the singleton β€” returns the outer component for chaining -const quantityField = form.find('/quantity'); -quantityField.mask(node => new IMask(node, { - mask: '0[.]00' -})); - -// The inner field has the _maskInstance -const innerField = quantityField.children['']; -console.log(innerField._maskInstance !== undefined); // true -console.log(quantityField._maskInstance === undefined); // true -``` +{% raw %}{% endraw %} +{% capture mask_singleton_notes -%} +**Try it!** This playground demonstrates singleton masking: edit the JS to check the instance locations and try different masks. +{%- endcapture %}{% raw %}{% endraw %} + +{% include components/sampletabs_tpl.md + formId="mask-singleton" + htmlSource=mask_singleton_html + jsHead=mask_singleton_js + notes=mask_singleton_notes + showEditor=true +%} ## Validation Many masking libraries include built-in validation. You can integrate it by checking validity before accepting values: -```javascript -priceField.mask(node => { - const imask = new IMask(node, { - mask: Number, - scale: 100, - thousandsSeparator: ' ' - }); - - // Listen for validation changes - node.addEventListener('accept', (e) => { - const input = e.target; - input.setCustomValidity(imask.isValid ? '' : 'Please enter a valid price'); - }); - - return imask; +{% raw %}{% endraw %} +{% capture mask_validation_html -%} + +
+

+ + +

+

+ +

+
+{%- endcapture %}{% raw %}{% endraw %} + +{% raw %}{% endraw %} +{% capture mask_validation_js -%} +const form = new SmarkForm("#pricevalidation"); +form.rendered.then(async () => { + const priceField = form.find("/price"); + const imask = new IMask(priceField.targetFieldNode, { + mask: Number, + scale: 100, + thousandsSeparator: " " + }); + priceField._maskInstance = imask; + priceField.targetFieldNode.addEventListener("accept", (e) => { + const input = e.target; + input.setCustomValidity(imask.masked.isValid ? "" : "Please enter a valid price"); + }); }); -``` +{%- endcapture %}{% raw %}{% endraw %} + +{% raw %}{% endraw %} +{% capture mask_validation_notes -%} +**Try it!** The playground below highlights invalid price input using a custom validation message (try entering a non-numeric value). +{%- endcapture %}{% raw %}{% endraw %} + +{% include components/sampletabs_tpl.md + formId="mask-validation" + htmlSource=mask_validation_html + jsHead=mask_validation_js + notes=mask_validation_notes + showEditor=true +%} ## Masking is Permanent @@ -140,36 +211,14 @@ When a mask converts an input to `text`, that change is permanent for the lifeti ## Complete Example with CDN +> Try the playgrounds above to see the key features. For production usage with your own CDN, use: + ```html - - - - - - -
- - - - -``` \ No newline at end of file + + + +``` + +```javascript +// See above for field setup! Only IMask and SmarkForm loading differs for CDN usage. +``` diff --git a/src/lib/component.js b/src/lib/component.js index 41877c1b..72fdebf9 100644 --- a/src/lib/component.js +++ b/src/lib/component.js @@ -252,7 +252,51 @@ export class SmarkComponent { };//}}} setNodeOptions(node, options) {//{{{ const me = this; - node.dataset[me.property_name] = JSON.stringify(options); + // Check for non-serializable values before attempting JSON.stringify + function isSerializable(value, path = "", visited = new WeakSet()) { + // Primitive types + if (value === null || typeof value !== "object") { + if (typeof value === "function") { + throw new Error(`Function found at ${path}`); + } + if (typeof value === "symbol") { + throw new Error(`Symbol found at ${path}`); + } + if (typeof value === "undefined") { + throw new Error(`Undefined found at ${path}`); + } + if (typeof value === "number" && (!Number.isFinite(value))) { + throw new Error(`Non-finite number found at ${path}`); + } + return; + } + // Objects: detect circular references + if (visited.has(value)) { + return; // already visited, skip to avoid infinite recursion + } + visited.add(value); + if (Array.isArray(value)) { + value.forEach((item, i) => { + isSerializable(item, `${path}[${i}]`, visited); + }); + } else { + // plain object + for (const key of Object.keys(value)) { + isSerializable(value[key], path ? `${path}.${key}` : key, visited); + } + } + } + try { + isSerializable(options); + node.dataset[me.property_name] = JSON.stringify(options); + } catch (err) { + console.log("Error stringifying options for node", node, "with options", options); + console.log({ me }); + throw me.renderError( + "INVALID_OPTIONS_OBJECT", + `Cannot serialize options for data-${me.property_name} on node: encountered unserializable properties. Original error: ${err && err.message ? err.message : err}` + ); + } };//}}} async safeEnhance(node, defaultOptions) {//{{{ const me = this; From 6a2cf48f798087d63f85e3be1077cc8981ec53d6 Mon Sep 17 00:00:00 2001 From: Joan Miquel Torres Date: Thu, 21 May 2026 23:07:34 +0200 Subject: [PATCH 07/85] test: merge general tests with options validation tests - Restore original general tests (document loaded, focus behavior, default values focus race, basic introspection) - Add new tests for options serialization validation (cyclic refs, functions) - These validate the INVALID_OPTIONS_OBJECT renderError behavior --- test/general.tests.js | 59 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/test/general.tests.js b/test/general.tests.js index 527b66eb..fb558c6f 100644 --- a/test/general.tests.js +++ b/test/general.tests.js @@ -312,3 +312,62 @@ block mainForm }); +test.describe('options validation', () => { + test('throws renderError for unserializable options (cyclic)', async ({ page }) => { + const pug = `div#cyclic-form(data-smark='{"type":"form","name":"c"}')\nscript(src=\"../../dist/SmarkForm.umd.js\")`; + const { url, onClosed } = await renderPug({ title: 'cyclic options', src: pug }); + try { + await page.goto(url); + const result = await page.evaluate(() => { + const SmarkForm = window.SmarkForm; + const node = document.getElementById('cyclic-form'); + const comp = new SmarkForm(node); + comp.options.badCyclic = comp.options; // introduce cyclic reference + try { + comp.setNodeOptions(node, comp.options); + return {error: false}; + } catch (e) { + return { + error: true, + name: e.name, + message: e.message, + code: e.code, + }; + } + }); + expect(result.error).toBe(true); + expect(result.code === "INVALID_OPTIONS_OBJECT" || result.name === "renderError").toBe(true); + } finally { + await onClosed(); + } + }); + + test('throws renderError for unserializable options (function property)', async ({ page }) => { + const pug = `div#func-form(data-smark='{"type":"form","name":"f"}')\nscript(src=\"../../dist/SmarkForm.umd.js\")`; + const { url, onClosed } = await renderPug({ title: 'function option', src: pug }); + try { + await page.goto(url); + const result = await page.evaluate(() => { + const SmarkForm = window.SmarkForm; + const node = document.getElementById('func-form'); + const comp = new SmarkForm(node); + comp.options.badFn = () => 42; + try { + comp.setNodeOptions(node, comp.options); + return {error: false}; + } catch (e) { + return { + error: true, + name: e.name, + message: e.message, + code: e.code, + }; + } + }); + expect(result.error).toBe(true); + expect(result.code === "INVALID_OPTIONS_OBJECT" || result.name === "renderError").toBe(true); + } finally { + await onClosed(); + } + }); +}); From 0c11450ae161ffbab18872b71e1bbf05c490d3cf Mon Sep 17 00:00:00 2001 From: Joan Miquel Torres Date: Fri, 22 May 2026 00:14:28 +0200 Subject: [PATCH 08/85] fix: prevent options serialization validation crash during construction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove 'undefined' check from isSerializable() β€” JSON.stringify silently drops undefined property values, so they are serializable - Guard getPath() against unset parents (called during construction when renderError is thrown before parents is initialized) - Update masking docs: replace 'restores original type' with 'masking is permanent' to match current implementation --- dist/SmarkForm.esm.js | 2 +- dist/SmarkForm.esm.js.map | 2 +- dist/SmarkForm.umd.js | 2 +- dist/SmarkForm.umd.js.map | 2 +- docs/_advanced_concepts/field_masking.md | 4 ++-- src/lib/component.js | 5 +---- 6 files changed, 7 insertions(+), 10 deletions(-) diff --git a/dist/SmarkForm.esm.js b/dist/SmarkForm.esm.js index 1b9211fe..35fbef33 100644 --- a/dist/SmarkForm.esm.js +++ b/dist/SmarkForm.esm.js @@ -1 +1 @@ -function e(e,t,n){if("function"==typeof e?e===t:e.has(t))return arguments.length<3?t:n;throw new TypeError("Private element is not present on this object")}function t(e){if(Object(e)!==e)throw TypeError("right-hand side of 'in' should be an object, got "+(null!==e?typeof e:"null"));return e}function n(e,t){(function(e,t){if(t.has(e))throw new TypeError("Cannot initialize the same private elements twice on an object")})(e,t),t.add(e)}function r(e,t,n){return(t=l(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0;E--){var _;void 0!==(b=r(w[E],o,p,u,l,c,d,h,f))&&(i(l,b),0===l?_=b:1===l?(_=b.init,v=b.get||h.get,N=b.set||h.set,h={get:v,set:N}):h=b,void 0!==_&&(void 0===g?g=_:"function"==typeof g?g=[g,_]:g.push(_)))}if(0===l||1===l){if(void 0===g)g=function(e,t){return t};else if("function"!=typeof g){var S=g;g=function(e,t){for(var n=t,r=0;r3,v=h>=5,N=r;if(v?(g=e,0!=(h-=5)&&(m=i=i||[]),b&&!a&&(a=function(n){return t(n)===e}),N=a):(g=e.prototype,0!==h&&(m=o=o||[])),0!==h&&!b){var w=v?d:l,E=w.get(y)||0;if(!0===E||3===E&&4!==h||4===E&&3!==h)throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: "+y);!E&&h>2?w.set(y,h):w.set(y,!0)}c(s,g,p,y,h,v,b,m,N)}}return u(s,o),u(s,i),s}function u(e,t){t&&e.push(function(e){for(var n=0;n0){for(var r=[],o=t,a=t.name,s=n.length-1;s>=0;s--){var l={v:!1};try{var c=n[s](o,{kind:"class",name:a,addInitializer:e(r,l)})}finally{l.v=!0}void 0!==c&&(i(10,c),o=c)}return[o,function(){for(var e=0;e=0;t--)r.push(e.children[t]);for(;r.length;){const e=r.pop();if(e.matches(t))n.push(e);else for(let t=e.children.length-1;t>=0;t--)r.push(e.children[t])}return n}function u(e,t){let n=e.parentNode;const r=t>=0?1:-1;for(;n;){if(n.scrollHeight>n.clientHeight*r){var o=n.scrollHeight-n.clientHeight*r;if(t<=o*r)return void(n.scrollTop+=t);n.scrollTop=o,t-=o}n=n.parentNode}}function f(){return Math.random().toString(36).substring(2)}function p(e){try{return JSON.parse(e)}catch(e){}}function g(e){if(5===e.length&&":"===e[2]){const t=parseInt(e.substring(0,2),10),n=parseInt(e.substring(3,5),10);if(t>=0&&t<=23&&n>=0&&n<=59)return e+":00"}if(8===e.length&&":"===e[2]&&":"===e[5]){const t=parseInt(e.substring(0,2),10),n=parseInt(e.substring(3,5),10),r=parseInt(e.substring(6,8),10);if(t>=0&&t<=23&&n>=0&&n<=59&&r>=0&&r<=59)return e}if(6===e.length){const t=parseInt(e.substring(0,2),10),n=parseInt(e.substring(2,4),10),r=parseInt(e.substring(4,6),10);if(t>=0&&t<=23&&n>=0&&n<=59&&r>=0&&r<=59)return[e.substring(0,2),e.substring(2,4),e.substring(4,6)].join(":")}if(4===e.length){const t=parseInt(e.substring(0,2),10),n=parseInt(e.substring(2,4),10);if(t>=0&&t<=23&&n>=0&&n<=59)return[e.substring(0,2),e.substring(2,4),"00"].join(":")}return null}function m(e){if(15===e.length&&"T"===e[8]){const t=[e.substring(0,4),e.substring(4,6),e.substring(6,8)].join("-"),n=[e.substring(9,11),e.substring(11,13),e.substring(13,15)].join(":");return new Date(`${t}T${n}`)}if(13===e.length&&"T"===e[8]){const t=[e.substring(0,4),e.substring(4,6),e.substring(6,8)].join("-"),n=[e.substring(9,11),e.substring(11,13),"00"].join(":");return new Date(`${t}T${n}`)}if(19===e.length&&"-"===e[4]&&"-"===e[7]&&"T"===e[10]&&":"===e[13]&&":"===e[16])return new Date(e);if(16===e.length&&"-"===e[4]&&"-"===e[7]&&"T"===e[10]&&":"===e[13])return new Date(e+":00");return e.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?$/)?new Date(e):NaN}function h(e){let t=null==e?void 0:e.parentElement;for(;t;){if("DETAILS"===t.tagName&&!t.open){const n=t.querySelector(":scope > summary");if(!n||!n.contains(e))return!0}t=t.parentElement}return!1}function y(e,t,n,r){const o=e.tagName,i=e.getAttribute("type");if("INPUT"!=o||(i||t).toLowerCase()!=t){const e=new Error(r);throw e.code=n,e}i||(e.type=t)}const b=Symbol("Events"),v=Symbol("onEvents"),N=Symbol("allEvents"),w=/^on(?:Before|After)Action_/,E=/^onLocal_/,_=/^on_/,S=/^onAll_/,x={keydown:{bubbles:!0},keyup:{bubbles:!0},keypress:{bubbles:!0},beforeinput:{bubbles:!0},input:{bubbles:!0},change:{bubbles:!0},focus:{bubbles:!1},blur:{bubbles:!1},focusin:{bubbles:!0},focusout:{bubbles:!0},click:{bubbles:!0},dblclick:{bubbles:!0},contextmenu:{bubbles:!0},mousedown:{bubbles:!0},mouseup:{bubbles:!0},mousemove:{bubbles:!0},mouseenter:{bubbles:!1},mouseleave:{bubbles:!1},mouseover:{bubbles:!0},mouseout:{bubbles:!0},focusenter:{bubbles:!0,synthetic:!0},focusleave:{bubbles:!0,synthetic:!0}};function T(e,t,n){return e.has(t)||e.set(t,[]),e.get(t).push(n.bind(this)),this}async function k(e,t){const n=e?[e,...e.parents]:[],r=t?[t,...t.parents]:[],o=function(e,t){var n;const r=new Set(t);return null!==(n=e.find(e=>r.has(e)))&&void 0!==n?n:null}(n,r);for(const e of n){if(e===o)break;await e.emit("focusleave",{type:"focusleave",context:e})}for(const e of r){if(e===o)break;await e.emit("focusenter",{type:"focusenter",context:e})}}const I=Symbol("smarkform_legacy_prevent");var A={disEnhance(e){"form"!==e.targetNode.tagName.toLowerCase()||e.targetNode[I]||(e.targetNode[I]=!0,e.targetNode.addEventListener("submit",function(e){e.preventDefault()}))}};const O=["type"],L=new Map,C=new Set;function D(e){const t=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT);let n=t.currentNode;for(;n;)n.hasAttribute("id")&&(n.setAttribute("data-id",n.getAttribute("id")),n.removeAttribute("id")),n=t.nextNode()}function P(e){try{return new URL(e).origin!==location.origin}catch(e){return!1}}function j(e){try{return new URL(e).origin}catch(e){return""}}function $(e,t,n){if("string"==typeof e)return e;if(null!==e&&"object"==typeof e){if(Object.prototype.hasOwnProperty.call(e,t))return e[t];if(Object.prototype.hasOwnProperty.call(e,"*"))return e["*"]}return n}async function F(e,t,n){const r=t.type,o=r.indexOf("#"),s=r.slice(0,o),l=r.slice(o+1);if(!l)throw n.renderError("MIXIN_TYPE_MISSING_FRAGMENT",`Mixin type reference "${r}" must include a non-empty #templateId fragment.`,e);let c,d;if(s){var u;d=new URL(s,document.baseURI).href;const t=null!==(u=n.root.options.allowExternalMixins)&&void 0!==u?u:"block",o=j(d),i=$(t,o,"block");if("block"===i)throw n.renderError("MIXIN_EXTERNAL_FETCH_BLOCKED",`Mixin type "${r}" references an external URL but allowExternalMixins is "block" (the default). Set allowExternalMixins to "same-origin", "allow", or a per-origin policy object on the root SmarkForm instance to permit external mixin loading.`,e);if("same-origin"===i&&P(d))throw n.renderError("MIXIN_CROSS_ORIGIN_FETCH_BLOCKED",`Mixin type "${r}" references a cross-origin URL (${o}) but allowExternalMixins is "same-origin". Set allowExternalMixins to "allow" or add the origin to the per-origin policy object to permit cross-origin mixin loading.`,e);L.has(d)||L.set(d,fetch(d).then(e=>{if(!e.ok)throw Object.assign(new Error(`Failed to fetch mixin source: ${d} (HTTP ${e.status})`),{code:"MIXIN_FETCH_ERROR"});return e.text()}).then(e=>(new DOMParser).parseFromString(e,"text/html"))),c=await L.get(d)}else c=document,d=document.baseURI;const f=`${d}#${l}`,g=n._mixinChain||new Set;if(g.has(f))throw n.renderError("MIXIN_CIRCULAR_DEPENDENCY",`Circular mixin dependency detected: "${f}" is already being expanded in this rendering chain.`,e);const m=c.getElementById(l);if(!m||"template"!==m.tagName.toLowerCase())throw n.renderError("MIXIN_TEMPLATE_NOT_FOUND",`Mixin template #${l} not found in ${s||"the current document"}.`,e);const h=[...m.content.childNodes].filter(e=>e.nodeType===Node.ELEMENT_NODE),y=h.filter(e=>"script"===e.tagName.toLowerCase()),b=h.filter(e=>"style"===e.tagName.toLowerCase()),v=h.filter(e=>{const t=e.tagName.toLowerCase();return"script"!==t&&"style"!==t});if(1!==v.length)throw n.renderError("MIXIN_TEMPLATE_INVALID_ROOT",`Mixin template #${l} must contain exactly one root element (found ${v.length}).`,e);const N=v[0],w=p(N.getAttribute("data-smark"))||{};if(void 0!==w.name)throw n.renderError("MIXIN_TEMPLATE_ROOT_HAS_NAME",`Mixin template #${l} root element must not specify a "name" in its data-smark options. The name must be set on the placeholder (usage site).`,e);const E=N.cloneNode(!0),_=[...e.children].filter(e=>e.hasAttribute("data-for"));_.length>0&&function(e,t){for(const n of t){const t=n.getAttribute("data-for"),r=e.querySelector(`[id="${CSS.escape(t)}"]`);if(!r)continue;const o=n.cloneNode(!0);D(o),o.removeAttribute("data-for"),r.replaceWith(o)}}(E,_),function(e,t,n){if(e.querySelector("script"))throw t.renderError("MIXIN_NESTED_SCRIPT_DISALLOWED","Mixin template root subtree must not contain \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested +

@@ -41,16 +41,13 @@ A credit card field demonstrates spaces between every 4 digits β€” a pattern fam {% raw %}{% endraw %} {% capture mask_cc_js -%} -const form = new SmarkForm("#payment"); -form.rendered.then(async () => { - const cardField = form.find("/cardNumber"); +const myForm = new SmarkForm("#payment"); +myForm.rendered.then(async () => { + const cardField = myForm.find("/cardNumber"); cardField.mask(node => { // Using iMask.js (loaded via CDN): https://cdn.jsdelivr.net/npm/imask return new IMask(node, { - mask: "0000 0000 0000 0000", - blocks: { - "0000": {mask: IMask.Number} - } + mask: "0000 0000 0000 0000" }); }); }); @@ -67,6 +64,7 @@ form.rendered.then(async () => { jsHead=mask_cc_js notes=mask_cc_notes showEditor=true + tests=false %} ## Price Example (Non-Text Field Type) @@ -75,7 +73,7 @@ This shows how to use a `number` field type β€” SmarkForm converts it to `text` {% raw %}{% endraw %} {% capture mask_price_html -%} - +

@@ -89,13 +87,13 @@ This shows how to use a `number` field type β€” SmarkForm converts it to `text` {% raw %}{% endraw %} {% capture mask_price_js -%} -const form = new SmarkForm("#product"); -form.rendered.then(async () => { - const priceField = form.find("/price"); +const myForm = new SmarkForm("#product"); +myForm.rendered.then(async () => { + const priceField = myForm.find("/price"); priceField.mask(node => { const imask = new IMask(node, { mask: Number, - scale: 100, // 2 decimal places + scale: 2, // 2 decimal places thousandsSeparator: " " // Add space separator every 3 digits }); // Export returns a proper JavaScript number, not a formatted string @@ -115,37 +113,36 @@ form.rendered.then(async () => { jsHead=mask_price_js notes=mask_price_notes showEditor=true + tests=false %} -## Singleton Masking +## Singleton (Single Field) Masking -When masking is applied to a singleton component (e.g., a number field wrapped in a type converter), the method delegates to the inner field. The `_maskInstance` lives on the inner field while the outer component remains chainable. +Masking works on any field within a form, including single-field forms. Apply the mask to the child field just like any other. {% raw %}{% endraw %} {% capture mask_singleton_html -%} - -

- + +
+

+ + +

{%- endcapture %}{% raw %}{% endraw %} {% raw %}{% endraw %} {% capture mask_singleton_js -%} -const form = new SmarkForm("#singleton"); -form.rendered.then(async () => { - // Mask on the singleton β€” returns the outer component for chaining - const quantityField = form.find("/quantity"); +const myForm = new SmarkForm("#singleton"); +myForm.rendered.then(async () => { + const quantityField = myForm.find("/quantity"); quantityField.mask(node => new IMask(node, { mask: "0[.]00" })); - // The inner field has the _maskInstance - const innerField = quantityField.children[""]; - console.log("Inner field has mask:", innerField._maskInstance !== undefined); - console.log("Outer field has mask:", quantityField._maskInstance === undefined); }); {%- endcapture %}{% raw %}{% endraw %} {% raw %}{% endraw %} {% capture mask_singleton_notes -%} -**Try it!** This playground demonstrates singleton masking: edit the JS to check the instance locations and try different masks. +**Try it!** Try different masks and see the quantity field format change. {%- endcapture %}{% raw %}{% endraw %} {% include components/sampletabs_tpl.md @@ -154,6 +151,7 @@ form.rendered.then(async () => { jsHead=mask_singleton_js notes=mask_singleton_notes showEditor=true + tests=false %} ## Validation @@ -162,7 +160,7 @@ Many masking libraries include built-in validation. You can integrate it by chec {% raw %}{% endraw %} {% capture mask_validation_html -%} - +

@@ -176,12 +174,12 @@ Many masking libraries include built-in validation. You can integrate it by chec {% raw %}{% endraw %} {% capture mask_validation_js -%} -const form = new SmarkForm("#pricevalidation"); -form.rendered.then(async () => { - const priceField = form.find("/price"); +const myForm = new SmarkForm("#pricevalidation"); +myForm.rendered.then(async () => { + const priceField = myForm.find("/price"); const imask = new IMask(priceField.targetFieldNode, { mask: Number, - scale: 100, + scale: 2, thousandsSeparator: " " }); priceField._maskInstance = imask; @@ -203,6 +201,7 @@ form.rendered.then(async () => { jsHead=mask_validation_js notes=mask_validation_notes showEditor=true + tests=false %} ## Masking is Permanent @@ -214,7 +213,7 @@ When a mask converts an input to `text`, that change is permanent for the lifeti > Try the playgrounds above to see the key features. For production usage with your own CDN, use: ```html - + ``` From 7d2e02903145a6771d24ddb6c5e8ef80eb44a55b Mon Sep 17 00:00:00 2001 From: Joan Miquel Torres Date: Sat, 27 Jun 2026 21:48:23 +0200 Subject: [PATCH 11/85] =?UTF-8?q?docs:=20rewrite=20field=20masking=20docs?= =?UTF-8?q?=20=E2=80=94=20fix=20UNKNOWN=5FTYPE,=20wrapper=20pattern,=20CC?= =?UTF-8?q?=20validation,=20phone=20list=20with=20singleton,=20remove=20va?= =?UTF-8?q?lidation/singleton=20sections?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All 5 runs from the improvement plan, completed in this commit: Run 1: Fix Custom Mask UNKNOWN_TYPE bug (type:"text" β†’ "input") Run 2: All examples use id="myForm$$" wrapper + document.getElementById("myForm$$") - mask-singleton: switch from singleton to regular form (timing issue with validateInputType) - mask-custom: fix test path /custom/phone β†’ /contacts/phones Run 3: Credit card factory returns null for unmaskedValue when invalid; remove Validation section Run 4: Custom Mask β†’ phone list with singleton+list; remove Singleton section; improve all notes Run 5: Evaluate DOM error indicator smoke test β€” deferred (redundant with console.error) Also: updated AGENTS/Documentation-Examples.md with singleton+list pattern docs, updated AGENTS.md masking docs, created docs/masking_docs_plan.md. --- AGENTS.md | 65 +- AGENTS/Documentation-Examples.md | 63 ++ AGENTS/SmarkForm-Usage.md | 18 + PROMPTS.md | 190 ++++++ dist/SmarkForm.esm.js | 2 +- dist/SmarkForm.esm.js.map | 2 +- dist/SmarkForm.umd.js | 2 +- dist/SmarkForm.umd.js.map | 2 +- docs/_advanced_concepts/error_codes.md | 2 +- docs/_advanced_concepts/field_masking.md | 429 ++++++++---- docs/_advanced_concepts/mixin_types.md | 2 +- .../security_considerations.md | 2 +- docs/_includes/components/sampletabs_ctrl.md | 15 +- docs/_resources/AGENTS/SmarkForm-Usage.md | 18 + docs/masking_docs_plan.md | 113 ++++ src/lib/component.js | 17 +- src/lib/mixin.js | 28 + src/lib/test/layout.pug | 3 + src/main.js | 40 ++ src/types/input.type.js | 66 +- test/declarative_mask.tests.js | 637 ++++++++++++++++++ test/mask.tests.js | 348 +++++----- 22 files changed, 1737 insertions(+), 327 deletions(-) create mode 100644 docs/masking_docs_plan.md create mode 100644 test/declarative_mask.tests.js diff --git a/AGENTS.md b/AGENTS.md index 18db91c8..60332f1c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -51,18 +51,23 @@ For convenience, include this exact sentence in the PR description when you want ### Field Masking -The `mask(callback)` method (see `src/types/input.type.js`) enables integration with external masking libraries like iMask.js. It converts input types (`number`, `date`, `time`, `datetime-local`, `radio`, `color`) to `text` for masking while keeping exported data typed correctly. The mask instance is stored in `_maskInstance` and provides `unmaskedValue` for clean exports. +SmarkForm's declarative masking API allows integrating external masking libraries via `SmarkForm.registerMask(name, factory)` and ` +``` + +The `script[type="smark-mask"]` tag is inert β€” browsers don't execute it. +SmarkForm scans for these during initialization, evaluates the content, and +registers each named mask function internally. + +This works naturally inside **mixin templates** β€” a mixin that uses a masked +field can carry its own `\n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested +``` + +SmarkForm scans for these elements on construction and registers each factory +automatically. Script elements inside a ` +

+
+
+ + + +`; + + let onClosed; + try { + const rendered = await renderHtml(html, 'mixin_scoped'); + onClosed = rendered.onClosed; + await page.goto(rendered.url); + await page.waitForFunction(() => typeof window.myForm !== 'undefined'); + await page.waitForFunction(() => window.myForm.renderedSync === true); + + const result = await page.evaluate(() => { + const field = myForm.find('used/inner'); + if (!field) return { found: false, hasInstance: false, type: null }; + return { + found: true, + hasInstance: !!field._maskInstance, + type: field.targetFieldNode.getAttribute('type'), + }; + }); + expect(result.found).toBe(true); + expect(result.hasInstance).toBe(true); + expect(result.type).toBe('text'); + } finally { + if (onClosed) await onClosed(); + } + }); + + test('mixin-local mask overrides global mask with same name', async ({ page }) => { + const html = ` + +Mixin Override + + + +
+
+ +
+ + + +`; + + let onClosed; + try { + const rendered = await renderHtml(html, 'mixin_override'); + onClosed = rendered.onClosed; + await page.goto(rendered.url); + await page.waitForFunction(() => typeof window.myForm !== 'undefined'); + await page.waitForFunction(() => window.myForm.renderedSync === true); + + const result = await page.evaluate(() => { + const innerField = myForm.find('used/inner'); + const plainField = myForm.find('plain'); + if (!innerField || !plainField) { + return { innerVal: null, plainVal: null, innerFound: !!innerField, plainFound: !!plainField }; + } + return { + innerVal: innerField._maskInstance.unmaskedValue, + plainVal: plainField._maskInstance.unmaskedValue, + }; + }); + expect(result.innerVal).toBe('LOCAL'); + expect(result.plainVal).toBe('GLOBAL'); + } finally { + if (onClosed) await onClosed(); + } + }); + +}); diff --git a/test/mask.tests.js b/test/mask.tests.js index 5614bc53..a632c7e7 100644 --- a/test/mask.tests.js +++ b/test/mask.tests.js @@ -1,6 +1,26 @@ +// test/mask.tests.js +// ================== +// Playwright tests for SmarkForm masking β€” declarative API. +// Masks are applied via the `mask` property in data-smark, referencing +// a factory registered with SmarkForm.registerMask() or a +// \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested
-

@@ -171,8 +171,8 @@ for masking but exports a proper JavaScript number. {% raw %}{% endraw %} {% capture mask_price_html -%} +

-

@@ -220,7 +220,7 @@ The example also demonstrates how masks work inside list items wrapped in a singleton. Each phone `` sits inside a `` that SmarkForm treats as a standalone singleton field. The `mask` property placed on the singleton wrapper is automatically inherited by the inner -``, and the list template lets you add or remove phones dynamically. +``, and the list's add button lets you add or remove phones dynamically. {% raw %}{% endraw %} {% capture mask_custom_html -%} @@ -228,21 +228,18 @@ placed on the singleton wrapper is automatically inherited by the inner

-

- +
+

+ + + + +

+

- - {%- endcapture %}{% raw %}{% endraw %} {% raw %}{% endraw %} @@ -263,7 +260,7 @@ const myForm = new SmarkForm(document.getElementById("myForm$$")); {% raw %}{% endraw %} {% capture mask_custom_notes -%} -Each phone input is wrapped in a singleton (`type:"input"`) with `mask:"digits"` on the wrapper. The mask is inherited by the inner `` automatically, and `export()` returns only the digits. Add new phones with the button β€” each new item gets the mask applied on render. +Each phone input is wrapped in a singleton (`type:"input"`) with `mask:"digits"` on the wrapper. The mask is inherited by the inner `` automatically, and `export()` returns only the digits. Add new phones with the button β€” each new item inherits the mask from its singleton wrapper. {%- endcapture %}{% raw %}{% endraw %} {% raw %}{% endraw %} @@ -278,11 +275,11 @@ export default async ({ expect, readField, root, page }) => { {% include components/sampletabs_tpl.md formId="mask-custom" - htmlSource=mask_price_html - jsHead=mask_price_js - notes=mask_price_notes + htmlSource=mask_custom_html + jsHead=mask_custom_js + notes=mask_custom_notes showEditor=true - tests=false + tests=mask_custom_tests %} ## Mixin-Scoped Masks diff --git a/src/types/number.type.js b/src/types/number.type.js index 0f1a1b01..08803896 100644 --- a/src/types/number.type.js +++ b/src/types/number.type.js @@ -25,7 +25,7 @@ export class number extends input { const data = await super.export(...args); if (me.isSingleton) return data; // Overload only inner field return ( - data.length && ! isNaN(data) ? Number(data) + data && data.length && ! isNaN(data) ? Number(data) : null ); };//}}} diff --git a/test/co_located_tests.tests.js b/test/co_located_tests.tests.js index 3fb21fc4..9228b30b 100644 --- a/test/co_located_tests.tests.js +++ b/test/co_located_tests.tests.js @@ -170,7 +170,12 @@ function validateHtmlSource(html) { * meaning it must not be wrapped in an extra container. */ function isFormRoot(htmlSource) { - return /^\s*<[\w]+[^>]*\bid="myForm[^"]*"/i.test(htmlSource); + // Skip leading +{{ via_js_form }} +{%- endcapture %}{% raw %}{% endraw %} + +{% raw %}{% endraw %} +{% capture via_js_notes -%} +The `registerMask()` call must happen **before** any form that uses it is constructed. The factory receives the input's DOM node and returns any object with an `unmaskedValue` property β€” here, an IMask instance. SmarkForm reads `unmaskedValue` for `export()` so the clean digit string is returned, not the formatted display with spaces. +{%- endcapture %}{% raw %}{% endraw %} + +{% include components/sampletabs_tpl.md + formId="via-js" + htmlSource=via_js_html + jsHead=via_js_js + notes=via_js_notes + selected="javascript" + tests=false +%} + ### Via Declarative HTML Place a ` -``` - -SmarkForm scans for these elements on construction and registers each factory -automatically. Script elements inside a `\n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested `; @@ -547,7 +547,7 @@ test.describe('Declarative Masking API', () => {
`; @@ -603,7 +603,7 @@ test.describe('Declarative Masking API', () => {
`; diff --git a/test/doc/WRITING_TESTS.md b/test/doc/WRITING_TESTS.md index c0937318..c526bd07 100644 --- a/test/doc/WRITING_TESTS.md +++ b/test/doc/WRITING_TESTS.md @@ -139,7 +139,7 @@ these options into the SmarkForm constructor so the example renders correctly: formId="my_mixin_example" htmlSource=my_mixin_example_html demoValue='{"foo":"bar"}' - smarkformOptions='{"allowLocalMixinScripts":"allow"}' + smarkformOptions='{"smark_mixin_allowLocalScripts":"allow"}' tests=false %} ``` @@ -148,10 +148,10 @@ Supported option keys (all optional): | Option | Allowed values | Default | Purpose | |---|---|---|---| -| `allowLocalMixinScripts` | `"block"` / `"noscript"` / `"allow"` / per-origin object | `"block"` | Allow `\n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested -
- -
+{% raw %}{% endraw %} +{% capture mixin_mask_html -%} +
+
+

+ + +

+
+ + -``` +{%- endcapture %}{% raw %}{% endraw %} -A mixin-local mask **overrides** a global mask with the same name, so mixins -can safely define their own versions of shared mask names. +{% raw %}{% endraw %} +{% capture mixin_mask_notes -%} +The `digits` mask is defined inside the `#digitsMixin` template and is scoped to it β€” only fields expanded from that mixin can use it. The global `"global"` field tries to use the same `"digits"` mask but fails (mask not found globally). This prevents naming conflicts between different mixins. +{%- endcapture %}{% raw %}{% endraw %} + +{% include components/sampletabs_tpl.md + formId="mixin-mask" + htmlSource=mixin_mask_html + notes=mixin_mask_notes + smarkformOptions='{"smark_mixin_allowLocalScripts":"allow"}' + selected="html" + expectedConsoleErrors=1 + tests=false +%} + +This example requires `smark_mixin_allowLocalScripts: "allow"` because the +` +
+

+ + +

+
+{%- endcapture %}{% raw %}{% endraw %} + +{% raw %}{% endraw %} +{% capture inputmask_js -%} +SmarkForm.registerMask("price", (node) => { + node.inputMode = "decimal"; + + Inputmask({ + alias: "numeric", + groupSeparator: " ", + radixPoint: ".", + digits: 2, + digitsOptional: false, + placeholder: "0", + allowMinus: false, + }).mask(node); + + return { + get unmaskedValue() { + return node.inputmask?.unmaskedvalue() ?? node.value; + }, + set unmaskedValue(v) { + node.inputmask?.setValue(v); + }, + }; +}); + +const myForm = new SmarkForm(document.getElementById("myForm$$")); +{%- endcapture %}{% raw %}{% endraw %} + +{% raw %}{% endraw %} +{% capture inputmask_notes -%} +The factory uses Inputmask's `numeric` alias with `groupSeparator: " "` (space every 3 digits), `radixPoint: "."` (always display period), `digits: 2` (two decimal places), and `digitsOptional: false` (show `.00` until decimal digits are typed). The wrapper reads `unmaskedvalue()` for export and calls `setValue()` for import β€” SmarkForm only needs the `unmaskedValue` contract. +{%- endcapture %}{% raw %}{% endraw %} + +{% include components/sampletabs_tpl.md + formId="inputmask-price" + htmlSource=inputmask_html + jsHead=inputmask_js + notes=inputmask_notes + showEditor=true + selected="js" + tests=false +%} ## Error Handling From d1f9dcdf1dd602cbeae7526502b8a3335a04c481 Mon Sep 17 00:00:00 2001 From: Joan Miquel Torres Date: Fri, 31 Jul 2026 18:55:32 +0200 Subject: [PATCH 55/85] fix: remove error-throwing global field from mixin-scoped demo The global field intentionally triggered MASK_NOT_FOUND to demonstrate scoping, but a demo should work cleanly. Removed it; the notes now explain the scoping behavior in text. --- docs/_working_with_forms/field_masking.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/docs/_working_with_forms/field_masking.md b/docs/_working_with_forms/field_masking.md index 5bbf36a5..a2d49aa7 100644 --- a/docs/_working_with_forms/field_masking.md +++ b/docs/_working_with_forms/field_masking.md @@ -422,10 +422,6 @@ can safely define their own versions of shared mask names. {% capture mixin_mask_html -%}
-

- - -

\n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested - -{%- endcapture %} -{% raw %} {% endraw %} +A product customizer with **mixin templates** for different product variants +(Sedan, SUV, Sports) and **field masking** for price formatting. Each variant +template includes option choices and a price calculator. -{% raw %} {% endraw %} -{% capture nested_schedule_table -%} +{% raw %} {% endraw %} +{% capture configurator_html -%}
-

πŸ—“οΈ Periods:

-
-
πŸ”’ Out of Service
-
-
- + + +
    +
  • +
+

πŸ’‘ Try the Inputmask price field: type digits, see space grouping and 2 decimal places

-{{ period_item_tpl }} -{{ schedule_row_tpl }} -{%- endcapture %} -{% raw %} {% endraw %} -{% raw %} {% endraw %} -{% capture nested_schedule_notes -%} + +{%- endcapture %}{% raw %}{% endraw %} + +{% raw %} {% endraw %} +{% capture configurator_js -%} +SmarkForm.registerMask("price", (node) => { + node.inputMode = "decimal"; + Inputmask({ + alias: "numeric", + groupSeparator: " ", + radixPoint: ".", + digits: 2, + digitsOptional: false, + placeholder: "0", + allowMinus: false, + }).mask(node); + return { + get unmaskedValue() { return node.inputmask?.unmaskedvalue() ?? node.value; }, + set unmaskedValue(v) { node.inputmask?.setValue(v); }, + }; +}); -πŸ‘‰ **Two templates, one example:** - * `#scheduleRow` (inner) β€” the time-interval list template. - * `#periodItem` (outer) β€” wraps the whole period fieldset and uses - `#scheduleRow` inside it, demonstrating mixin composition. +const myForm = new SmarkForm(document.getElementById("myForm$$")); +{%- endcapture %}{% raw %}{% endraw %} -πŸ‘‰ **Smart date prefill (the ` -
-

- - -

-
-
-{%- endcapture %} -{% raw %} {% endraw %} - -{% raw %} {% endraw %} -{% capture showcase_mask_js -%} -SmarkForm.registerMask("card", (node) => { - node.placeholder = "0000 0000 0000 0000"; - node.inputMode = "numeric"; - - let showLazy = true; - const imask = new IMask(node, { - mask: "0000 0000 0000 0000", - lazy: true, - }); - - let prevValue = node.value; - let lastKey = ""; - node.addEventListener("keydown", (e) => { lastKey = e.key; }); - node.addEventListener("input", () => { - const hasContent = imask.masked.unmaskedValue.length > 0; - if (hasContent && showLazy) { - showLazy = false; - imask.updateOptions({ - mask: "0000 0000 0000 0000", - lazy: false, - placeholderChar: "_", - }); - } else if (!hasContent && !showLazy) { - showLazy = true; - imask.updateOptions({ - mask: "0000 0000 0000 0000", - lazy: true, - }); - } - if (node.value === prevValue && node === document.activeElement) { - if (lastKey !== "Backspace" && lastKey !== "Delete") { - node.style.boxShadow = "0 0 0 2px #f80"; - setTimeout(() => node.style.boxShadow = "", 250); - } - } - prevValue = node.value; - const raw = imask.masked.unmaskedValue; - const showError = raw.length > 0 && !imask.masked.isComplete; - node.setCustomValidity(showError ? "Please enter the full 16-digit card number" : ""); - }); - - return { - get unmaskedValue() { - return imask.masked.isComplete ? imask.masked.unmaskedValue : null; - }, - set unmaskedValue(v) { imask.masked.unmaskedValue = v; }, - }; -}); - -const myForm = new SmarkForm(document.getElementById("myForm$$")); -{%- endcapture %} -{% raw %} {% endraw %} - -{% raw %} {% endraw %} -{% capture showcase_mask_notes -%} -A fully-worked credit card field: IMask with lazy switching (placeholder β†’ underscore fill), `inputMode: "numeric"` for mobile keyboards, orange blink on rejected keystrokes, and a wrapper that returns `null` for incomplete numbers so they are never exported. - -> **Want to learn more?** See -> [Field Masking]({{ "/working_with_forms/field_masking" | relative_url }}). -{%- endcapture %} -{% raw %} {% endraw %} - -{% capture demoValue -%} -{"payment": {"cardNumber": 4111111111111111}} -{%- endcapture %} - -{% raw %} {% endraw %} -{% capture showcase_mask_css -%} -input:invalid { - outline: 1px solid #d4c070; - outline-offset: -1px; -} -{%- endcapture %} -{% raw %} {% endraw %} - -{% include components/sampletabs_tpl.md - formId="field_masking" - htmlSource=showcase_mask_html - jsHead=showcase_mask_js - cssSource=showcase_mask_css - notes=showcase_mask_notes - selected="preview" - showEditor=true - demoValue=demoValue - tests=false -%} - -> **Want to learn more about masking?** See the full reference in -> [Field Masking]({{ "/working_with_forms/field_masking" | relative_url }}). - - - -### Dynamic Dropdown Options - -{: .warning :} -> Section still under construction... - -In this example, we'll illustrate how to create dropdown menus with dynamic options. This is particularly useful for forms that need to load options based on user input or external data sources. - - -{% include components/sampletabs_tpl.md - formId="dynamic_dropdown" - htmlSource=dynamic_dropdown - selected="preview" - tests=false -%} - - - - -## Random Examples - -Here are some random examples to showcase the flexibility of SmarkForm and how -it can be used to create various types of forms or even more complex interfaces -with different functionalities. - -### Simple Calculator - -The following example implements a simple calculator with just single input -field and several buttons triggering the *import* action over that field with -the *data* property accordingly set. - -It leverages the *singleton* pattern to avoid specifying the context for every -button. Then a very simple JavaScript code makes the rest... - -{% raw %} {% endraw %} -{% capture calculator -%} -
-
- - -
- - - - - - - - - - - - - - - - - - - - -
-
- -
{%- endcapture %} -{% raw %} {% endraw %} - -{% raw %} {% endraw %} -{% capture calculatorStyles_css -%} -{{""}}#myForm$$ .calculator { - background-color: #333; - border-radius: 10px; - padding: 20px; - box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); - width: 300px; -} -{{""}}#myForm$$ .display { - width: 100%; - box-sizing: border-box; - background-color: #fff; - border: 1px solid #ccc; - border-radius: 5px; - padding: 10px; - margin-bottom: 10px; - font-size: 24px; - text-align: right; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; -} -{{""}}#myForm$$ .display:invalid { - background-color: #fcc; -} -{{""}}#myForm$$ .buttons { - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 5px; -} -{{""}}#myForm$$ button { - padding: 15px; - font-size: 18px; - border: none; - border-radius: 5px; - cursor: pointer; - background-color: #555; - color: white; - transition: background-color 0.2s; -} -{{""}}#myForm$$ button:hover { - background-color: #777; -} -{{""}}#myForm$$ .operator { - background-color: #f9a825; -} -{{""}}#myForm$$ .operator:hover { - background-color: #ffb300; -} -{{""}}#myForm$$ .equals { - background-color: #4caf50; -} -{{""}}#myForm$$ .equals:hover { - background-color: #66bb6a; -} -{{""}}#myForm$$ .clear { - background-color: #d32f2f; -} -{{""}}#myForm$$ .clear:hover { - background-color: #ef5350; -} -{{ hotkeys_reveal_css }} -{%- endcapture %} -{% raw %} {% endraw %} - -{% raw %} {% endraw %} -{% capture calculator_css -%} -{{ calculatorStyles_css }} -{{ hotkeys_reveal_css }} -{%- endcapture %} -{% raw %} {% endraw %} - -{% raw %} {% endraw %} -{% capture calculator_js -%} - -const invalidChars = /[^0-9+\-*\/().]+/g; - -myForm.on("BeforeAction_import", async (ev)=>{ - const prevValue = await ev.context.export(); - const key = ev.data; - switch (key) { - case "C": - ev.data = "0"; /* Clear display */ - break; - case "Del": - ev.data = prevValue.slice(0, -1) || "0"; /* Remove last character */ - break; - case "=": - try { - /* Evaluate expression */ - const sanitized = prevValue.replace(invalidChars, ''); - ev.data = eval(sanitized); - } catch (e) { - alert("Invalid expression"); - ev.preventDefault(); /* Keep existing data */ - } - break; - default: - if (prevValue.trim() === "0") { - ev.data = key; /* Replace 0 with new input */ - } else { - ev.data = prevValue + key; /* Append to existing value */ - }; - }; -});{% -endcapture %} -{% raw %} {% endraw %} - -{% raw %} {% endraw %} -{% capture notes -%} -πŸ‘‰ The code in this example is listening to all *import* actions in the whole -form. - -This isn't an issue for this simple example. But if we had other fields in -the form (unless they were intended to be additional calculators) would be -affected too. - -In that case, we could have attached the listener directly to the *display* -field like this: - -```javascript -myForm.onRendered(()=>{ - /* Now display field is rendered */ - const display = myForm.find("/display"); - display.onLocal("BeforeAction_import", async (ev)=>{ - /* ... */ - }); -}); -``` - -πŸ‘‰ Using `.on()`or `.onLocal()` here is indifferent since inputs have no -children. - -...But in case of forms (or lists of forms) using `.on()` would have lead to -intercept every "BeforeAcction_import" event in it **or its children** while -.onLocal() will only intercept those triggered by the form itself. Not from -any of its descendants. - -{%- endcapture %}{% raw %} {% endraw %} - -{% include components/sampletabs_tpl.md - formId="calculator" - htmlSource=calculator - jsSource=calculator_js - cssSource=calculator_css - notes=notes - selected="preview" - tests=false -%} - -{: .hint :} -> Notice that this calculator has *the power superpower* for free: -> -> Expressions like `2**10` are valid, so you can calculate any power. - -πŸ‘‰ A single event handler over the *onAfterAction_import* does all the magic by -intercepting the new value and appending it to the current one except for the -few special cases like `C`, `Del` and `=` where the value is handled -accordingly. - -Check the *JS* tab to see the little JavaScript code that does the job. - -Don't miss the *Notes* tab too for some additional insights. - - -πŸ‘Œ The best thing is that you can either use the calculator buttons or directly -type in the input field: Every time you use a button, the *import* action will -bring the focus back to the input field so you can continue typing. - - -### Calculator (UX improved) - -The UX feeling of the previous example isn't perfect since it was intended to -be a very simple implementation. - -Let's handle the keydown event too and notice the so little effort is needed to -reach a perfect UX. - -{% raw %} {% endraw %} -{% capture supercalculator -%} -
-
- - -
- - - - - - - - - - - - - - - - - - - - -
-
- -
{%- endcapture %} -{% raw %} {% endraw %} - -{% raw %} {% endraw %} -{% capture supercalculator_js -%} - -var invalidChars = /[^0-9+\-*\/().]+/g; - -function updateDisplay(prevValue, key) { - switch (key.toLowerCase()) { - case "c": - case "delete": - return "0"; /* Clear display */ - break; - case "backspace": - return prevValue.slice(0, -1) || "0"; /* Remove last character */ - break; - case "=": - case "enter": /* Keyboard enter key */ - try { - /* Evaluate expression */ - const sanitized = prevValue.replaceAll(invalidChars, ''); - return eval(sanitized); - } catch (e) { - return "Error!"; - } - break; - default: - if (!! key.match(invalidChars)) { - return prevValue; /* Keep existing data */ - }; - if (prevValue.replace(/[0\s]+/, "") === "") { - return key; /* Replace 0 with new input */ - }; - return prevValue + key; /* Append to existing value */ - }; -}; - -myForm.on("BeforeAction_import", async (ev)=>{ - const prevValue = await ev.context.export(); - const key = ev.data; - ev.data = updateDisplay(prevValue, key); -}); - -myForm.on("keydown", async (ev)=>{ - ev.preventDefault(); - const prevValue = await ev.context.export(); - const key = ev.originalEvent.key; - const data = updateDisplay(prevValue, key); - await ev.context.import(data); -});{% -endcapture %} -{% raw %} {% endraw %} - -{% raw %} {% endraw %} -{% capture supercalculator_css -%} -{{""}}#myForm$$ .calculator input.display { - caret-color: transparent; /* Hide caret */ -} -{{ calculatorStyles_css }} -{%- endcapture %} -{% raw %} {% endraw %} - -{% raw %} {% endraw %} -{% capture notes -%} - -πŸ•΅οΈ It's off-topic but worth to mention the trick of doing `!! -key.match(invalidChars))` instead of `invalidChars.test(key)` is not arbitrary -since *invalidChars* is a regex with the global flag set, which makes it -suitable for 'String.replaceAll()'. - -With `test()`, the internal *lastIndex* property won't be reset making it to -fail after first usage. - -The `!!` bit is just stylistic to note we want to evaluate the result of -`.match()` as a boolean. - -{%- endcapture %}{% raw %} {% endraw %} - -{% raw %} {% endraw %} -{% capture supercalculator_tests -%} -export default async ({ page, expect, id, root, readField, writeField }) => { - await expect(root).toBeVisible(); - - // Enter first number "123": click button "1", press "2" (bare key, no - // modifier), then click "3" - await root.getByRole('button', { name: '1' }).click(); - await page.keyboard.press('2'); - await root.getByRole('button', { name: '3' }).click(); - expect(await readField('display')).toBe('123'); - - // Enter the operator: click the + button - await root.getByRole('button', { name: '+' }).click(); - expect(await readField('display')).toBe('123+'); - - // Enter second number "45": press "4" (bare key, no modifier), then click "5" - await page.keyboard.press('4'); - await root.getByRole('button', { name: '5' }).click(); - expect(await readField('display')).toBe('123+45'); - - // Evaluate by pressing Enter (keyboard without any modifier) - await page.keyboard.press('Enter'); - - // Export the form and verify the result in the display field - const data = await page.evaluate(async () => { - return await myForm.export(); - }); - expect(String(data.display)).toBe('168'); -}; -{%- endcapture %}{% raw %} {% endraw %} - -{% include components/sampletabs_tpl.md - formId="supercalculator" - htmlSource=supercalculator - jsSource=supercalculator_js - cssSource=supercalculator_css - notes=notes - selected="preview" - tests=supercalculator_tests -%} - -In this example we no longer need to define hotkeys since we are directly -listening to all keydown events. - -If you check the *JS* tab you'll see that we extracted the key processing logic -to a function called `updateDisplay()` that receives thwo arguments -(*prevValue* and *key*) to calculate the new value of the display. - -It returns null for invalid keystrokes and can report the "Error!" condition -directly to the display (like a real calculator) since it will be cleared with -the next keystroke (no matter which event it comes from). - -Then the *BeforeAction_import* event handler just calls that function and sets -the *ev.data* property with its result. - -The *keydown* event handler does call the `updateDisplay()` function but: - - * It takes the key from the original keydown event. - * Calls the `.preventDefault()` method to avoid the keystroke effectively - reaching the display. - * Programmatically triggers the *import* action over the display with the - new value calculated. - -{: .hint :} -> Since now all keyboard strokes are processed by to `updateDisplay()` -> function, this allows us to define handy aliases which will feel more natural -> in a PC keyboard for some keys like: -> -> * `Enter` as an alias for `=` -> * `Delete` as an alias for `C` -> -> In the case of the formerly named `Del` key, we just renamed it to -> `Backspace` to match the real key since `Del` was just a random name to void -> using en Emojii (←) as a key name. - -We also added a little CSS rule to hide the caret in the input field since the -display will no longer be directly editable. - - -### Team Event Planner - -This is the same demo shown on the [πŸ”— landing page]({{ "/" | relative_url }}) β€” a -compact form that showcases several SmarkForm features at once: a nested -subform, a sortable variable-length list, context-driven hotkeys, and date/time -coercion. - -Use the JSON editor below to inspect the exported data as you interact with the -form, or import your own JSON to pre-populate it. - -{% raw %} {% endraw %} -{% capture event_planner_html -%} -
-
-

- - -

-

- - -

-

- - -

-
- πŸ‘€ Organizer -

- - -

-

- - -

-
-
- - - πŸ‘₯ Attendees: -
    -
  • -
    - - - N ☰ - - - - - -
    - - -
    -
    -
  • -
-
-

πŸ’‘ Hold Ctrl to reveal shortcuts

-
-
{% -endcapture %} -{% raw %} {% endraw %} - -{% raw %} {% endraw %} -{% capture event_planner_css -%} -{{""}}#myForm$$ .ep { - display: flex; - flex-direction: column; - gap: 0.35em; - max-width: 460px; - font-size: 0.95em; -} -{{""}}#myForm$$ .ep p { - display: flex; - align-items: center; - gap: 0.5em; - margin: 0; -} -{{""}}#myForm$$ .ep label { - font-weight: 500; - white-space: nowrap; -} -{{""}}#myForm$$ .ep label:not(.bullet) { - min-width: 4.5em; -} -{{""}}#myForm$$ .ep input { - padding: 0.3em 0.5em; - border: 1px solid #ccc !important; - border-radius: 4px; -} -{{""}}#myForm$$ .ep input[type="text"], -{{""}}#myForm$$ .ep input[type="email"] { - flex: 1; -} -{{""}}#myForm$$ .ep fieldset { - border: 1px solid #ddd !important; - border-radius: 6px; - padding: 0.4em 0.8em 0.6em; - margin: 0; - display: flex; - flex-direction: column; - gap: 0.3em; -} -{{""}}#myForm$$ .ep fieldset legend { - font-weight: bold; - padding: 0 0.3em; -} -{{""}}#myForm$$ .ep-list ul { - list-style: none !important; - padding: 0 !important; - margin: 0.2em 0 0; - display: flex; - flex-direction: column; - gap: 0.25em; -} -{{""}}#myForm$$ .ep-list ul li { - display: flex; - align-items: flex-start; - gap: 0.3em; -} -{{""}}#myForm$$ .ep-list ul li details { - width: 100%; - border: 1px solid transparent; - border-radius: 4px; - transition: border-color 0.15s; -} -{{""}}#myForm$$ .ep-list ul li details[open] { - border-color: #ccc; - padding-bottom: 4px; -} -{{""}}#myForm$$ .ep-list ul li summary { - display: flex; - align-items: center; - gap: 0.4em; - cursor: default; - user-select: none; - padding: 0.1em 0.2em; - list-style: none; -} -{{""}}#myForm$$ .ep-attendee { - display: flex; - flex-wrap: wrap; - gap: 0.4em; - padding: 0.3em 0.4em 0.1em 1.5em; -} -{{""}}#myForm$$ .ep-attendee input { - flex: 1; - min-width: 120px; -} -{{""}}#myForm$$ .ep-hint { - font-size: 0.82em; - color: #888; - margin: 0.15em 0 0; -} -{{""}}#myForm$$ .ep-hint kbd { - background: rgba(165, 165, 165, .25); - border: 1px solid #ccc; - border-radius: 3px; - padding: 1px 4px; -} -/* Hotkey hints revealed on Ctrl press */ -{{""}}#myForm$$ [data-hotkey]{position:relative} -{{""}}#myForm$$ [data-hotkey]::after{ - content:"Ctrl+" attr(data-hotkey); - position:absolute; top:-1.6em; left:0; - font-size:0.7em; - background:#333; color:#fff; - padding:1px 4px; border-radius:3px; - white-space:nowrap; -} -/* Attendee list item entry/exit animations */ -{{""}}#myForm$$ .ep-list ul li.animated_item { - transform: translateX(-100%); - opacity: 0; - transition: transform 200ms ease, opacity 200ms ease; -} -{{""}}#myForm$$ .ep-list ul li.animated_item.ongoing { - transform: translateX(0); - opacity: 1; -}{% -endcapture %} -{% raw %} {% endraw %} - -{% raw %} {% endraw %} -{% capture event_planner_js %}const delay = ms=>new Promise(resolve=>setTimeout(resolve, ms)); -{{""}}myForm.onAll("afterRender", async function(ev) { - if (ev.context.parent?.options.type !== "list") return; - const item = ev.context.targetNode; - item.classList.add("animated_item"); - await delay(1); - item.classList.add("ongoing"); -}); -{{""}}myForm.onAll("beforeUnrender", async function(ev) { - if (ev.context.parent?.options.type !== "list") return; - const item = ev.context.targetNode; - item.classList.remove("ongoing"); - await delay(150); -});{%- endcapture %} -{% raw %} {% endraw %} - -{% raw %} {% endraw %} -{% capture notes -%} -πŸ‘‰ This demo highlights several SmarkForm features at once: - - * **Foldable rows**: Each attendee row uses a native `
`/`` - element. Click the β–Ά triangle (or anywhere on the row header outside the - name field and action buttons) to expand or collapse the extra fields. A - `` inside the `` acts as the - SmarkForm label, making it the drag handle for reordering (shown as `☰`). - Because the `` itself is not the label, the native disclosure - triangle and fold/unfold on click are preserved without extra CSS - workarounds. - * **Nested subform**: The `organizer` fieldset is a subform β€” its fields are - grouped and exported as a nested object. - * **Sortable list**: Attendees can be dragged to reorder them. The list uses - `exportEmpties: false` so empty slots are not exported. - * **Context-driven hotkeys**: The `βž•`/`βž–` buttons inside each list item - carry `-`/`+` hotkeys, active only when focus is within that item. The `🧹` - button uses `Delete` as a context-wide hotkey. - * **Date & time coercion**: The `date` and `time` inputs use SmarkForm's - built-in type coercion β€” values are normalised to ISO date/time format on - import/export. - * **Label components**: `data-smark='label'` on non-`
+ + {%- endcapture %}{% raw %}{% endraw %} {% raw %} {% endraw %} diff --git a/docs/_component_types/type_list.md b/docs/_component_types/type_list.md index 2b8c4b4d..8641f52a 100644 --- a/docs/_component_types/type_list.md +++ b/docs/_component_types/type_list.md @@ -469,7 +469,7 @@ original behaviour: the entire item root is draggable (backward compatible). Controls cross-list drag-and-drop by enforcing a maximum *sibling distance* between source and destination lists. - * **Type:** Number | true | false + * **Type:** Number \| true \| false * **Default value:** 0 (disabled) When `movingDepth` is set to a positive number or `true` on a list, its items diff --git a/src/lib/component.js b/src/lib/component.js index a163f258..5d384cb7 100644 --- a/src/lib/component.js +++ b/src/lib/component.js @@ -8,6 +8,23 @@ import {parseJSON, replaceWrongNode, isHiddenByClosedDetails} from "./helpers.js import {isMixinRef, expandMixin} from "./mixin.js"; const sym_smart = Symbol("smart_component"); + +// Unique source-ID stamping for [data-smark] elements. When mixin templates +// are cloned, data attributes survive cloneNode() so copies of the same source +// carry the same ID. This is used by cross-list drag-and-drop to allow +// dragging between lists that originated from the same template/mixin. +let _nextSourceId = 1; +const _stampedDocs = new WeakSet(); +export function stampSourceIds(rootElement) { + const doc = rootElement.ownerDocument || rootElement; + if (_stampedDocs.has(doc)) return; + _stampedDocs.add(doc); + for (const el of doc.querySelectorAll('[data-smark]')) { + if (!el.dataset.smSrc) { + el.dataset.smSrc = String(_nextSourceId++); + } + } +} const re_valid_typename_chars = /^[a-z0-9_]+$/i; const re_has_wildcards = /[\*\?]/; const wild2regex = wname => new RegExp(//{{{ diff --git a/src/lib/mixin.js b/src/lib/mixin.js index ee969ca6..9451e65d 100644 --- a/src/lib/mixin.js +++ b/src/lib/mixin.js @@ -5,6 +5,7 @@ // normal SmarkForm enhancement begins. import {parseJSON} from "./helpers.js"; +import {stampSourceIds} from "./component.js"; // Module-level caches (shared for the lifetime of the page): const docCache = new Map(); // absoluteUrl β†’ Promise @@ -283,7 +284,11 @@ export async function expandMixin(node, options, component) { //{{{ }) .then(html => { const parser = new DOMParser(); - return parser.parseFromString(html, 'text/html'); + const doc = parser.parseFromString(html, 'text/html'); + // Stamp source IDs on this external document so + // clones carry the same IDs for cross-list drag. + stampSourceIds(doc); + return doc; }) ); } @@ -388,6 +393,10 @@ export async function expandMixin(node, options, component) { //{{{ // makes default snippet slots self-documenting via data-id. convertIds(clone); + // Stamp cloned [data-smark] elements with source IDs so cross-list drag + // can identify lists that share the same mixin source. + stampSourceIds(clone); + // Gather styles from the template top level (siblings of the root element) // and inject them into once per unique content: injectStyles(topLevelStyles); diff --git a/src/main.js b/src/main.js index 113aca16..8399036b 100644 --- a/src/main.js +++ b/src/main.js @@ -1,7 +1,7 @@ // SmarkForm.js // ============ -import {createType} from "./lib/component.js"; +import {createType, stampSourceIds} from "./lib/component.js"; import {hotKeys_handler} from "./lib/hotkeys.js"; // Import core component types and event handlers: @@ -44,6 +44,11 @@ class SmarkForm extends form { // Auto-scan global mask scripts from the document: SmarkForm._scanGlobalMasks(); + // Stamp unique source IDs on ALL [data-smark] elements (including those + // inside \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root.\n // Stamp with a unique template ID so clones from the same template\n // share it β€” used by cross-list drag-and-drop.\n if (! templateRoot.dataset.sfTpl) {\n templateRoot.dataset.sfTpl = nextSourceId();\n }\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root:\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root.\n // Stamp with a unique template ID so clones from the same template\n // share it β€” used by cross-list drag-and-drop.\n if (! templateRoot.dataset.sfTpl) {\n templateRoot.dataset.sfTpl = nextSourceId();\n }\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root.\n // Stamp with a unique template ID so clones from the same template\n // share it β€” used by cross-list drag-and-drop.\n if (! templateRoot.dataset.sfTpl) {\n templateRoot.dataset.sfTpl = nextSourceId();\n }\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root.\n // Stamp with a unique template ID so clones from the same template\n // share it β€” used by cross-list drag-and-drop.\n if (! templateRoot.dataset.sfTpl) {\n templateRoot.dataset.sfTpl = nextSourceId();\n }\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root.\n // Stamp with a unique template ID so clones from the same template\n // share it β€” used by cross-list drag-and-drop.\n if (! templateRoot.dataset.sfTpl) {\n templateRoot.dataset.sfTpl = nextSourceId();\n }\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested \n // \n // is valid.\n const templateContentEls = [...template.content.childNodes]\n .filter(n => n.nodeType === Node.ELEMENT_NODE);\n const topLevelScripts = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'script');\n const topLevelStyles = templateContentEls\n .filter(n => n.tagName.toLowerCase() === 'style');\n const rootElements = templateContentEls\n .filter(n => {\n const tag = n.tagName.toLowerCase();\n return tag !== 'script' && tag !== 'style';\n });\n if (rootElements.length !== 1) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_INVALID_ROOT'\n , `Mixin template #${templateId} must contain exactly one root`\n + ` element (found ${rootElements.length}).`\n , node\n );\n }\n\n const templateRoot = rootElements[0];\n\n // Validate: template root must not set \"name\" in data-smark:\n const templateRootOptions = parseJSON(\n templateRoot.getAttribute('data-smark')\n ) || {};\n if (templateRootOptions.name !== undefined) {\n throw component.renderError(\n 'MIXIN_TEMPLATE_ROOT_HAS_NAME'\n , `Mixin template #${templateId} root element must not specify`\n + ' a \"name\" in its data-smark options.'\n + ' The name must be set on the placeholder (usage site).'\n , node\n );\n }\n\n // Deep-clone the template root.\n // Stamp with a unique template ID so clones from the same template\n // share it β€” used by cross-list drag-and-drop.\n if (! templateRoot.dataset.sfTpl) {\n templateRoot.dataset.sfTpl = nextSourceId();\n }\n const clone = templateRoot.cloneNode(true);\n\n // Collect snippet parameter nodes: direct children of the placeholder\n // that carry a `data-for` attribute, referencing elements inside the clone\n // by their id. These are consumed (not rendered as children).\n const params = [...node.children].filter(el => el.hasAttribute('data-for'));\n\n // Apply snippet parameter substitutions on the clone:\n if (params.length > 0) {\n applySnippetParams(clone, params);\n }\n\n // Enforce no nested + Welcome to the SmarkForm Showcase β€” a visual catalogue of what SmarkForm can do. Each example is a fully working form; explore the tabs to see the HTML, CSS, and JavaScript behind it. @@ -118,39 +160,47 @@ SmarkForm actions: `import`, `export`, `clear`, `addItem`, `removeItem`.
-
- Auto-registration +
+ +
Fields are discovered automatically from HTML `data-smark` attributes β€” no JavaScript wiring needed. Any element with `data-smark` becomes a SmarkForm field component whose name is taken from its `name` attribute. See [Core Concepts]({{ "/getting_started/core_concepts" | relative_url }}). -
+
+
-
- Null values +
+ +
Empty inputs produce `null` in exported JSON. The colour field (`type="color"`) is a good example: clearing it exports `null` rather than an empty string. See [Value Coercion]({{ "/working_with_forms/value_coercion" | relative_url }}). -
+
+
-
- Trigger buttons +
+ +
Buttons with `data-smark` attributes trigger `import`, `export`, and `clear` actions on the root form β€” no custom JavaScript required. See [Triggers]({{ "/component_types/type_trigger" | relative_url }}), [Data Import & Export]({{ "/working_with_forms/data_import_and_export" | relative_url }}). -
+
+
-
- JSON playground +
+ +
The editor tab shows the form's current state as JSON. Edit it and click **_Import_** to see the form react β€” everything in sync without code. See [Playground]({{ "/working_with_forms/playground" | relative_url }}). -
+
+ @@ -270,8 +320,9 @@ button { position: relative; }
-
- Mixin templates +
+ +
The column layout is defined once in a `