diff --git a/configs/mocha-config-devtools/register/tsnode-register.js b/configs/mocha-config-devtools/register/tsnode-register.js index 168466fb..07eb826a 100644 --- a/configs/mocha-config-devtools/register/tsnode-register.js +++ b/configs/mocha-config-devtools/register/tsnode-register.js @@ -10,6 +10,18 @@ require('ts-node').register({ }, }); +// The require()-based register above only patches CommonJS module loading. +// For "type": "module" packages, Mocha loads spec files via dynamic import(), +// which goes through Node's own ESM loader. Newer Node versions natively +// strip TypeScript types but don't resolve `.js` specifiers to `.ts` files, +// so we also register ts-node's ESM loader hook to get NodeNext-style +// extension resolution for those packages. +{ + const { register } = require('node:module'); + const { pathToFileURL } = require('node:url'); + register('ts-node/esm', pathToFileURL('./')); +} + // XXX: @cspotcode/source-map-support library used by ts-node internally causes // issues when running tests in electron renderer environment due to webassembly // module registering that it's trying to run going out of allowed size boundary diff --git a/package-lock.json b/package-lock.json index bd5a9571..c03d1746 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3510,31 +3510,31 @@ "license": "MIT" }, "node_modules/@leafygreen-ui/a11y": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/a11y/-/a11y-3.0.5.tgz", - "integrity": "sha512-HJqDsLCNj4d1RQiifYuhpazZVYgsLZJeEboaQ+UsNhAppV13kdPb2qX7H4RbGmXkalTBzHOkbAYp6vvioSAVdQ==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/a11y/-/a11y-3.0.6.tgz", + "integrity": "sha512-JUY14ijuq01+EWUyCq3HJq3+VnF+N1t3zCqgF4JRqjKCQHscHIRsXwBbuAVVdliNHfRXHKgotpZXN0+OY8nZBA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.0.3", - "@leafygreen-ui/hooks": "^9.1.4", - "@leafygreen-ui/lib": "^15.4.0" + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/lib": "^15.7.1" } }, "node_modules/@leafygreen-ui/avatar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/avatar/-/avatar-3.3.0.tgz", - "integrity": "sha512-3x4wx40yB0kl224d7HK9AihwJmaZtQU2K2u6MrzJoG+LNp8ptFJ+tHrR2WFXKn5Heo7sKWW9AbD6DuUQp32MvA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/avatar/-/avatar-3.3.1.tgz", + "integrity": "sha512-uJT/XxosBID8HHzQYbo6RPyttEKQ+rY4OB5mGp/pHS9HJJNeyZqWaz+TQcOu6XOLwXO9yWi7KyzlpMGV3ecaUA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/logo": "^11.1.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/logo": "^11.1.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", "lodash": "^4.17.21" }, "peerDependencies": { @@ -3542,54 +3542,54 @@ } }, "node_modules/@leafygreen-ui/badge": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/badge/-/badge-10.2.4.tgz", - "integrity": "sha512-iWciiIrnmlrSOtgq438x7Tg0mYjLd148LeCNFypGpeiyfecZz3XmfCQM4NAydT2zA7Q65kcX7f+Yt+cuCz8Mqg==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/badge/-/badge-10.2.5.tgz", + "integrity": "sha512-ldA/FUTM5f6mxxDXjScyWLVSUK53PQsJJ3cJvNPTJgA23Je4IMiLGMilswvrmKPth7qPsfr1g14olnaselhr1A==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2" + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@leafygreen-ui/banner": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/banner/-/banner-10.2.5.tgz", - "integrity": "sha512-CE3Stz4S00kyDqjhfPutJlxxDDdVuynyT/cWpufxhB+a/RvWktRRZL3o3AKPPBwnwzp5e18p+vzAMAilIJ0s0Q==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/banner/-/banner-10.2.6.tgz", + "integrity": "sha512-k90EyDAcUKuYG1pyokcWjt5A9hHJzjweXrkkb/z6ggP281sUKs879RKqAlyAGiNDFcwYR7+K5HAyzPZgrc/e5g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/icon-button": "^17.1.5", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4" + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/icon-button": "^17.1.6", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@leafygreen-ui/button": { - "version": "25.2.1", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/button/-/button-25.2.1.tgz", - "integrity": "sha512-hJWf4K1WNCSaM1VWj8obGuMAxnd1/KCORnGNqphjBKS83Nu0XYCWedl6rhwQTkpCDzESQYAExw3KJV+moygB/w==", + "version": "25.2.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/button/-/button-25.2.2.tgz", + "integrity": "sha512-3eaNjvWp6tlkFWGPK6ejqQGdqMel95G42Yz3mh6DrAW5r2b8d5kn4cog1OBnktmJZyA4icRb+VSx3jA3bSirYg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/polymorphic": "^3.1.0", - "@leafygreen-ui/ripple": "^2.0.7", - "@leafygreen-ui/tokens": "^4.2.2", - "@lg-tools/test-harnesses": "^0.3.4", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/polymorphic": "^3.1.1", + "@leafygreen-ui/ripple": "^2.0.8", + "@leafygreen-ui/tokens": "^4.2.3", + "@lg-tools/test-harnesses": "^0.3.5", "polished": "^4.2.2" }, "peerDependencies": { @@ -3597,37 +3597,37 @@ } }, "node_modules/@leafygreen-ui/card": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/card/-/card-13.3.1.tgz", - "integrity": "sha512-WkH0VbSqy2y959pVQstF4RFF9T31ps+1gl6c7Y8q5Pzt/GEQoUA4y8DvbInrd0RJwyHsIXMV2bbujiMUrh3tvw==", + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/card/-/card-13.3.2.tgz", + "integrity": "sha512-wYpSOHzplJGaE7pokXt8vzL6oC2viJcPPv0phWeUQzL9wCypAJ7FpImL++VHi+cKtgmQcco5gjH+dgNuEyewnQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/polymorphic": "^3.1.0", - "@leafygreen-ui/tokens": "^4.2.2" + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/polymorphic": "^3.1.1", + "@leafygreen-ui/tokens": "^4.2.3" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@leafygreen-ui/checkbox": { - "version": "18.1.5", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/checkbox/-/checkbox-18.1.5.tgz", - "integrity": "sha512-KTvSdlZPY+gmfCuTrRhM+slyGoILzAuVgoXdanneLVDZiD8W3j3t4z69znCYK/tL1pNLin1FaRkO/kpQAkv/DQ==", + "version": "18.1.6", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/checkbox/-/checkbox-18.1.6.tgz", + "integrity": "sha512-zCSxVNWJuhTSAviM/sDl/M2dCjMvjVGFiJOHEogFA3iIrdAxAlNyqW7yAVt2AIz9KFpYw49RclkprC4NWMWGwA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/a11y": "^3.0.5", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4", - "@lg-tools/test-harnesses": "^0.3.4", + "@leafygreen-ui/a11y": "^3.0.6", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5", + "@lg-tools/test-harnesses": "^0.3.5", "react-transition-group": "^4.4.5" }, "peerDependencies": { @@ -3635,44 +3635,44 @@ } }, "node_modules/@leafygreen-ui/chip": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/chip/-/chip-4.2.1.tgz", - "integrity": "sha512-xLnRfz01dNwUWrE5074QjlGolq3XboLAvgJrXR+KCkTyLFExVOMCMXTn5nq4mG5v3rnHB6D3BhOLLDm8euTlxA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/chip/-/chip-4.2.2.tgz", + "integrity": "sha512-dY0FG/YuqV0nY0W1lBW8iNQzaSLmdLB6dDCHibKQTY4zFROyy5P5Nm/m+IyJtEwWf/IdkQDBBpMOpHj/d4OGQg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/inline-definition": "^9.2.1", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2" + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/inline-definition": "^9.2.2", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@leafygreen-ui/code": { - "version": "20.2.8", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/code/-/code-20.2.8.tgz", - "integrity": "sha512-Y9iaaelKP6I7PZskHMPMkg6K5vszhBOc5AQ5BSNoRIzEnaJdOrB9lCNtPzc1sVyp4kL2ryuYyy2Dcv4HwBVVeA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/code/-/code-20.2.9.tgz", + "integrity": "sha512-AHrvEHTp1Sy/RUVskxIjteW7M2xdHJAfwDnnzSyTi86YAYNvsZ9fHOzhssfiYqLPc0H4nIPbn0E2pr5rF0jQzQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/a11y": "^3.0.5", - "@leafygreen-ui/button": "^25.2.1", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.9.0", - "@leafygreen-ui/icon-button": "^17.1.5", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/select": "^17.1.0", - "@leafygreen-ui/skeleton-loader": "^3.0.11", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/tooltip": "^14.3.2", - "@leafygreen-ui/typography": "^22.2.4", - "@lg-tools/test-harnesses": "^0.3.4", + "@leafygreen-ui/a11y": "^3.0.6", + "@leafygreen-ui/button": "^25.2.2", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/icon-button": "^17.1.6", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/select": "^17.1.1", + "@leafygreen-ui/skeleton-loader": "^3.0.12", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/tooltip": "^14.3.3", + "@leafygreen-ui/typography": "^22.2.5", + "@lg-tools/test-harnesses": "^0.3.5", "@types/facepaint": "^1.2.1", "@types/highlight.js": "^10.1.0", "clipboard": "^2.0.6", @@ -3687,25 +3687,25 @@ } }, "node_modules/@leafygreen-ui/combobox": { - "version": "12.5.1", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/combobox/-/combobox-12.5.1.tgz", - "integrity": "sha512-Wf78P44DHq+19b+LLIbFnfF2zTlhuo0kgrCDnm9QcqYwIzyWDHLx2bOQslfpj0Xbm3oFYMIOChnUNK5kHON8Aw==", + "version": "12.5.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/combobox/-/combobox-12.5.2.tgz", + "integrity": "sha512-qnLgjp9IZU7kZJ4M2/wZ2hErSb+Gjqi7Hw2SQ28OlN+hzcrq2qvbqJpjnK1mezRpJ78m4Gyygb3kcxs5jl444w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/checkbox": "^18.1.5", - "@leafygreen-ui/chip": "^4.2.1", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/form-field": "^4.0.9", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/icon-button": "^17.1.5", - "@leafygreen-ui/input-option": "^4.1.5", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/popover": "^14.3.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4", + "@leafygreen-ui/checkbox": "^18.1.6", + "@leafygreen-ui/chip": "^4.2.2", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/form-field": "^4.0.10", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/icon-button": "^17.1.6", + "@leafygreen-ui/input-option": "^4.1.6", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/popover": "^14.3.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5", "chalk": "^4.1.2", "lodash": "^4.17.21", "polished": "^4.2.2" @@ -3745,50 +3745,50 @@ } }, "node_modules/@leafygreen-ui/compound-component": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/compound-component/-/compound-component-0.3.0.tgz", - "integrity": "sha512-gTso3f6nOHIgz9zCIl7AqO9lhFzGcJHPjuh2IahM27PXBRdNXOdjhx8T0WywC5RLLhHOy7eDXZubkBdiii0qDA==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/compound-component/-/compound-component-0.3.1.tgz", + "integrity": "sha512-AlqwOxqGO57KBZfaO7wWX26sF5tYXTc7ymcK7Iw8h3iU1kiBXDNAA928u/sUtByaSmkmKcYXzF1qP87T7N2mkg==", "dev": true, "license": "Apache-2.0" }, "node_modules/@leafygreen-ui/confirmation-modal": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/confirmation-modal/-/confirmation-modal-11.0.1.tgz", - "integrity": "sha512-crYjy/qqEb5cMNuh1zdl6X0GN2V3wGSdo6n7qaTnRFlpaI1waWIDCGwhj+GxlyacNKqUHCTuvhU0xafyqX+p6A==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/confirmation-modal/-/confirmation-modal-11.0.2.tgz", + "integrity": "sha512-Dv9WkwBG+ZFclMnul75R7/9z+hd0lQzl4Ttqq+MAc52JSlg+lcVl2liumfbOeMx49xzm5PTO0nLB2KgxNLMQpw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/button": "^25.2.1", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/modal": "^22.0.1", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/text-input": "^16.2.3", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4" + "@leafygreen-ui/button": "^25.2.2", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/modal": "^22.0.2", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/text-input": "^16.2.4", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@leafygreen-ui/copyable": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/copyable/-/copyable-12.0.3.tgz", - "integrity": "sha512-BVhRh/HZbOMHIAA1lRzzl6UH1L9Uu2gokq9v1EOTl9/95QdkpYs/lUA/VwKjhYM0RNQPY4rSoWjhqC7HzrN6/A==", + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/copyable/-/copyable-12.0.4.tgz", + "integrity": "sha512-+bve11vIzKofDG5HsQAOvYLRex6l4gb+Lwn+VSuk29VUuns4mcWKaYHlLQXC/IqR16rLUnt/PImzS4wH4yxqlg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/button": "^25.2.1", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/tooltip": "^14.3.1", - "@leafygreen-ui/typography": "^22.2.4", + "@leafygreen-ui/button": "^25.2.2", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/tooltip": "^14.3.3", + "@leafygreen-ui/typography": "^22.2.5", "clipboard": "^2.0.6", "polished": "^4.2.2" }, @@ -3797,14 +3797,14 @@ } }, "node_modules/@leafygreen-ui/descendants": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/descendants/-/descendants-3.1.1.tgz", - "integrity": "sha512-A4IdIaUpyzQl7vFdqPxuBuO7GRwx8pFKf4OVQ7v8DXF0qLl508cu1T8oeb5p6elHk8xS3DT/CEVCMrEQt9dDzQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/descendants/-/descendants-3.1.2.tgz", + "integrity": "sha512-WGyHzi6kKYhe5WT8xFAWrS3/kfASqNteHizrYXTv7m3JQeCfV67/mvtYNRi3UdiUpI40TLjLVc3QjI22Lr91kw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/lib": "^15.7.0", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/lib": "^15.7.1", "lodash": "^4.17.21" }, "peerDependencies": { @@ -3812,26 +3812,26 @@ } }, "node_modules/@leafygreen-ui/drawer": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/drawer/-/drawer-5.2.3.tgz", - "integrity": "sha512-H3lYN0xyZzYUwwl7LaJh75Flmhx7xVC7Q7RITN28aAN+NDIe5LPHbfPusqVs6/lT0Mb39XCYwAqoR5/HQAqSww==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/drawer/-/drawer-5.2.4.tgz", + "integrity": "sha512-ZaqvK33wG94W6iM/X0kzfvQ1HL/tk1PY6Ah/CJ3JL4rGJCpCsVIZfFTUiwkq5mlMGFU7kNrbNrL5C5upBrFrbg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/button": "^25.2.1", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/icon-button": "^17.1.5", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/polymorphic": "^3.1.0", - "@leafygreen-ui/resizable": "^0.1.3", - "@leafygreen-ui/tabs": "^17.0.11", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/toolbar": "^1.3.2", - "@leafygreen-ui/typography": "^22.2.4", - "@lg-tools/test-harnesses": "^0.3.4", + "@leafygreen-ui/button": "^25.2.2", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/icon-button": "^17.1.6", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/polymorphic": "^3.1.1", + "@leafygreen-ui/resizable": "^0.1.4", + "@leafygreen-ui/tabs": "^17.0.12", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/toolbar": "^1.3.3", + "@leafygreen-ui/typography": "^22.2.5", + "@lg-tools/test-harnesses": "^0.3.5", "polished": "^4.2.2", "react-intersection-observer": "^8.25.1" }, @@ -3840,9 +3840,9 @@ } }, "node_modules/@leafygreen-ui/emotion": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/emotion/-/emotion-5.2.0.tgz", - "integrity": "sha512-Sov+2tDy4ahGsRR8/KOV8Zzx+5fvVAj7Smyc1brgzE8ocXnOiwahQQ1wVvthAXS91iXPpnuNA5KL6Qzh3jNfQw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/emotion/-/emotion-5.2.1.tgz", + "integrity": "sha512-QrU1DvqvlTj6RemZozDeSgwmHe/lwkeVPEVxOIueMErIB5jwlLZ21Xfr3G1fjx1qEaGtKgH4o9+R7K9QmxalHw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3852,42 +3852,42 @@ } }, "node_modules/@leafygreen-ui/form-field": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/form-field/-/form-field-4.0.9.tgz", - "integrity": "sha512-lbFkP1Hh0cwUxPsatGjSCcDGt4nqDrJ5jWozwhljWbdQF74H9LG+HdVr9FALiLHeDm+47NbmTUQj+hOl4DD5aw==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/form-field/-/form-field-4.0.10.tgz", + "integrity": "sha512-VMNbl3de7p9aGzOLL5kVrMbAbvfcbU2xsquVSKMRLidzFwZSn/yDIc+TnhJcJkwI+jg96n/gjThxyig6EcEPZg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4" + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@leafygreen-ui/guide-cue": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/guide-cue/-/guide-cue-8.2.0.tgz", - "integrity": "sha512-YDNcu+DA4aOE9QxI/TujT/v7h0YXz07rQpBcrew+7dIhDp3NcuNLN39SYegz5Gi6br+rhEQh8n7dowBHhVM6iw==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/guide-cue/-/guide-cue-8.2.1.tgz", + "integrity": "sha512-bVoEQoVAkisLKEg70v9Ftsfibwi3fiTj+IpXXsD3T+u1gJB2XQkmOYqhDdSEPFdHvsKWOyR7fQMEmIhXHBUmoQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/a11y": "^3.0.5", - "@leafygreen-ui/button": "^25.1.3", - "@leafygreen-ui/emotion": "^5.1.0", - "@leafygreen-ui/hooks": "^9.3.0", - "@leafygreen-ui/icon": "^14.7.1", - "@leafygreen-ui/icon-button": "^17.1.4", - "@leafygreen-ui/lib": "^15.6.2", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/popover": "^14.3.1", - "@leafygreen-ui/tooltip": "^14.3.0", - "@leafygreen-ui/typography": "^22.2.3", + "@leafygreen-ui/a11y": "^3.0.6", + "@leafygreen-ui/button": "^25.2.2", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/icon-button": "^17.1.6", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/popover": "^14.3.3", + "@leafygreen-ui/tooltip": "^14.3.3", + "@leafygreen-ui/typography": "^22.2.5", "focus-trap": "6.9.4", "focus-trap-react": "9.0.2", "polished": "^4.2.2" @@ -3897,43 +3897,43 @@ } }, "node_modules/@leafygreen-ui/hooks": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/hooks/-/hooks-9.3.1.tgz", - "integrity": "sha512-KGJa5GoZ/crA1e0MRnJaadqXP5Gyn5VlLBkPyizFTe1dnlrp2OUnNEeb/TWKytT67sewNjOKqjBr0TG5YJQeJQ==", + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/hooks/-/hooks-9.3.2.tgz", + "integrity": "sha512-wWtWEzmcwvCoj5gfJgLsMK5jImMrPNZfw+tD/XcP1gbEJ3xEpCDYjlzPM6ULDJA7d9HK0Sl/+Zxxlndfq/r7bg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/tokens": "^4.2.2", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/tokens": "^4.2.3", "lodash": "^4.17.21" } }, "node_modules/@leafygreen-ui/icon": { - "version": "14.9.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/icon/-/icon-14.9.0.tgz", - "integrity": "sha512-moH8z8Qkd0uO03MO2ttpSo+2nkMfFvNvJ1t1Z4a1nUVSg+xE+c6oiwrNyvhUwrTsbSHUAQr9h+Nld1LTsRsVKw==", + "version": "14.9.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/icon/-/icon-14.9.1.tgz", + "integrity": "sha512-2AFskKx5Xgw1Z09g+V6CkwgUiK4MBZqUEtVgf3wmpO4fmZxapU7PqDFvm/LGB/TL5872sbXmXMqYjpVtTxTEbA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", "lodash": "^4.17.21" } }, "node_modules/@leafygreen-ui/icon-button": { - "version": "17.1.5", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/icon-button/-/icon-button-17.1.5.tgz", - "integrity": "sha512-fTxvykvM1mWZ2cFgwBzvuC0WXY1gJ2kyWWRIUOu24eRTJLwoobzhOF1uF6rBm/DOfhcNDh0dsXJfatdj/lqQXA==", + "version": "17.1.6", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/icon-button/-/icon-button-17.1.6.tgz", + "integrity": "sha512-GFS9F9pY7blRaaxkPNbbIoHbNlOlk9PXLwRRwi8KDKfscYY19ptNSlno5utG970sWB/JHkmbnkka0IgIndt9kw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/a11y": "^3.0.5", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/polymorphic": "^3.1.0", - "@leafygreen-ui/tokens": "^4.2.2", + "@leafygreen-ui/a11y": "^3.0.6", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/polymorphic": "^3.1.1", + "@leafygreen-ui/tokens": "^4.2.3", "polished": "^4.2.2" }, "peerDependencies": { @@ -3941,75 +3941,75 @@ } }, "node_modules/@leafygreen-ui/info-sprinkle": { - "version": "5.0.11", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/info-sprinkle/-/info-sprinkle-5.0.11.tgz", - "integrity": "sha512-ne4CE9xO6tsaac6hhVtKZzjfPXXgLJ//VIShczNc6ibd+pNtDtGg+FkNgcRVkJ2MyXg5qEErqxv5nn+l6eQhtQ==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/info-sprinkle/-/info-sprinkle-5.0.12.tgz", + "integrity": "sha512-VLmrHbtCx1ER/Z84BPQP7DQ49UB4zUmZ1Le5qCY0cqM/Ej61BKe3Wk1Em68iDmxVF9wZCG158D0sfHS5dxysnA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/tooltip": "^14.3.1" + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/tooltip": "^14.3.3" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@leafygreen-ui/inline-definition": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/inline-definition/-/inline-definition-9.2.1.tgz", - "integrity": "sha512-AUDjEvyI9QizDSoew9uqjidoBJ8L88FWPhgzN+ixSo5B4EcwT7Ig52P82thfazdfb2RQUQz0/ajkEnoHFPLy9Q==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/inline-definition/-/inline-definition-9.2.2.tgz", + "integrity": "sha512-dXLrYPeNxhysh2VL3RhzC/hBTFdcl/qxIJcbgg9yat7E6pd5HDDcJPWIEuIHCVuGbG2EaksZxedFR8X0ZF8Z2w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/tooltip": "^14.3.1" + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/tooltip": "^14.3.3" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@leafygreen-ui/input-option": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/input-option/-/input-option-4.1.5.tgz", - "integrity": "sha512-cYwx1uVrnev4ZIEYU8k08OAOUwe+dlUVrZh1aRoyXiOYx8LVw3N1C1bKxzW6dgwipPxloQa0RLIOwoqC0qkbQw==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/input-option/-/input-option-4.1.6.tgz", + "integrity": "sha512-lMACyHuuuIhbnhgTtXZorU6m+SIecxF5Z02t5s1YBwtDLr7Aw/Su3t4SmOEhfpPSm6dqtnHxwxLYEPAqdU5Usw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/a11y": "^3.0.5", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/polymorphic": "^3.1.0", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4" + "@leafygreen-ui/a11y": "^3.0.6", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/polymorphic": "^3.1.1", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@leafygreen-ui/leafygreen-provider": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/leafygreen-provider/-/leafygreen-provider-5.0.4.tgz", - "integrity": "sha512-VDlmjTiIqlITVhq4VKUDq8FLySWnHkTxSV2n1sxOanLNPuatOXjxsPmCkPUBXhmQKk/fBf4yQnDKOwJvkyzE6Q==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/leafygreen-provider/-/leafygreen-provider-5.0.5.tgz", + "integrity": "sha512-RSc94eCB2qZuJsm/iO+vGFm7EQo/qB0O0a237tMa0G8xW4TOr+8qV0kHN3p8sruVUK3D4aO2beZvasYLgGxA3g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/hooks": "^9.1.3", - "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/lib": "^15.7.1", "react-transition-group": "^4.4.5" } }, "node_modules/@leafygreen-ui/lib": { - "version": "15.7.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-15.7.0.tgz", - "integrity": "sha512-qHv7oN2uN7ywdG9lUN/ayFJzzgJELLgNxIo24m64Sl9c8gSWQeCW+NDj3ukoNVH7ME7QPoULseNd1fElonH8Vg==", + "version": "15.7.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-15.7.1.tgz", + "integrity": "sha512-BUTecsvMA0sSsOPWFlQ8+uf9Fy/0Pr3hFY1AJOJ8k/51wsKlvA96y3Hz4qIFMUhhurZmxxTK+ddiNfdE+MoxDQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4020,18 +4020,18 @@ } }, "node_modules/@leafygreen-ui/loading-indicator": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/loading-indicator/-/loading-indicator-5.1.1.tgz", - "integrity": "sha512-ZXAupHOjv3++yo0hiGhHfNC4YjwP9FByrMj6zfjGFGL3WfFb+vvdpqK6/KYSawKyh0HBy7yvpUc6Eibk/APceg==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/loading-indicator/-/loading-indicator-5.1.2.tgz", + "integrity": "sha512-JJEvCItef91jRwxLYv4YeXsJW9WU1jFg8jAZGY+7x373w0ZqJW7sApitMxn5XePxRKVwohxHygAbZ2Vr+bDMzA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4", - "@lg-tools/test-harnesses": "^0.3.4", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5", + "@lg-tools/test-harnesses": "^0.3.5", "react-lottie-player": "^1.5.6" }, "peerDependencies": { @@ -4039,55 +4039,55 @@ } }, "node_modules/@leafygreen-ui/logo": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/logo/-/logo-11.1.0.tgz", - "integrity": "sha512-UupcGGmtfIXvuXEpJ5h/uvaqExJEXELyxj7VRSAB1JShmaNopRqoNcwfANxtKZ60nFtmBaSz6ix6xc4/gdSK2w==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/logo/-/logo-11.1.1.tgz", + "integrity": "sha512-t+VJ/YMisKWCLy/rgORfcnIfRT1vpHGCBpGLB+SkuvM+HJ+MC/RLOcVh+FipLI/1yOV2PS4gegqFOKXIxDGNmw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.0.3", - "@leafygreen-ui/lib": "^15.6.1", - "@leafygreen-ui/palette": "^5.0.2" + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3" } }, "node_modules/@leafygreen-ui/marketing-modal": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/marketing-modal/-/marketing-modal-8.2.1.tgz", - "integrity": "sha512-3G/9f9gMVRbQTl5B7YlTszfQ8MbkuYnpAVVF1Jhu70nmoTnjd01cZ38D4ky2/4t13cApfWY9uIQ/IerNOZV+6A==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/marketing-modal/-/marketing-modal-8.2.2.tgz", + "integrity": "sha512-xUTMCFn7RoyMOp443L9rqEth1iVUwOFQKJAdqajyXb3podMF0lS2n4zw9XUSe79XxCrwpRaekjgXm2tdv3qA+Q==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/button": "^25.2.1", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/modal": "^22.0.1", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4" + "@leafygreen-ui/button": "^25.2.2", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/modal": "^22.0.2", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@leafygreen-ui/menu": { - "version": "33.2.1", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/menu/-/menu-33.2.1.tgz", - "integrity": "sha512-nCcWKgEgEgJj1AYPmoF79NqtwsupMhaFhDvr8ITESOsP+ihlttpX4eTtl09SzM2Bj8ghQR+l8mXuc0xxYC16tw==", + "version": "33.2.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/menu/-/menu-33.2.2.tgz", + "integrity": "sha512-TZwfBwuN3ooQthTRXpU7OyVZD+B1a3LZvvgin8UrbJ3k3AcshGX/zUeawQ4zSnMRQtpnkn+zKEOrFZZwjYScrg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/descendants": "^3.1.0", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/icon-button": "^17.1.5", - "@leafygreen-ui/input-option": "^4.1.5", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/polymorphic": "^3.1.0", - "@leafygreen-ui/popover": "^14.3.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4", + "@leafygreen-ui/descendants": "^3.1.2", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/icon-button": "^17.1.6", + "@leafygreen-ui/input-option": "^4.1.6", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/polymorphic": "^3.1.1", + "@leafygreen-ui/popover": "^14.3.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5", "lodash": "^4.17.21", "polished": "^4.3.1", "react-transition-group": "^4.4.5" @@ -4097,20 +4097,20 @@ } }, "node_modules/@leafygreen-ui/modal": { - "version": "22.0.1", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/modal/-/modal-22.0.1.tgz", - "integrity": "sha512-hBKBPDr/btuiA1xqVe1aCxxdFucxzJOh8dT2tvzrvReVJtDvTe2RrdZjHoLX6dCX2E+876euCh3cMpuMgWeUyw==", + "version": "22.0.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/modal/-/modal-22.0.2.tgz", + "integrity": "sha512-b44Xy2kDOLFJy90vNBetxUMDDp2QVD1zgz2lWTvSJWAge5wGEJKM6BuJh/i4wfVtFj2V9fHhrW80hMCxOUqDFA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/icon-button": "^17.1.5", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@lg-tools/test-harnesses": "^0.3.4", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/icon-button": "^17.1.6", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@lg-tools/test-harnesses": "^0.3.5", "polished": "^4.2.2" }, "peerDependencies": { @@ -4118,26 +4118,26 @@ } }, "node_modules/@leafygreen-ui/palette": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/palette/-/palette-5.0.2.tgz", - "integrity": "sha512-+PrfGeJSv4goxm/vKpfJJDOP7t/uElj+14K8jiIyu3qR3TcFRIZ5h1VMvICTUgqvRc8W+xIZYQwsLa2XCu2lvw==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/palette/-/palette-5.0.3.tgz", + "integrity": "sha512-7xA4FetYcJSgElQSvvSvRDUnhsgCN7i7kx5EVqu4oy5rHXaD8WuMdR4GFac5d7ea0F5f1KC8v9UpuV2CGBxYoA==", "dev": true, "license": "Apache-2.0" }, "node_modules/@leafygreen-ui/pipeline": { - "version": "8.0.10", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/pipeline/-/pipeline-8.0.10.tgz", - "integrity": "sha512-yjd/Jv07HEdmGe+V8yMzl2589Mv81UQ19EiAvUlkFzLNH66zVMI7r/E55/rrsBodk7cRJfCdjcWXzs1ed8/OfQ==", + "version": "8.0.11", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/pipeline/-/pipeline-8.0.11.tgz", + "integrity": "sha512-Pt8cxcQvniQd65tLNzO6+fxfXMSweaSdOFaGt+kHa+yHpxZ2Kl9qhbX7cJjm3AZFtJvM877YmsRT9sjYfkC0gQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/tooltip": "^14.3.1", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/tooltip": "^14.3.3", "react-intersection-observer": "^8.25.1" }, "peerDependencies": { @@ -4145,29 +4145,29 @@ } }, "node_modules/@leafygreen-ui/polymorphic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/polymorphic/-/polymorphic-3.1.0.tgz", - "integrity": "sha512-5fbXD6ExTmMScvODuipfB1Ti/Dvoaxxg+daSftqXfNQlEkEnd5cPnezOOl1LMsu2xUoZT6NXsFgukZYsmXEVpQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/polymorphic/-/polymorphic-3.1.1.tgz", + "integrity": "sha512-aMxXcpH/Iykf7/6Cr+qrWGitU8kDxkCbGBH3uUQgDvHdVoJhZeuOWQuhmZyELm3LmL7mjpwZyWHka6nj6dQenQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/lib": "^15.4.0", + "@leafygreen-ui/lib": "^15.7.1", "lodash": "^4.17.21" } }, "node_modules/@leafygreen-ui/popover": { - "version": "14.3.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/popover/-/popover-14.3.2.tgz", - "integrity": "sha512-2+qXbiGUcCS1xLzY4lUlZdM4iDXFgrJry8J7MzgzN2JpHIIDJUVz+rycqZiXJnnq7NSd3emRoiviKOGxXGywbw==", + "version": "14.3.3", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/popover/-/popover-14.3.3.tgz", + "integrity": "sha512-cyrXwdGCIiDlGV+Qfl3M7OLhIxr3+X6XoQETImUZwfQoLgUCKNezEnho308kqOdl6jR284IDRx5I4cl/j8wEzA==", "dev": true, "license": "Apache-2.0", "dependencies": { "@floating-ui/react": "^0.26.28", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/portal": "^7.1.0", - "@leafygreen-ui/tokens": "^4.2.2", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/portal": "^7.1.1", + "@leafygreen-ui/tokens": "^4.2.3", "@types/react-transition-group": "^4.4.5", "lodash": "^4.17.21", "react-transition-group": "^4.4.5" @@ -4177,35 +4177,35 @@ } }, "node_modules/@leafygreen-ui/portal": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/portal/-/portal-7.1.0.tgz", - "integrity": "sha512-wCldB70m/NtlIeVRxi5S/U74W6jxQScqptI2I4+7RweBquBfxIg1SipHXqMC+Zo3aL+s/fCMuFKNlLuwGWu8MA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/portal/-/portal-7.1.1.tgz", + "integrity": "sha512-auOUA2IiR+6Apg7/76DgAe6sXUtt5sY+G550OvUGTaa5UKcPP0U2J9b5G5LIWA8b3IYvmfwRQQn+55IYUTKdWQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/hooks": "^9.2.0", - "@leafygreen-ui/lib": "^15.6.1" + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/lib": "^15.7.1" }, "peerDependencies": { "react-dom": "^17.0.0 || ^18.0.0" } }, "node_modules/@leafygreen-ui/progress-bar": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/progress-bar/-/progress-bar-1.0.8.tgz", - "integrity": "sha512-NY1B9AQIqMnutkE0JujjvAU+EeWBQqmSrPZZxWtgA2oIHme1OlXce6cGJT766Ks1UNe6rP6A0xIq4mntBb4Y9Q==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/progress-bar/-/progress-bar-1.0.9.tgz", + "integrity": "sha512-tLF8xRXAZ6dzDvDY7UkYc0E6ViBm6Fs9dUOf3XUvleLsIggVXq7wBZRmSWvuzSmmKpOuy8JaFBnjt7G8iDWP8Q==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/a11y": "^3.0.5", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4", - "@lg-tools/test-harnesses": "^0.3.4", + "@leafygreen-ui/a11y": "^3.0.6", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5", + "@lg-tools/test-harnesses": "^0.3.5", "lodash": "^4.17.21", "polished": "^4.2.2" }, @@ -4214,81 +4214,81 @@ } }, "node_modules/@leafygreen-ui/radio-box-group": { - "version": "15.0.11", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/radio-box-group/-/radio-box-group-15.0.11.tgz", - "integrity": "sha512-BVG9sokWt/yGI4BD7WHKGkD4gayoxWMskZ1HWNA1IWEnkb1F7VFBbVDsa4k0XXlOzxxMBnIdAFo7s6Je2vhI5A==", + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/radio-box-group/-/radio-box-group-15.0.12.tgz", + "integrity": "sha512-+CbLjXNsMUu6lbPHmFAUBmVsOyTDRGdEPTK6sUD/DGZXMDINYiOBmztG3cxFYVo8KmvHKKVsrF1XiJjp2Ctn3g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2" + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@leafygreen-ui/radio-group": { - "version": "13.0.12", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/radio-group/-/radio-group-13.0.12.tgz", - "integrity": "sha512-Mggusg9KRWh75sP92Zi1FiMC1mxnphCYwkuNranjnIMas8fmUkJTFHopLYKp73eA3SuVmU5zTWpregFdxTIWIA==", + "version": "13.0.13", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/radio-group/-/radio-group-13.0.13.tgz", + "integrity": "sha512-q2nH/NqMMNy1YYDYtLnWqgZC8vTV3XHaGCGz1NoX07ERcNc6AM6IilKC+AqioIGedQO6b1SYn3ioovV/C64KVQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4" + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@leafygreen-ui/resizable": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/resizable/-/resizable-0.1.3.tgz", - "integrity": "sha512-hGpRUvqMLsfpqUgAFP5rTgY3viwHP9T8fe0eBL3sbShezvbuLNO9qSVZYQVGg7VNC4wg3reJ1aSQxjn1XYe5Nw==", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/resizable/-/resizable-0.1.4.tgz", + "integrity": "sha512-V1btTPVNMklWzII0K94MZtNPYqfq/Mknacbsuf5QYGlRIM0si3K6s6p/OOfcBUmkKxXgV/yOXStnOHd/3l2YzQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.0.3", - "@leafygreen-ui/lib": "^15.4.0", - "@leafygreen-ui/palette": "^5.0.2" + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3" } }, "node_modules/@leafygreen-ui/ripple": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/ripple/-/ripple-2.0.7.tgz", - "integrity": "sha512-+Ixyb5deA5S+tbLQl4Iad/JWVQhYLeET6tstzYmE/sk6YfJWDyWH8CYVJ3Q9wdZXBJlXoZAi1Vy4ff4x9GSXGA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/ripple/-/ripple-2.0.8.tgz", + "integrity": "sha512-Edf2WEYR9dORZ+jFGpacydw2+ViK1k0J4TmJjOWxI0BndC0+MFKUBy8HzGHxpZLaJo2Xn7FheglPv3KUspwoSA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/tokens": "^4.0.0" + "@leafygreen-ui/tokens": "^4.2.3" } }, "node_modules/@leafygreen-ui/search-input": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/search-input/-/search-input-6.1.3.tgz", - "integrity": "sha512-225f7UpILMcaY8vVKdjm/xLQcsAsa775+SYSbB3OGFlscwATtoNNhdk6XDeXI6gb4fSAxu0RPm8e/ODZEuunig==", + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/search-input/-/search-input-6.1.4.tgz", + "integrity": "sha512-grfIz62a+irIdb5zLvwVl4X21gnVoh1JJr+iRTyK2KnZUJWD3lxe7gz9znCO/dplbdUphqIhOQMltzdltOnzSQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/a11y": "^3.0.5", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/icon-button": "^17.1.5", - "@leafygreen-ui/input-option": "^4.1.5", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/polymorphic": "^3.1.0", - "@leafygreen-ui/popover": "^14.3.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4", + "@leafygreen-ui/a11y": "^3.0.6", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/icon-button": "^17.1.6", + "@leafygreen-ui/input-option": "^4.1.6", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/polymorphic": "^3.1.1", + "@leafygreen-ui/popover": "^14.3.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5", "lodash": "^4.17.21", "polished": "^4.2.2" }, @@ -4297,20 +4297,20 @@ } }, "node_modules/@leafygreen-ui/segmented-control": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/segmented-control/-/segmented-control-11.1.0.tgz", - "integrity": "sha512-TbMFGY3Lt8lEETuiA/CyaBxhcEos1xxyUzoEaImnvrS2Os0RL4WA03CWjeTvuPJ7z2RfBJ7kEte2jeNDgn+E+Q==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/segmented-control/-/segmented-control-11.1.1.tgz", + "integrity": "sha512-8KlraGd/RAuyWTTD1U7bICt4SAMpro4rXMOSf6M6rhygp20yhX47h+hL8WcVv8DUmDSjo7sh3P+lRTCMmPvijw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/polymorphic": "^3.1.0", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/polymorphic": "^3.1.1", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5", "lodash": "^4.17.21", "polished": "^4.2.2" }, @@ -4319,24 +4319,24 @@ } }, "node_modules/@leafygreen-ui/select": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/select/-/select-17.1.0.tgz", - "integrity": "sha512-HA9pe8snzpQC5OtuXPjKKOdDI4EC4ZeG9rk8hx+YXQcA6+Racg+h5R2r/vfOrv8/xThiJdO7jgiZ0/RwgkQsZg==", + "version": "17.1.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/select/-/select-17.1.1.tgz", + "integrity": "sha512-k7PtmRzSS64B89N/3caCeGhWE0NcySR2tw33293CUHYyGPTTt9r4/5kVuPq/6QxJ+YQEczMTbjrC0SRiidU7pA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/button": "^25.2.1", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/form-field": "^4.0.9", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.9.0", - "@leafygreen-ui/input-option": "^4.1.5", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/popover": "^14.3.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4", - "@lg-tools/test-harnesses": "^0.3.4", + "@leafygreen-ui/button": "^25.2.2", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/form-field": "^4.0.10", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/input-option": "^4.1.6", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/popover": "^14.3.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5", + "@lg-tools/test-harnesses": "^0.3.5", "@types/react-is": "^18.0.0", "lodash": "^4.17.21", "polished": "^4.1.3", @@ -4347,20 +4347,20 @@ } }, "node_modules/@leafygreen-ui/skeleton-loader": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/skeleton-loader/-/skeleton-loader-3.0.11.tgz", - "integrity": "sha512-jJYdlrQRZDqwBFMIbHTSr5q1h7K9MdS7u4RDrkWoVk5StKGX9brXFLGGSVFWogGMby4vIqRFoeLZLlO/YLO1NA==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/skeleton-loader/-/skeleton-loader-3.0.12.tgz", + "integrity": "sha512-RVXuHFey/H2XFlvHXSon4iraaJYwgWJS7FDrQUGHvjdpnd8V+d3IOMk6dYUW08THQWqYaBmwSSNrW1rdjcwcNg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/a11y": "^3.0.5", - "@leafygreen-ui/card": "^13.3.1", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4", + "@leafygreen-ui/a11y": "^3.0.6", + "@leafygreen-ui/card": "^13.3.2", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5", "lodash": "^4.17.21" }, "peerDependencies": { @@ -4368,44 +4368,44 @@ } }, "node_modules/@leafygreen-ui/split-button": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/split-button/-/split-button-6.3.1.tgz", - "integrity": "sha512-BcBNKbl2ZLCCfAq0UnUmhhATwgtd31RNDwc3CVIB+tfseZYVlGUMfV4qmaDLeackXhnLEZEDZWmdmezxcQ6A4A==", + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/split-button/-/split-button-6.3.2.tgz", + "integrity": "sha512-liCM2ndpv6D5H+uiznrBcaCjE7sv3mabrtVPtZyzhdf+TqI+8wqF5FYsP4FXxCPdgsmRTzxvAplTTF1NH/yZqg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/button": "^25.2.1", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/menu": "^33.2.1", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/polymorphic": "^3.1.0", - "@leafygreen-ui/tokens": "^4.2.2" + "@leafygreen-ui/button": "^25.2.2", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/menu": "^33.2.2", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/polymorphic": "^3.1.1", + "@leafygreen-ui/tokens": "^4.2.3" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@leafygreen-ui/table": { - "version": "15.2.3", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/table/-/table-15.2.3.tgz", - "integrity": "sha512-T0YY6jdE92sgqa6OyDugRMMdto884CIKa1Fzud2oD8tOmTqENqhD7nC1+JsPEa3VKXFxsNtKbTfRmLMWJMPoVg==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/table/-/table-15.2.4.tgz", + "integrity": "sha512-LnxKxFtL6K1Y+5K+iW6fs3b+XepFhyHkTn9Z84ex6DX/YjJKLzeyJo4zapKk+ng948ydS1wZeEBeyyuFtVdjUg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/checkbox": "^18.1.5", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/icon-button": "^17.1.5", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/polymorphic": "^3.1.0", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4", - "@lg-tools/test-harnesses": "^0.3.4", + "@leafygreen-ui/checkbox": "^18.1.6", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/icon-button": "^17.1.6", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/polymorphic": "^3.1.1", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5", + "@lg-tools/test-harnesses": "^0.3.5", "@tanstack/react-table": "^8.20.5", "@tanstack/react-virtual": "^3.10.7", "lodash": "^4.17.21", @@ -4418,83 +4418,83 @@ } }, "node_modules/@leafygreen-ui/tabs": { - "version": "17.0.11", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/tabs/-/tabs-17.0.11.tgz", - "integrity": "sha512-dC+RMjkJh66Y4w00Y00g+1Bl3oT7m+paJSd1KwhvBVz0zus0vNAAz5XkJcCQvL8+WesPVjrX22ZdBlkaKQGvYA==", + "version": "17.0.12", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tabs/-/tabs-17.0.12.tgz", + "integrity": "sha512-1XimbAKofyX9+D5Zr+PvW9zxTEhh8HehTf1xNOcT0gwLjg5oUu+PwAnEp9dCgW0dqwhPon5edUMl+9kZQUDY/g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/a11y": "^3.0.5", - "@leafygreen-ui/descendants": "^3.1.0", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/polymorphic": "^3.1.0", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4", - "@lg-tools/test-harnesses": "^0.3.4" + "@leafygreen-ui/a11y": "^3.0.6", + "@leafygreen-ui/descendants": "^3.1.2", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/polymorphic": "^3.1.1", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5", + "@lg-tools/test-harnesses": "^0.3.5" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@leafygreen-ui/text-area": { - "version": "12.1.5", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/text-area/-/text-area-12.1.5.tgz", - "integrity": "sha512-bp3j0i+sQf2796lkN6TSJuoV2RMF0sgHJ9vmLN6Ij5ARA7FL7QZbDODTehMtFD3UZk5+6Kl9dEvcYtlfRaqfnQ==", + "version": "12.1.6", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/text-area/-/text-area-12.1.6.tgz", + "integrity": "sha512-tLxB4Pkd1MDkK+6aGCUVhUlgvqPCg3pEnuYPNX8/32IDVWyGSNwWiXdZUTWa01/8HZkMqO7w0oedxNFzwr6sNg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/form-field": "^4.0.9", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4", - "@lg-tools/test-harnesses": "^0.3.4" + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/form-field": "^4.0.10", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5", + "@lg-tools/test-harnesses": "^0.3.5" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@leafygreen-ui/text-input": { - "version": "16.2.3", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/text-input/-/text-input-16.2.3.tgz", - "integrity": "sha512-Jbbbov2aTlE2ucuddTLF5IK68jWPwqmTEzwz/LVl9En7Jc4ksEIUZK+Bs0gRMIj6t/ppgwQlSfcoi9MQjZjHpg==", + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/text-input/-/text-input-16.2.4.tgz", + "integrity": "sha512-0hVjp8PMzNNx/TWh7GA4anT8AeA+nh3Ht0gS2SE+ryj4zr8X0MGodkjtxTQFKbmAhEu8pODFyvcSwF0j99+1ow==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/form-field": "^4.0.9", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4", - "@lg-tools/test-harnesses": "^0.3.4" + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/form-field": "^4.0.10", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5", + "@lg-tools/test-harnesses": "^0.3.5" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@leafygreen-ui/toast": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/toast/-/toast-8.1.3.tgz", - "integrity": "sha512-rds7bCY1GRul+2omciMoaI1lbVqAdorIMPhAQ22kq0HuYSivZnoY0owBI2ye3OfdG6Naf+AEOsHDJ/Y6H94d7A==", + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/toast/-/toast-8.1.4.tgz", + "integrity": "sha512-5RapXKEFy00wmmJkcXXEVj5e5zk6ptGKWrkfD+Z409KBZMxpRTbusfulq5Z/vQXBZ/T5YmYSKsXYfuH9s5WJkw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/icon-button": "^17.1.5", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/portal": "^7.1.0", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/icon-button": "^17.1.6", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/portal": "^7.1.1", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5", "lodash": "^4.17.21", "polished": "^4.2.2", "react-transition-group": "^4.4.5" @@ -4504,74 +4504,74 @@ } }, "node_modules/@leafygreen-ui/toggle": { - "version": "12.1.5", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/toggle/-/toggle-12.1.5.tgz", - "integrity": "sha512-+exS86dWnZs2TqulXZSzs7hkPrXmxqIeJDSHaunzLtpuYO0fiDDBMZvghhWAFmvPCUN63fh95NXWFUjaJQgC1g==", + "version": "12.1.6", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/toggle/-/toggle-12.1.6.tgz", + "integrity": "sha512-Qu1pCgKPp7QRJWvNv8XWcG0P0noQ0+QJEISHxu/kQxmwSDz3Vvv7KdFzflYbS8FLK9HWxWB9WCN3KA9a8Q6ILw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/a11y": "^3.0.5", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@lg-tools/test-harnesses": "^0.3.4" + "@leafygreen-ui/a11y": "^3.0.6", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@lg-tools/test-harnesses": "^0.3.5" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@leafygreen-ui/tokens": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-4.2.2.tgz", - "integrity": "sha512-7E31JJ3ASrS5hJ7RxUA/ax+/nBLmNGwJb0AR8tlQsTHeipVeFwQEyWrxHxFTBv3Qyu3qk4L1n52LDuKFkbuENA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-4.2.3.tgz", + "integrity": "sha512-pLl3/Xn/TXS8NzDmC8JsasZRrwC6uLlNlxRPlGzAtBqCvkwiYSMYFsXyJZ/xELNxCM6zsu+cm2l2bWhGHylxFw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2" + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3" } }, "node_modules/@leafygreen-ui/toolbar": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/toolbar/-/toolbar-1.3.2.tgz", - "integrity": "sha512-S74SYJPAaniKJJ/9ZbhnywPaNCK7wOkutaUKRBlVzvI0/DhISFfEytrTn/v2c2o0RoRdYe751gUjuYInkCWaNQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/toolbar/-/toolbar-1.3.3.tgz", + "integrity": "sha512-QcBb422ZOJiYvE1wJLAp6RHH11VSh9r2XJ/BWahVDyCyQaQ3YqlXwJloFbSiCvBy70w8MRZmWNSCTC0OQjk/4g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/descendants": "^3.1.0", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/icon-button": "^17.1.5", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/tooltip": "^14.3.1", - "@lg-chat/chat-button": "^0.2.2", - "@lg-tools/test-harnesses": "^0.3.4" + "@leafygreen-ui/descendants": "^3.1.2", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/icon-button": "^17.1.6", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/tooltip": "^14.3.3", + "@lg-chat/chat-button": "^0.2.3", + "@lg-tools/test-harnesses": "^0.3.5" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@leafygreen-ui/tooltip": { - "version": "14.3.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/tooltip/-/tooltip-14.3.2.tgz", - "integrity": "sha512-YMIvpVSM/vnZPnAtlbXfx3fyOhwMWZs8R1GLHp3q9i+JpoQ696cg3jepnjqHc2Hu2AUH/g4fMrql5raw8WHk8Q==", + "version": "14.3.3", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tooltip/-/tooltip-14.3.3.tgz", + "integrity": "sha512-0IbuA9NmJqmZecuexLJQKCN5wd8u12F8deKyF6L78gYN79fSPFNosbWePbPeeTBweEpWyjCdjfVA60Sm9r1XGg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.9.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/popover": "^14.3.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/popover": "^14.3.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5", "lodash": "^4.17.21", "polished": "^4.2.2" }, @@ -4580,56 +4580,56 @@ } }, "node_modules/@leafygreen-ui/typography": { - "version": "22.2.4", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/typography/-/typography-22.2.4.tgz", - "integrity": "sha512-ffMvuuANOWPbuWwJQcn257YROMT2WJwLV+xlG2l8UH3Gb5ldxiTJAdKqtXIshLjmkrRwv2O7ylHBXsHPMYjUjA==", + "version": "22.2.5", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/typography/-/typography-22.2.5.tgz", + "integrity": "sha512-r393zIcAGaONc3GJeWYqV4wo8ysRB+vr5Uov8OlDpyRLs5TiA0JQ+YzqimOan2uOp36qolbqvW0sMyEphrv0XA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/polymorphic": "^3.1.0", - "@leafygreen-ui/tokens": "^4.2.2" + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/polymorphic": "^3.1.1", + "@leafygreen-ui/tokens": "^4.2.3" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@lg-chat/chat-button": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@lg-chat/chat-button/-/chat-button-0.2.2.tgz", - "integrity": "sha512-yCCOPnbYbOeUQ+/vQCGUmdZZJHEeGapn0uH48zf87B7YIQbpC7VBDwmO0XXSb8/+YnpssIioeJWzEZt2bDsWZw==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@lg-chat/chat-button/-/chat-button-0.2.3.tgz", + "integrity": "sha512-ZqPCH4dXsjUtyH0jo/tN+8CC0EJV7nsZugtfDP6fhrDExJT0nIQHyHNry1tqREZdc5mMuixTI4aACtY8bY81ZQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/avatar": "^3.3.0", - "@leafygreen-ui/button": "^25.2.1", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/icon-button": "^17.1.5", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@lg-tools/test-harnesses": "^0.3.4" + "@leafygreen-ui/avatar": "^3.3.1", + "@leafygreen-ui/button": "^25.2.2", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/icon-button": "^17.1.6", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@lg-tools/test-harnesses": "^0.3.5" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@lg-chat/chat-window": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@lg-chat/chat-window/-/chat-window-6.0.2.tgz", - "integrity": "sha512-v0h9iCDP9OVcP42XXG/R5ZNsiTPEZgSrbYgK7tPiX7bPKJc+DtOPy2Gc3+hMQLHdnr1/4Rptq96SnVdmVrbv8A==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@lg-chat/chat-window/-/chat-window-6.0.3.tgz", + "integrity": "sha512-VSAGTVwfKvHBsHMT0owj48ThD/OuiJXXyz0Wco6YywXbT+E3IhW/HOJkzgR19dofyQ67uVxbpPZBfhOm2k9MhQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@lg-chat/title-bar": "^5.0.2", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@lg-chat/title-bar": "^5.0.3", "react-keyed-flatten-children": "^2.2.1" }, "peerDependencies": { @@ -4638,28 +4638,28 @@ } }, "node_modules/@lg-chat/input-bar": { - "version": "12.2.2", - "resolved": "https://registry.npmjs.org/@lg-chat/input-bar/-/input-bar-12.2.2.tgz", - "integrity": "sha512-Yv6RrgBv599Yd34BxaE66Pa5tvFqg8BYf+cS8ZKIXjjClOaW6lRCm5lUMgrTaTAZn3ajdL6D6RqB9oKrNuDBbQ==", + "version": "12.2.3", + "resolved": "https://registry.npmjs.org/@lg-chat/input-bar/-/input-bar-12.2.3.tgz", + "integrity": "sha512-qBWaucJ/9PXK5oFjL3Pgdn5o2FSUtQ6paq7kbxkplcHkUVTWPuTJs3hMeynOriUMmwmrGOTQceMTPgVZIrOPIg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/avatar": "^3.3.0", - "@leafygreen-ui/banner": "^10.2.5", - "@leafygreen-ui/button": "^25.2.1", - "@leafygreen-ui/compound-component": "^0.3.0", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/icon-button": "^17.1.5", - "@leafygreen-ui/input-option": "^4.1.5", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/polymorphic": "^3.1.0", - "@leafygreen-ui/popover": "^14.3.2", - "@leafygreen-ui/search-input": "^6.1.3", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4", + "@leafygreen-ui/avatar": "^3.3.1", + "@leafygreen-ui/banner": "^10.2.6", + "@leafygreen-ui/button": "^25.2.2", + "@leafygreen-ui/compound-component": "^0.3.1", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/icon-button": "^17.1.6", + "@leafygreen-ui/input-option": "^4.1.6", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/polymorphic": "^3.1.1", + "@leafygreen-ui/popover": "^14.3.3", + "@leafygreen-ui/search-input": "^6.1.4", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5", "lodash": "^4.17.21", "react-keyed-flatten-children": "^1.3.0", "react-textarea-autosize": "^8.5.9" @@ -4690,25 +4690,25 @@ } }, "node_modules/@lg-chat/leafygreen-chat-provider": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@lg-chat/leafygreen-chat-provider/-/leafygreen-chat-provider-6.0.0.tgz", - "integrity": "sha512-zhvqAPpHX0VVOvrsVKs7H37ImIEOGuTLarULXEW4dwglKGqhbagmdN2UEje++wDAD/JqnAYEaQYzrPFH2EIgWQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@lg-chat/leafygreen-chat-provider/-/leafygreen-chat-provider-6.0.1.tgz", + "integrity": "sha512-WPJzEgMwXPKyB+6FHVr5zEyzkxOr4/92Ck/+DgnhRWTsPYCmTNuxgko300IIDeVEXw4VV+CHIo5BDEUFQKS9ng==", "dev": true, "license": "Apache-2.0" }, "node_modules/@lg-chat/lg-markdown": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@lg-chat/lg-markdown/-/lg-markdown-5.0.3.tgz", - "integrity": "sha512-zqbCIe1S1BcKEO6RcU1d1mntRC6huIJhzhL/8oJsFP4EpfZu0xi3xh7PK4/wPJv0E7oznHPfnUC9AAxL3jytaw==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@lg-chat/lg-markdown/-/lg-markdown-5.0.4.tgz", + "integrity": "sha512-ppsrvkYyevVA+991bXkgeegIpxuHwP7wik/w2Rc/iV8SQfjXjxGoqihW4MM3TCZTtK3zcvSfkvMDPU0iuMlfRQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/code": "^20.2.7", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4", + "@leafygreen-ui/code": "^20.2.9", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5", "react-markdown": "^8.0.7" }, "peerDependencies": { @@ -4716,32 +4716,32 @@ } }, "node_modules/@lg-chat/message": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@lg-chat/message/-/message-12.0.0.tgz", - "integrity": "sha512-ZVz6L4Ju8U1zByQLDEYJfOWKzJUCVraiuw/GrcR0fG+xIjkT572oZ7P3EYzSnNPxpMLl46/ovSwlXrd6faSNTQ==", + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@lg-chat/message/-/message-12.0.1.tgz", + "integrity": "sha512-/qEs7KwTFlnxySz8tnqQnyiEU6r2cj3huQsOyXKpcyE8GF1rq2HNQYk/eGJFMB+GDhb/EiSW1ih+z6V8dOxxeg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/badge": "^10.2.4", - "@leafygreen-ui/banner": "^10.2.5", - "@leafygreen-ui/button": "^25.2.1", - "@leafygreen-ui/chip": "^4.2.1", - "@leafygreen-ui/compound-component": "^0.3.0", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/icon-button": "^17.1.5", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/loading-indicator": "^5.1.1", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/polymorphic": "^3.1.0", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/tooltip": "^14.3.1", - "@leafygreen-ui/typography": "^22.2.4", - "@lg-chat/lg-markdown": "^5.0.3", - "@lg-chat/message-feedback": "^9.0.2", - "@lg-chat/message-rating": "^7.1.2", - "@lg-chat/rich-links": "^4.0.8" + "@leafygreen-ui/badge": "^10.2.5", + "@leafygreen-ui/banner": "^10.2.6", + "@leafygreen-ui/button": "^25.2.2", + "@leafygreen-ui/chip": "^4.2.2", + "@leafygreen-ui/compound-component": "^0.3.1", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/icon-button": "^17.1.6", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/loading-indicator": "^5.1.2", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/polymorphic": "^3.1.1", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/tooltip": "^14.3.3", + "@leafygreen-ui/typography": "^22.2.5", + "@lg-chat/lg-markdown": "^5.0.4", + "@lg-chat/message-feedback": "^9.0.3", + "@lg-chat/message-rating": "^7.1.3", + "@lg-chat/rich-links": "^4.0.9" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0", @@ -4749,22 +4749,22 @@ } }, "node_modules/@lg-chat/message-feedback": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@lg-chat/message-feedback/-/message-feedback-9.0.2.tgz", - "integrity": "sha512-Q7L4WNxmcmNZ/tqun863UX0jn8P3N9jYlLW/pXiPORG2v2HeG27U+I+yaFNoZOFYvBR2hlRXm1qnkvd+Z4N/PQ==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@lg-chat/message-feedback/-/message-feedback-9.0.3.tgz", + "integrity": "sha512-JlhdFNXbaoSBkS6h10Wq1UaFP3SUnHXaHknSgubN/QbvkGENZnKjO87C4/20C2mSFszsQmkSA8YnREYTbwGdaA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/button": "^25.2.1", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/icon-button": "^17.1.5", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/text-area": "^12.1.5", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4" + "@leafygreen-ui/button": "^25.2.2", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/icon-button": "^17.1.6", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/text-area": "^12.1.6", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0", @@ -4772,40 +4772,40 @@ } }, "node_modules/@lg-chat/message-prompts": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@lg-chat/message-prompts/-/message-prompts-4.2.2.tgz", - "integrity": "sha512-JBf5ns7tkk9Xx2GvLQQ0V8gNGPYX4fwjkl5FgQV2kBEgEC80DihfE6OdxyZQ4OwRWOrWL4wTlSXCmbsaLBh9PA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@lg-chat/message-prompts/-/message-prompts-4.2.3.tgz", + "integrity": "sha512-8nC8MdrNK62OvyNTJKCaultjBM+ahsdNcJNrNKLk9qqnPtuDU3ZiR4y/ni8mF8YDTXYdsjNx12Dm/3HmAGhVqw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/icon-button": "^17.1.5", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/tooltip": "^14.3.1", - "@leafygreen-ui/typography": "^22.2.4" + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/icon-button": "^17.1.6", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/tooltip": "^14.3.3", + "@leafygreen-ui/typography": "^22.2.5" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@lg-chat/message-rating": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@lg-chat/message-rating/-/message-rating-7.1.2.tgz", - "integrity": "sha512-WyFwsDQrLZzZupjNnIJp9QRNKCDRjR0CtQ8r5TX7swx0lg1iTkRu7p56bTXbZYfV2loB6VyqHzWfcqrKrtyl9g==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@lg-chat/message-rating/-/message-rating-7.1.3.tgz", + "integrity": "sha512-M2KosL/c7pPoZEvTY6EMFlyZyOrBT4TNakPg4gKAEbLEshqPuHYYSacpEvrQwP4JuoZUQacJS6b6Q0eLNbuLDQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/icon-button": "^17.1.5", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/tooltip": "^14.3.1" + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/icon-button": "^17.1.6", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/tooltip": "^14.3.3" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0", @@ -4813,37 +4813,37 @@ } }, "node_modules/@lg-chat/rich-links": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@lg-chat/rich-links/-/rich-links-4.0.8.tgz", - "integrity": "sha512-Iu5XHywEmlSU86VpzzGys0h0CvlLXBnFyC7q0V6K9SF2WNq1e4iMAICsFBDkIoK/+dbMw2tUrXCzQHgoTy59Ww==", + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@lg-chat/rich-links/-/rich-links-4.0.9.tgz", + "integrity": "sha512-hCb/urya0niNz6wsswzUR6L9fqlaPppN8pEtzq7mIY6QgyvmxtL9Q6NLzt4HarMfEWjPNRRAoKEjJeV+MB++uA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/card": "^13.3.1", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/icon": "^14.8.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/polymorphic": "^3.1.0", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4" + "@leafygreen-ui/card": "^13.3.2", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/polymorphic": "^3.1.1", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0" } }, "node_modules/@lg-chat/title-bar": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@lg-chat/title-bar/-/title-bar-5.0.2.tgz", - "integrity": "sha512-iB2G2oZr0XMz1uyrKgfvpGljg0THxqhIFkyjttg7X5sgAdvYtp9VBbgChz5COsMEHsecip5XwDIfTVbZADQvBw==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@lg-chat/title-bar/-/title-bar-5.0.3.tgz", + "integrity": "sha512-azZouDqJn8ZYkNy04sZsHc0PxUALwV06ecXREWHsqGEf82AKJ/vRyst88Z8jRs9m54xHZpSQP5FEiOe6fyBBTg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/badge": "^10.2.4", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/lib": "^15.7.0", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/typography": "^22.2.4" + "@leafygreen-ui/badge": "^10.2.5", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/lib": "^15.7.1", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/typography": "^22.2.5" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^5.0.0 || ^4.0.0 || ^3.2.0", @@ -4851,9 +4851,9 @@ } }, "node_modules/@lg-tools/test-harnesses": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@lg-tools/test-harnesses/-/test-harnesses-0.3.4.tgz", - "integrity": "sha512-JfJj2LSMe5vTSDQoLxWUHx2r4wUgKqU1UrgqjvNYM7iebXE0JCE7RvLiEg5SnsRO8xXQbEMjgISErmCDR4DS7Q==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@lg-tools/test-harnesses/-/test-harnesses-0.3.5.tgz", + "integrity": "sha512-3NdrBTdpUf1uhhn15Mu/6RAp5bycY55vmQS3ZcCart37F4n6+5WXtFVMTcW0BPXOm/Vcy8CL9Y8sAKOLVByUZQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4865,79 +4865,78 @@ "link": true }, "node_modules/@mongodb-js/compass-components": { - "version": "1.70.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/compass-components/-/compass-components-1.70.0.tgz", - "integrity": "sha512-ysgl0uGC5a8T2hrBIHb/vfrnP+g4v9cEf/9eBloyWhtBCT1XOB/XskS7Nb8OT9pPiie7NmqEMKAZ6WM9FsOy8A==", + "version": "1.70.1", + "resolved": "https://registry.npmjs.org/@mongodb-js/compass-components/-/compass-components-1.70.1.tgz", + "integrity": "sha512-8znkFBcg6VUjnuJtqJBte4tnfP/B9XXrTm0E1pcslLOP38EmLi2PMFzTCvld5OrciTledwLbRtX48y9L4vxCEg==", "dev": true, "license": "SSPL", "dependencies": { "@dnd-kit/core": "^6.0.7", "@dnd-kit/sortable": "^7.0.2", "@dnd-kit/utilities": "^3.2.1", - "@leafygreen-ui/avatar": "^3.3.0", - "@leafygreen-ui/badge": "^10.2.4", - "@leafygreen-ui/banner": "^10.2.5", - "@leafygreen-ui/button": "^25.2.1", - "@leafygreen-ui/card": "^13.3.1", - "@leafygreen-ui/checkbox": "^18.1.5", - "@leafygreen-ui/chip": "^4.2.1", - "@leafygreen-ui/code": "^20.2.8", - "@leafygreen-ui/combobox": "^12.5.1", - "@leafygreen-ui/confirmation-modal": "^11.0.1", - "@leafygreen-ui/copyable": "^12.0.3", - "@leafygreen-ui/drawer": "^5.2.3", - "@leafygreen-ui/emotion": "^5.2.0", - "@leafygreen-ui/guide-cue": "^8.2.0", - "@leafygreen-ui/hooks": "^9.3.1", - "@leafygreen-ui/icon": "^14.9.0", - "@leafygreen-ui/icon-button": "^17.1.5", - "@leafygreen-ui/info-sprinkle": "^5.0.11", - "@leafygreen-ui/input-option": "^4.1.5", - "@leafygreen-ui/leafygreen-provider": "^5.0.4", - "@leafygreen-ui/logo": "^11.1.0", - "@leafygreen-ui/marketing-modal": "^8.2.1", - "@leafygreen-ui/menu": "^33.2.1", - "@leafygreen-ui/modal": "^22.0.1", - "@leafygreen-ui/palette": "^5.0.2", - "@leafygreen-ui/pipeline": "^8.0.10", - "@leafygreen-ui/polymorphic": "^3.1.0", - "@leafygreen-ui/popover": "^14.3.2", - "@leafygreen-ui/portal": "^7.1.0", - "@leafygreen-ui/progress-bar": "^1.0.8", - "@leafygreen-ui/radio-box-group": "^15.0.11", - "@leafygreen-ui/radio-group": "^13.0.12", - "@leafygreen-ui/search-input": "^6.1.3", - "@leafygreen-ui/segmented-control": "^11.1.0", - "@leafygreen-ui/select": "^17.1.0", - "@leafygreen-ui/skeleton-loader": "^3.0.11", - "@leafygreen-ui/split-button": "^6.3.1", - "@leafygreen-ui/table": "^15.2.3", - "@leafygreen-ui/tabs": "^17.0.11", - "@leafygreen-ui/text-area": "^12.1.5", - "@leafygreen-ui/text-input": "^16.2.3", - "@leafygreen-ui/toast": "^8.1.3", - "@leafygreen-ui/toggle": "^12.1.5", - "@leafygreen-ui/tokens": "^4.2.2", - "@leafygreen-ui/tooltip": "^14.3.2", - "@leafygreen-ui/typography": "^22.2.4", - "@lg-chat/chat-window": "^6.0.2", - "@lg-chat/input-bar": "^12.2.2", - "@lg-chat/leafygreen-chat-provider": "^6.0.0", - "@lg-chat/message": "^12.0.0", - "@lg-chat/message-prompts": "^4.2.2", - "@mongodb-js/compass-context-menu": "^0.3.17", + "@leafygreen-ui/avatar": "^3.3.1", + "@leafygreen-ui/badge": "^10.2.5", + "@leafygreen-ui/banner": "^10.2.6", + "@leafygreen-ui/button": "^25.2.2", + "@leafygreen-ui/card": "^13.3.2", + "@leafygreen-ui/checkbox": "^18.1.6", + "@leafygreen-ui/chip": "^4.2.2", + "@leafygreen-ui/code": "^20.2.9", + "@leafygreen-ui/combobox": "^12.5.2", + "@leafygreen-ui/confirmation-modal": "^11.0.2", + "@leafygreen-ui/copyable": "^12.0.4", + "@leafygreen-ui/drawer": "^5.2.4", + "@leafygreen-ui/emotion": "^5.2.1", + "@leafygreen-ui/guide-cue": "^8.2.1", + "@leafygreen-ui/hooks": "^9.3.2", + "@leafygreen-ui/icon": "^14.9.1", + "@leafygreen-ui/icon-button": "^17.1.6", + "@leafygreen-ui/info-sprinkle": "^5.0.12", + "@leafygreen-ui/input-option": "^4.1.6", + "@leafygreen-ui/leafygreen-provider": "^5.0.5", + "@leafygreen-ui/logo": "^11.1.1", + "@leafygreen-ui/marketing-modal": "^8.2.2", + "@leafygreen-ui/menu": "^33.2.2", + "@leafygreen-ui/modal": "^22.0.2", + "@leafygreen-ui/palette": "^5.0.3", + "@leafygreen-ui/pipeline": "^8.0.11", + "@leafygreen-ui/polymorphic": "^3.1.1", + "@leafygreen-ui/popover": "^14.3.3", + "@leafygreen-ui/portal": "^7.1.1", + "@leafygreen-ui/progress-bar": "^1.0.9", + "@leafygreen-ui/radio-box-group": "^15.0.12", + "@leafygreen-ui/radio-group": "^13.0.13", + "@leafygreen-ui/search-input": "^6.1.4", + "@leafygreen-ui/segmented-control": "^11.1.1", + "@leafygreen-ui/select": "^17.1.1", + "@leafygreen-ui/skeleton-loader": "^3.0.12", + "@leafygreen-ui/split-button": "^6.3.2", + "@leafygreen-ui/table": "^15.2.4", + "@leafygreen-ui/tabs": "^17.0.12", + "@leafygreen-ui/text-area": "^12.1.6", + "@leafygreen-ui/text-input": "^16.2.4", + "@leafygreen-ui/toast": "^8.1.4", + "@leafygreen-ui/toggle": "^12.1.6", + "@leafygreen-ui/tokens": "^4.2.3", + "@leafygreen-ui/tooltip": "^14.3.3", + "@leafygreen-ui/typography": "^22.2.5", + "@lg-chat/chat-window": "^6.0.3", + "@lg-chat/input-bar": "^12.2.3", + "@lg-chat/leafygreen-chat-provider": "^6.0.1", + "@lg-chat/message": "^12.0.1", + "@lg-chat/message-prompts": "^4.2.3", + "@mongodb-js/compass-context-menu": "^0.3.18", "@mongodb-js/diagramming": "^2.4.1", "@react-aria/interactions": "^3.9.1", "@react-aria/utils": "^3.13.1", "@react-aria/visually-hidden": "^3.3.1", "bson": "^7.3.2", "focus-trap-react": "^9.0.2", - "hadron-document": "^8.12.1", - "hadron-type-checker": "^7.5.10", + "hadron-document": "^8.12.2", + "hadron-type-checker": "^7.5.11", "is-electron-renderer": "^2.0.1", "lodash": "^4.18.1", - "mongodb-query-parser": "^5.0.5", - "mongodb-query-util": "^2.6.10", + "mongodb-query-util": "^2.6.11", "polished": "^4.2.2", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -4948,9 +4947,9 @@ } }, "node_modules/@mongodb-js/compass-context-menu": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@mongodb-js/compass-context-menu/-/compass-context-menu-0.3.17.tgz", - "integrity": "sha512-YzEvsARlPBrzFeFl7xW/Jt92x9KIaIoA3my/yIXzUyeDF/AYe6lvHi4AfcrufYdeLw2wg7KcSUJKcTc4pKdNig==", + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@mongodb-js/compass-context-menu/-/compass-context-menu-0.3.18.tgz", + "integrity": "sha512-BWpcRkW+DEpeJl/UioaP+WsWlNtOoR+8VSJTsuA0QPVhKJzZciPyGXCY1FQ2fqmrjKYsYWQA5182BiyC8gyL2A==", "dev": true, "license": "SSPL", "dependencies": { @@ -6260,9 +6259,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6277,9 +6273,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6294,9 +6287,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6311,9 +6301,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -15899,24 +15886,24 @@ } }, "node_modules/hadron-document": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/hadron-document/-/hadron-document-8.12.1.tgz", - "integrity": "sha512-fHjfU8iKLsZ5dNEngzfM/ox1Y/4iNqnT9WHIbzLG4+OC/Cw/hwK+Vv6JUF5yIrGGuZHFwoomN1FI5NlEbLmEeA==", + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/hadron-document/-/hadron-document-8.12.2.tgz", + "integrity": "sha512-g6rwi+xz/AAXEb+UI81Em4grwR3+rxsh2RiCs17tGiDBFl5Bx7JrljBgfIhS8nPHpmB8u2yHgNEjfWyr8X4qyg==", "dev": true, "license": "SSPL", "dependencies": { "bson": "^7.3.2", "eventemitter3": "^4.0.0", - "hadron-type-checker": "^7.5.10", + "hadron-type-checker": "^7.5.11", "lodash": "^4.18.1", "mongodb": "^7.5.0", - "mongodb-query-parser": "^5.0.5" + "mongodb-query-parser": "^5.1.3" } }, "node_modules/hadron-type-checker": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/hadron-type-checker/-/hadron-type-checker-7.5.10.tgz", - "integrity": "sha512-Cz0CjuW6JSaKgSqjLxWL91aDPd4nuNNCFKr+HxKRnnLefpn0gNqFgT7/bqBrlg1hjpj9QUg4i4Dl3hYhSLvVsA==", + "version": "7.5.11", + "resolved": "https://registry.npmjs.org/hadron-type-checker/-/hadron-type-checker-7.5.11.tgz", + "integrity": "sha512-NhOtbfifN7DHWoMZ2dxOMCUU2knKnrDdSbfQDcXSHFM5iw+BHSv+oBZdR21pOBOjTYeq0pAsC4Yfjuap6oLZTQ==", "dev": true, "license": "SSPL", "dependencies": { @@ -20491,18 +20478,31 @@ "link": true }, "node_modules/mongodb-query-parser": { - "resolved": "packages/query-parser", - "link": true + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/mongodb-query-parser/-/mongodb-query-parser-5.1.3.tgz", + "integrity": "sha512-nftNIxEqfPF+8UFlACutzfYxeQKfl8vM/68VKQrVw8Z1CuaQWR2vA8jYh0nwueMGTV0XXEw5+Z99rx4inNZ9ag==", + "deprecated": "This package is deprecated. Use @mongodb-js/shell-bson-parser instead.", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/shell-bson-parser": "1.6.3", + "debug": "^4.4.0", + "javascript-stringify": "^2.1.0", + "lodash": "^4.17.21" + }, + "peerDependencies": { + "bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.0.0" + } }, "node_modules/mongodb-query-util": { - "version": "2.6.10", - "resolved": "https://registry.npmjs.org/mongodb-query-util/-/mongodb-query-util-2.6.10.tgz", - "integrity": "sha512-aHEB/WzRzDLqSWgnIhWeWOS38RF2BhUwnCSfs84tyrAKmEBhSw7OJVKEv4tD+Lp2DQcpuq4ihMJQhBm38N3piQ==", + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/mongodb-query-util/-/mongodb-query-util-2.6.11.tgz", + "integrity": "sha512-m8SD/XqOxePXXwpMVIoJa5WGPmhTN2A4E1LFjDA5XB8QQH5WkvrQKQ+Wp+X3H2kbZLavk+DzS3pejnAeXR1soQ==", "dev": true, "license": "SSPL", "dependencies": { "bson": "^7.3.2", - "hadron-type-checker": "^7.5.10", + "hadron-type-checker": "^7.5.11", "lodash": "^4.18.1" } }, @@ -27698,6 +27698,12 @@ "node": ">= 8" } }, + "node_modules/web-worker": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz", + "integrity": "sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==", + "license": "Apache-2.0" + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -29883,10 +29889,11 @@ "license": "Apache-2.0", "devDependencies": { "@leafygreen-ui/emotion": "^5.2.0", - "@mongodb-js/compass-components": "^1.69.0", + "@mongodb-js/compass-components": "^1.70.1", "@mongodb-js/eslint-config-devtools": "^0.11.13", "@mongodb-js/mocha-config-devtools": "^2.0.2", "@mongodb-js/prettier-config-devtools": "^1.0.6", + "@mongodb-js/shell-bson-parser": "1.6.3", "@mongodb-js/tsconfig-devtools": "^1.1.5", "@types/chai": "^4.2.21", "@types/mocha": "^9.1.1", @@ -29897,7 +29904,6 @@ "eslint": "^7.25.0 || ^8.0.0", "gen-esm-wrapper": "^1.1.3", "mocha": "^8.4.0", - "mongodb-query-parser": "^5.1.3", "nyc": "^15.1.0", "prettier": "^3.8.1", "react": "^18.3.1", @@ -30086,39 +30092,33 @@ "node": "^20.19.0 || ^22.12.0 || >=23" } }, - "packages/query-parser": { - "name": "mongodb-query-parser", - "version": "5.1.3", - "deprecated": "This package is deprecated. Use @mongodb-js/shell-bson-parser instead.", - "license": "Apache-2.0", + "packages/query-parser/node_modules/@types/node": { + "version": "25.9.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.4.tgz", + "integrity": "sha512-dszCsrKb5U7ZsVZBWiHFklTloVl0mSEnWH/iZXfZUlI4rzCUnsvGmgqfuVRHL54ugE7/wRuxEIXRa2iMZ+BG6g==", + "extraneous": true, + "license": "MIT", "dependencies": { - "@mongodb-js/shell-bson-parser": "1.6.3", - "debug": "^4.4.0", - "javascript-stringify": "^2.1.0", - "lodash": "^4.17.21" - }, - "devDependencies": { - "@mongodb-js/eslint-config-devtools": "^0.11.13", - "@mongodb-js/mocha-config-devtools": "^2.0.2", - "@mongodb-js/prettier-config-devtools": "^1.0.6", - "@mongodb-js/tsconfig-devtools": "^1.1.5", - "@types/chai": "^4.2.21", - "@types/mocha": "^9.1.1", - "@types/node": "^25.5.2", - "bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.0.0", - "depcheck": "^1.4.7", - "eslint": "^7.25.0 || ^8.0.0", - "gen-esm-wrapper": "^1.1.3", - "mocha": "^8.4.0", - "nyc": "^15.1.0", - "prettier": "^3.8.1", - "sinon": "^9.2.3", - "typescript": "^5.9.3" - }, - "peerDependencies": { - "bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.0.0" + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "packages/query-parser/node_modules/bson": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-7.3.1.tgz", + "integrity": "sha512-h/C0qe6857pQhcSJHLfsR1uYGj98Ge3wKAD3Ed9KqH3wcVh+BM4Jq4xISD7vs9OPuT07n+q3QQVjslJ286j6ag==", + "extraneous": true, + "license": "Apache-2.0", + "engines": { + "node": ">=20.19.0" } }, + "packages/query-parser/node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "extraneous": true, + "license": "MIT" + }, "packages/saslprep": { "name": "@mongodb-js/saslprep", "version": "1.6.0", @@ -30208,7 +30208,8 @@ "license": "Apache-2.0", "dependencies": { "acorn": "^8.18.0", - "javascript-stringify": "^2.1.0" + "javascript-stringify": "^2.1.0", + "web-worker": "^1.5.0" }, "devDependencies": { "@mongodb-js/eslint-config-devtools": "^0.11.13", @@ -30223,12 +30224,12 @@ "chai": "^4.5.0", "depcheck": "^1.4.7", "eslint": "^7.25.0 || ^8.0.0", - "gen-esm-wrapper": "^1.1.3", "mocha": "^8.4.0", "nyc": "^15.1.0", "prettier": "^3.8.1", "sinon": "^9.2.3", - "typescript": "^5.9.3" + "typescript": "^5.9.3", + "webpack": "^5.82.0" }, "peerDependencies": { "bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.0.0" diff --git a/packages/mql-typescript/src/driverSchema/docs-crawler.ts b/packages/mql-typescript/src/driverSchema/docs-crawler.ts index d2ef86e1..6fea524c 100644 --- a/packages/mql-typescript/src/driverSchema/docs-crawler.ts +++ b/packages/mql-typescript/src/driverSchema/docs-crawler.ts @@ -14,7 +14,7 @@ export class DocsCrawler { private virtualConsole: VirtualConsole; - private fuzzyParse(json: string): unknown[] | undefined { + private async fuzzyParse(json: string): Promise { // Sometimes the snippet will end with ellipsis instead of json json = json.replace(/\.\.\.$/g, ''); @@ -38,7 +38,7 @@ export class DocsCrawler { ); try { - let result = parse(json, { mode: ParseMode.Loose }); + let result = await parse(json, { mode: ParseMode.Loose }); if (!Array.isArray(result)) { result = [result]; @@ -70,10 +70,10 @@ export class DocsCrawler { } } - private getInsertionCode( + private async getInsertionCode( element: Element | null | undefined, extendedSearch = false, - ): { collectionName: string; documents: unknown[] } | undefined { + ): Promise<{ collectionName: string; documents: unknown[] } | undefined> { if (!element) { return undefined; } @@ -112,7 +112,7 @@ export class DocsCrawler { } } - const documents = this.fuzzyParse(codeSnippet); + const documents = await this.fuzzyParse(codeSnippet); if (documents) { collectionName ??= Array.from(element?.querySelectorAll('p') || []) @@ -133,7 +133,7 @@ export class DocsCrawler { element?.querySelector("a[href='#examples']") ?? element?.querySelector("a[href='#example']"); if (examples) { - return this.getInsertionCode(element, true); + return await this.getInsertionCode(element, true); } } @@ -156,7 +156,7 @@ export class DocsCrawler { .querySelector(`a[href='${fragment}']:not([target])`) ?.closest('section'); - const insertionCode = this.getInsertionCode(exampleSection); + const insertionCode = await this.getInsertionCode(exampleSection); if (!insertionCode) { return; diff --git a/packages/oidc-http-server-pages/.depcheckrc b/packages/oidc-http-server-pages/.depcheckrc index 54702a0a..16f81e8c 100644 --- a/packages/oidc-http-server-pages/.depcheckrc +++ b/packages/oidc-http-server-pages/.depcheckrc @@ -4,6 +4,6 @@ ignores: - '@types/chai' - 'sinon' # this is required due to the compass-components package - - 'mongodb-query-parser' + - '@mongodb-js/shell-bson-parser' ignore-patterns: - 'dist' diff --git a/packages/oidc-http-server-pages/package.json b/packages/oidc-http-server-pages/package.json index 63ad95bc..be698373 100644 --- a/packages/oidc-http-server-pages/package.json +++ b/packages/oidc-http-server-pages/package.json @@ -53,10 +53,11 @@ }, "devDependencies": { "@leafygreen-ui/emotion": "^5.2.0", - "@mongodb-js/compass-components": "^1.69.0", + "@mongodb-js/compass-components": "^1.70.1", "@mongodb-js/eslint-config-devtools": "^0.11.13", "@mongodb-js/mocha-config-devtools": "^2.0.2", "@mongodb-js/prettier-config-devtools": "^1.0.6", + "@mongodb-js/shell-bson-parser": "1.6.3", "@mongodb-js/tsconfig-devtools": "^1.1.5", "@types/chai": "^4.2.21", "@types/mocha": "^9.1.1", @@ -67,7 +68,6 @@ "eslint": "^7.25.0 || ^8.0.0", "gen-esm-wrapper": "^1.1.3", "mocha": "^8.4.0", - "mongodb-query-parser": "^5.1.3", "nyc": "^15.1.0", "prettier": "^3.8.1", "react": "^18.3.1", diff --git a/packages/query-parser/.depcheckrc b/packages/query-parser/.depcheckrc deleted file mode 100644 index 86cd7d1f..00000000 --- a/packages/query-parser/.depcheckrc +++ /dev/null @@ -1,7 +0,0 @@ -ignores: - - '@mongodb-js/prettier-config-devtools' - - '@mongodb-js/tsconfig-devtools' - - '@types/chai' - - 'sinon' -ignore-patterns: - - 'dist' diff --git a/packages/query-parser/.eslintignore b/packages/query-parser/.eslintignore deleted file mode 100644 index 85a8a75e..00000000 --- a/packages/query-parser/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -.nyc-output -dist diff --git a/packages/query-parser/.eslintrc.js b/packages/query-parser/.eslintrc.js deleted file mode 100644 index 83296d73..00000000 --- a/packages/query-parser/.eslintrc.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - root: true, - extends: ['@mongodb-js/eslint-config-devtools'], - parserOptions: { - tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], - }, -}; diff --git a/packages/query-parser/.prettierignore b/packages/query-parser/.prettierignore deleted file mode 100644 index 4d28df66..00000000 --- a/packages/query-parser/.prettierignore +++ /dev/null @@ -1,3 +0,0 @@ -.nyc_output -dist -coverage diff --git a/packages/query-parser/.prettierrc.json b/packages/query-parser/.prettierrc.json deleted file mode 100644 index dfae21d0..00000000 --- a/packages/query-parser/.prettierrc.json +++ /dev/null @@ -1 +0,0 @@ -"@mongodb-js/prettier-config-devtools" diff --git a/packages/query-parser/LICENSE b/packages/query-parser/LICENSE deleted file mode 100644 index 5e0fd33c..00000000 --- a/packages/query-parser/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, -and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by -the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all -other entities that control, are controlled by, or are under common -control with that entity. For the purposes of this definition, -"control" means (i) the power, direct or indirect, to cause the -direction or management of such entity, whether by contract or -otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity -exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, -including but not limited to software source code, documentation -source, and configuration files. - -"Object" form shall mean any form resulting from mechanical -transformation or translation of a Source form, including but -not limited to compiled object code, generated documentation, -and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or -Object form, made available under the License, as indicated by a -copyright notice that is included in or attached to the work -(an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object -form, that is based on (or derived from) the Work and for which the -editorial revisions, annotations, elaborations, or other modifications -represent, as a whole, an original work of authorship. For the purposes -of this License, Derivative Works shall not include works that remain -separable from, or merely link (or bind by name) to the interfaces of, -the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including -the original version of the Work and any modifications or additions -to that Work or Derivative Works thereof, that is intentionally -submitted to Licensor for inclusion in the Work by the copyright owner -or by an individual or Legal Entity authorized to submit on behalf of -the copyright owner. For the purposes of this definition, "submitted" -means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, -and issue tracking systems that are managed by, or on behalf of, the -Licensor for the purpose of discussing and improving the Work, but -excluding communication that is conspicuously marked or otherwise -designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity -on behalf of whom a Contribution has been received by Licensor and -subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of -this License, each Contributor hereby grants to You a perpetual, -worldwide, non-exclusive, no-charge, royalty-free, irrevocable -copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the -Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of -this License, each Contributor hereby grants to You a perpetual, -worldwide, non-exclusive, no-charge, royalty-free, irrevocable -(except as stated in this section) patent license to make, have made, -use, offer to sell, sell, import, and otherwise transfer the Work, -where such license applies only to those patent claims licensable -by such Contributor that are necessarily infringed by their -Contribution(s) alone or by combination of their Contribution(s) -with the Work to which such Contribution(s) was submitted. If You -institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work -or a Contribution incorporated within the Work constitutes direct -or contributory patent infringement, then any patent licenses -granted to You under this License for that Work shall terminate -as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the -Work or Derivative Works thereof in any medium, with or without -modifications, and in Source or Object form, provided that You -meet the following conditions: - -(a) You must give any other recipients of the Work or -Derivative Works a copy of this License; and - -(b) You must cause any modified files to carry prominent notices -stating that You changed the files; and - -(c) You must retain, in the Source form of any Derivative Works -that You distribute, all copyright, patent, trademark, and -attribution notices from the Source form of the Work, -excluding those notices that do not pertain to any part of -the Derivative Works; and - -(d) If the Work includes a "NOTICE" text file as part of its -distribution, then any Derivative Works that You distribute must -include a readable copy of the attribution notices contained -within such NOTICE file, excluding those notices that do not -pertain to any part of the Derivative Works, in at least one -of the following places: within a NOTICE text file distributed -as part of the Derivative Works; within the Source form or -documentation, if provided along with the Derivative Works; or, -within a display generated by the Derivative Works, if and -wherever such third-party notices normally appear. The contents -of the NOTICE file are for informational purposes only and -do not modify the License. You may add Your own attribution -notices within Derivative Works that You distribute, alongside -or as an addendum to the NOTICE text from the Work, provided -that such additional attribution notices cannot be construed -as modifying the License. - -You may add Your own copyright statement to Your modifications and -may provide additional or different license terms and conditions -for use, reproduction, or distribution of Your modifications, or -for any such Derivative Works as a whole, provided Your use, -reproduction, and distribution of the Work otherwise complies with -the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, -any Contribution intentionally submitted for inclusion in the Work -by You to the Licensor shall be under the terms and conditions of -this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify -the terms of any separate license agreement you may have executed -with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade -names, trademarks, service marks, or product names of the Licensor, -except as required for reasonable and customary use in describing the -origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or -agreed to in writing, Licensor provides the Work (and each -Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -implied, including, without limitation, any warranties or conditions -of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -PARTICULAR PURPOSE. You are solely responsible for determining the -appropriateness of using or redistributing the Work and assume any -risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, -whether in tort (including negligence), contract, or otherwise, -unless required by applicable law (such as deliberate and grossly -negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, -incidental, or consequential damages of any character arising as a -result of this License or out of the use or inability to use the -Work (including but not limited to damages for loss of goodwill, -work stoppage, computer failure or malfunction, or any and all -other commercial damages or losses), even if such Contributor -has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing -the Work or Derivative Works thereof, You may choose to offer, -and charge a fee for, acceptance of support, warranty, indemnity, -or other liability obligations and/or rights consistent with this -License. However, in accepting such obligations, You may act only -on Your own behalf and on Your sole responsibility, not on behalf -of any other Contributor, and only if You agree to indemnify, -defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason -of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following -boilerplate notice, with the fields enclosed by brackets "{}" -replaced with your own identifying information. (Don't include -the brackets!) The text should be enclosed in the appropriate -comment syntax for the file format. We also recommend that a -file or class name and description of purpose be included on the -same "printed page" as the copyright notice for easier -identification within third-party archives. - -Copyright {yyyy} {name of copyright owner} - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/packages/query-parser/MIGRATION.md b/packages/query-parser/MIGRATION.md deleted file mode 100644 index a4544ebe..00000000 --- a/packages/query-parser/MIGRATION.md +++ /dev/null @@ -1,95 +0,0 @@ -# Migration Guide - -MongoDB Query Parser will attempt to minimise API changes between major versions. However, as part of our process to constantly improve security and keep packages up to date, backwards breaking changes can and will occur. - -This is a guide to help you make the switch when this happens. - -## Table Of Contents - -- [Migrating from 4.x to 5.0](#migrating-from-4x-to-50) -- [Migrating from 1.0 to 2.0](#migrating-from-10-to-20) - -## Migrating from 4.x to 5.0 - -The deprecated `stringify` export has been removed. It collapsed newlines and -runs of spaces into a single space, which silently corrupted string values in a -query, so there is no drop-in replacement that keeps that behaviour. - -Use `toJSString` instead, which is lossless: - -```js --stringify(query); -+toJSString(query, 0); // compact, no indentation -``` - -Note that `toJSString` does not insert a space after `:` or `,` when called with -an indentation of `0`, so the output is not byte-for-byte identical to what -`stringify` produced. - -## Migrating from 1.0 to 2.0 - -This major version includes two big changes: - -- [Updating the version of BSON from v1 to v4](#JS-BSON-breaking-changes) -- [Changing what types of MQL expressions are allowed in the query parser](#restricting-MQL-expressions) - -### JS-BSON Breaking Changes - -Please refer to [the list of BSON changes](https://github.com/mongodb/js-bson/blob/master/HISTORY.md), as well as the [upgrade documentation for v4](https://github.com/mongodb/js-bson/blob/master/docs/upgrade-to-v4.md) for more information, but breaking changes that have occurred as part of this upgrade are: - -- **`ObjectId`** will now throw TypeErrors instead of silently erroring with invalid input -- **`Symbol`** has now been renamed to **`BSONSymbol`** as it was conflicting with the ES6 **`Symbol`** type -- BSON-JS now relies on Node.js v6 or above, and uses ES2015 features in non-transpiled code -- All BSON types are now ES6 Classes -- Long is now backed by `Long.js` - -### Restricting MQL expressions - -MongoDB Query Parser was making use of [`safer-eval`](https://www.npmjs.com/package/safer-eval) to parse difficult queries, which has been removed as this dependency is insecure. - -We've replaced this dependency with [`@mongodb-js/shell-bson-parser`](https://github.com/mongodb-js/devtools-shared/tree/main/packages/shell-bson-parser), which instead walks the AST of the query, and only attempts to evaluate the query if it considered safe and well-formed. - -Because of this change, some queries that would validate are no longer valid. - -Expressions that will now **fail** include: - -- Function declarations (both `function` and lambdas) -- Ternary syntax (`y > 5 ? 'yes' : 'no'`) -- Comma syntax (`const a = 5, 3`) -- Variable assignment (`{ a: (c = 5, c)}`) - -As a result, queries like this will no longer be accepted as valid input: - -```javascript -{ - ternary: true ? 2 : 3, - commaOperator: ("doesn't work", "as users expect"), - functionDeclaration: (function(d) { return d.setMonth(0) })(new Date()), - variableAssignment: (d = new Date(), d.setDate(1), d), -} -``` - -However, we now officially support these use-cases: - -- Simple expressions (`+`, `-`, `/`, `!` etc) -- Method from Math (excluding `random`) -- Methods on Date (including `.now()`) -- Comments (both `//` and `/* */`) - -This means you can build an MQL expression like so: - -```javascript -{ - // Calculates the day for the current year (Jan 1 = 1, Feb 1 = 32) - dayOfYear: Math.round((new Date().setHours(23) - new Date(new Date().getFullYear(), 0, 1, 0, 0, 0))/1000/60/60/24), - now: Date.now(), - symbol: BSONSymbol('Example Symbol') -} -``` - -As part of this change, error messages that are produced will be slightly different. While it'll still throw a `SyntaxError`, we now return the coordinates of the incorrect token, instead of just what token was incorrect. - -```javascript -eval('({a: )'); // SyntaxError: Unexpected token ')' -parseProject('({a: )'); // SyntaxError: Unexpected token (1:5) -``` diff --git a/packages/query-parser/README.md b/packages/query-parser/README.md deleted file mode 100644 index b97e35d7..00000000 --- a/packages/query-parser/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# mongodb-query-parser - -> **Deprecated:** this package is deprecated. Use [`@mongodb-js/shell-bson-parser`](../shell-bson-parser) instead. - -> Safe parsing and validation for MongoDB queries (filters), projections, and more. - -## Example - -Turn some JS code as a string into a real JS object safely and with no bson type loss: - -```javascript -require('mongodb-query-parser')('{_id: ObjectId("58c33a794d08b991e3648fd2")}'); -// >>> {_id: ObjectId('58c33a794d08b991e3648fd2'x)} -``` - -### Usage with codemirror - -```javascript -var parser = require('mongodb-query-parser'); -var query = '{_id: ObjectId("58c33a794d08b991e3648fd2")}'; -// What is this highlighting/language mode for this string? -parser.detect(query); -// >>> `javascript` - -var queryAsJSON = '{"_id":{"$oid":"58c33a794d08b991e3648fd2"}}'; -// What is this highlighting/language mode for this string? -parser.detect(queryAsJSON); -// >>> `json` - -// Turn it into a JS string that looks pretty in codemirror: -parser.toJavascriptString(parse(query)); -// >>> '{_id:ObjectId(\'58c33a794d08b991e3648fd2\')}' -``` - -### Extended JSON Support - -```javascript -var bson = require('bson'); -var parser = require('mongodb-query-parser'); -var queryAsAnObjectWithTypes = parser.parseFilter(query); - -// Use extended json to prove types are intact -bson.EJSON.stringify(queryAsAnObjectWithTypes); -// >>> '{"_id":{"$oid":"58c33a794d08b991e3648fd2"}}' - -var queryAsJSON = '{"_id":{"$oid":"58c33a794d08b991e3648fd2"}}'; -parser.detect(queryAsJSON); -// >>> `json` -``` - -## Migrations - -We aim to not have any API breaking changes in this library as we consider it stable, but breakages may occur whenever we upgrade a core dependency or perform a major refactor. - -We have a [migration guide](MIGRATION.md) which covers what to look out for between releases. - -## Related - -- [`mongodb-stage-validator`](https://github.com/mongodb-js/stage-validator) Parse and validate MongoDB Aggregation Pipeline stages. -- [`@mongodb-js/compass-query-bar`](https://github.com/mongodb-js/compass-query-bar) Compass UI Plugin that uses `mongodb-query-parser` for validation. - -## License - -Apache 2.0 diff --git a/packages/query-parser/package.json b/packages/query-parser/package.json deleted file mode 100644 index c628bae5..00000000 --- a/packages/query-parser/package.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "name": "mongodb-query-parser", - "description": "Parse MongoDB queries", - "deprecated": "This package is deprecated. Use @mongodb-js/shell-bson-parser instead.", - "author": { - "name": "MongoDB Inc", - "email": "compass@mongodb.com" - }, - "publishConfig": { - "access": "public" - }, - "bugs": { - "url": "https://jira.mongodb.org/projects/COMPASS/issues", - "email": "compass@mongodb.com" - }, - "homepage": "https://github.com/mongodb-js/devtools-shared", - "version": "5.1.3", - "repository": { - "type": "git", - "url": "git+https://github.com/mongodb-js/devtools-shared.git" - }, - "files": [ - "dist" - ], - "license": "Apache-2.0", - "main": "dist/index.js", - "types": "./dist/index.d.ts", - "exports": { - "require": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "import": { - "types": "./dist/index.d.ts", - "default": "./dist/.esm-wrapper.mjs" - } - }, - "scripts": { - "bootstrap": "npm run compile", - "prepublishOnly": "npm run compile", - "compile": "tsc -p tsconfig.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", - "typecheck": "tsc --noEmit", - "eslint": "eslint", - "prettier": "prettier", - "lint": "npm run eslint . && npm run prettier -- --check .", - "depcheck": "depcheck", - "check": "npm run typecheck && npm run lint && npm run depcheck", - "check-ci": "npm run check", - "test": "mocha", - "test-cov": "nyc -x \"**/*.spec.*\" --reporter=lcov --reporter=text --reporter=html npm run test", - "test-watch": "npm run test -- --watch", - "test-ci": "npm run test-cov", - "reformat": "npm run prettier -- --write ." - }, - "dependencies": { - "@mongodb-js/shell-bson-parser": "1.6.3", - "debug": "^4.4.0", - "javascript-stringify": "^2.1.0", - "lodash": "^4.17.21" - }, - "devDependencies": { - "@mongodb-js/eslint-config-devtools": "^0.11.13", - "@mongodb-js/mocha-config-devtools": "^2.0.2", - "@mongodb-js/prettier-config-devtools": "^1.0.6", - "@mongodb-js/tsconfig-devtools": "^1.1.5", - "@types/chai": "^4.2.21", - "@types/mocha": "^9.1.1", - "@types/node": "^25.5.2", - "bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.0.0", - "depcheck": "^1.4.7", - "eslint": "^7.25.0 || ^8.0.0", - "gen-esm-wrapper": "^1.1.3", - "mocha": "^8.4.0", - "nyc": "^15.1.0", - "prettier": "^3.8.1", - "sinon": "^9.2.3", - "typescript": "^5.9.3" - }, - "peerDependencies": { - "bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.0.0" - } -} diff --git a/packages/query-parser/src/constants.ts b/packages/query-parser/src/constants.ts deleted file mode 100644 index 20cd63e5..00000000 --- a/packages/query-parser/src/constants.ts +++ /dev/null @@ -1,169 +0,0 @@ -const COLLATION_OPTIONS: { - [collationOption: string]: (string | number | boolean)[]; -} = { - locale: [ - 'af', - 'sq', - 'am', - 'ar', - 'ar@collation=compat', - 'hy', - 'as', - 'az', - 'az@collation=search', - 'be', - 'bn', - 'bn@collation=traditional', - 'bs', - 'bs@collation=search', - 'bs_Cyrl', - 'bg', - 'my', - 'ca', - 'ca@collation=search', - 'chr', - 'zh', - 'zh@collation=big5han', - 'zh@collation=gb2312han', - 'zh@collation=pinyin', - 'zh@collation=unihan', - 'zh@collation=zhuyin', - 'zh_Hant', - 'zh_Hant@collation=stroke', - 'hr', - 'hr@collation=search', - 'cs', - 'cs@collation=search', - 'da', - 'da@collation=search', - 'nl', - 'dz', - 'en', - 'en_US', - 'en_US_POSIX', - 'eo', - 'et', - 'ee', - 'fo', - 'fil', - 'fi_FI', - 'fi_FI@collation=search', - 'fi_FI@collation=traditional', - 'fr', - 'fr_CA', - 'gl', - 'gl@collation=search', - 'ka', - 'de', - 'de@collation=search', - 'de@collation=eor', - 'de@collation=phonebook', - 'de_AT', - 'de_AT@collation=phonebook', - 'el', - 'gu', - 'ha', - 'haw', - 'he', - 'he@collation=search', - 'hi', - 'hu', - 'is', - 'is@collation=search', - 'ig', - 'smn', - 'smn@collation=search', - 'id', - 'ga', - 'it', - 'ja', - 'ja@collation=unihan', - 'kl', - 'kl@collation=search', - 'kn', - 'kn@collation=traditional', - 'kk', - 'km', - 'kok', - 'ko', - 'ko@collation=search', - 'ko@collation=searchjl', - 'ko@collation=unihan', - 'ky', - 'lk', - 'lo', - 'lv', - 'li', - 'li@collation=phonetic', - 'lt', - 'dsb', - 'lb', - 'mk', - 'ms', - 'ml', - 'mt', - 'mr', - 'mn', - 'ne', - 'se', - 'se@collation=search', - 'nb', - 'nb@collation=search', - 'nn', - 'nn@collation=search', - 'or', - 'om', - 'ps', - 'fa', - 'fa_AF', - 'pl', - 'pt', - 'pa', - 'ro', - 'ru', - 'sr', - 'sr_Latn', - 'sr_Latn@collation=search', - 'si', - 'si@collation=dictionary', - 'sk', - 'sk@collation=search', - 'sl', - 'es', - 'es@collation=search', - 'es@collation=traditional', - 'sw', - 'sv', - 'sv@collation=search', - 'ta', - 'te', - 'th', - 'bo', - 'to', - 'tr', - 'tr@collation=search', - 'uk', - 'hsb', - 'ur', - 'ug', - 'vi', - 'vi@collation=traditional', - 'wae', - 'cy', - 'yi', - 'yi@collation=search', - 'yo', - 'zu', - 'simple', - ], - strength: [1, 2, 3, 4, 5], - caseLevel: [true, false], - caseFirst: ['upper', 'lower', 'off'], - numericOrdering: [true, false], - alternate: ['non-ignorable', 'shifted'], - maxVariable: [true, false], - backwards: [true, false], - normalization: [true, false], -}; - -export { COLLATION_OPTIONS }; diff --git a/packages/query-parser/src/index.spec.ts b/packages/query-parser/src/index.spec.ts deleted file mode 100644 index dea71121..00000000 --- a/packages/query-parser/src/index.spec.ts +++ /dev/null @@ -1,1025 +0,0 @@ -import assert from 'assert'; -import sinon from 'sinon'; -import bson from 'bson'; -import _debug from 'debug'; - -import { - isCollationValid, - isFilterValid, - isHintValid, - isLimitValid, - isMaxTimeMSValid, - isProjectValid, - isSkipValid, - isSortValid, - parseCollation, - parseFilter, - parseProject, - parseSort, - toJSString, - DEFAULT_LIMIT, - DEFAULT_MAX_TIME_MS, - DEFAULT_SKIP, - validate, -} from './index'; - -const debug = _debug('mongodb-query-parser:test'); - -function convert(string: string) { - const res = parseFilter(string); - const ret = bson.EJSON.serialize(res, { legacy: true, relaxed: false }); - debug('converted', { input: string, parsed: res, encoded: ret }); - return ret; -} - -describe('mongodb-query-parser', function () { - describe('filter', function () { - context('when no new keyword is provided', function () { - it('returns the filter', function () { - const res = parseFilter('{_id: ObjectId("58c33a794d08b991e3648fd2")}'); - assert.deepEqual(res, { - _id: new bson.ObjectId('58c33a794d08b991e3648fd2'), - }); - }); - }); - - context('when a new keyword is provided', function () { - it('returns the filter', function () { - const res = parseFilter( - '{_id: new ObjectId("58c33a794d08b991e3648fd2")}', - ); - assert.deepEqual(res, { - _id: new bson.ObjectId('58c33a794d08b991e3648fd2'), - }); - }); - }); - - describe('shell helpers', function () { - it('should support Code', function () { - assert.deepEqual(convert('Code("return true", {})'), { - $code: 'return true', - $scope: {}, - }); - }); - - it('should support new Date', function () { - assert.deepEqual(convert('new Date("2017-01-01T12:35:31.123Z")'), { - $date: '2017-01-01T12:35:31.123Z', - }); - }); - - it('should support new Date (0 ms)', function () { - assert.deepEqual(convert('new Date("2017-01-01T12:35:31.000Z")'), { - $date: '2017-01-01T12:35:31Z', - }); - }); - - it('should support ISODate', function () { - assert.deepEqual(convert('ISODate("2017-01-01T12:35:31.123Z")'), { - $date: '2017-01-01T12:35:31.123Z', - }); - }); - - it('should support ISODate (0 ms)', function () { - assert.deepEqual(convert('ISODate("2017-01-01T12:35:31.000Z")'), { - $date: '2017-01-01T12:35:31Z', - }); - }); - - it('should support new ISODate', function () { - assert.deepEqual(convert('new ISODate("2017-01-01T12:35:31.123Z")'), { - $date: '2017-01-01T12:35:31.123Z', - }); - }); - - it('should support new ISODate (0 ms)', function () { - assert.deepEqual(convert('new ISODate("2017-01-01T12:35:31.000Z")'), { - $date: '2017-01-01T12:35:31Z', - }); - }); - - it('should support BinData', function () { - assert.deepEqual( - convert( - `new BinData(${bson.Binary.SUBTYPE_BYTE_ARRAY}, "OyQRAeK7QlWMr0E2xWapYg==")`, - ), - { - $binary: 'OyQRAeK7QlWMr0E2xWapYg==', - $type: `0${bson.Binary.SUBTYPE_BYTE_ARRAY}`, - }, - ); - }); - - it('should support UUID', function () { - assert.deepEqual( - convert('UUID("3b241101-e2bb-4255-8caf-4136c566a962")'), - { - $binary: 'OyQRAeK7QlWMr0E2xWapYg==', - $type: `0${bson.Binary.SUBTYPE_UUID}`, - }, - ); - }); - - it('should support LegacyJavaUUID', function () { - assert.deepEqual( - convert('LegacyJavaUUID("00112233-4455-6677-8899-aabbccddeeff")'), - { - $binary: 'd2ZVRDMiEQD/7t3Mu6qZiA==', - $type: `0${bson.Binary.SUBTYPE_UUID_OLD}`, - }, - ); - }); - - it('should support LegacyCSharpUUID', function () { - assert.deepEqual( - convert('LegacyCSharpUUID("00112233-4455-6677-8899-aabbccddeeff")'), - { - $binary: 'MyIRAFVEd2aImaq7zN3u/w==', - $type: `0${bson.Binary.SUBTYPE_UUID_OLD}`, - }, - ); - }); - - it('should support LegacyPythonUUID', function () { - assert.deepEqual( - convert('LegacyPythonUUID("00112233-4455-6677-8899-aabbccddeeff")'), - { - $binary: 'ABEiM0RVZneImaq7zN3u/w==', - $type: `0${bson.Binary.SUBTYPE_UUID_OLD}`, - }, - ); - }); - - // https://www.mongodb.com/docs/manual/reference/method/Binary.createFromHexString/ - it('should support Binary.createFromHexString', function () { - assert.deepEqual( - convert( - `Binary.createFromHexString("deadbeef", ${bson.Binary.SUBTYPE_BYTE_ARRAY})`, - ), - { - $binary: '3q2+7w==', - $type: `0${bson.Binary.SUBTYPE_BYTE_ARRAY}`, - }, - ); - }); - - // https://www.mongodb.com/docs/manual/reference/method/Binary.createFromBase64/ - it('should support Binary.createFromBase64', function () { - assert.deepEqual( - convert( - `Binary.createFromBase64("3q2+7w==", ${bson.Binary.SUBTYPE_BYTE_ARRAY})`, - ), - { - $binary: '3q2+7w==', - $type: `0${bson.Binary.SUBTYPE_BYTE_ARRAY}`, - }, - ); - }); - - it('should support functions', function () { - assert.deepEqual(convert('{$match: () => true}'), { - $match: '() => true', - }); - - assert.deepEqual( - convert( - `{ - $expr: { - $function: { - body: function(name) { return hex_md5(name) == "15b0a220baa16331e8d80e15367677ad"; }, - args: [ "$name" ], - lang: "js" - } - } - }`, - ), - { - $expr: { - $function: { - body: 'function(name) { return hex_md5(name) == "15b0a220baa16331e8d80e15367677ad"; }', - args: ['$name'], - lang: 'js', - }, - }, - }, - ); - - assert.deepEqual( - convert('{$match: function() { return this.x === 2; }}'), - { - $match: 'function() { return this.x === 2; }', - }, - ); - }); - - context('for Date() and ISODate() without argument', function () { - // mock a specific timestamp with sinon.useFakeTimers - const now = 1533789516225; - const nowStr = '2018-08-09T04:38:36.225Z'; - let clock: sinon.SinonFakeTimers; - - beforeEach(function () { - clock = sinon.useFakeTimers(now); - }); - - afterEach(function () { - clock.restore(); - }); - - it('should support new Date', function () { - assert.deepEqual(convert('new Date()'), { - $date: nowStr, - }); - }); - - it('should support ISODate', function () { - assert.deepEqual(convert('ISODate()'), { - $date: nowStr, - }); - }); - - it('should support new ISODate', function () { - assert.deepEqual(convert('new ISODate()'), { - $date: nowStr, - }); - }); - }); - - it('should support Timestamp', function () { - assert.deepEqual(convert('{t: Timestamp(0, 0)}'), { - t: { $timestamp: { i: 0, t: 0 } }, - }); - }); - - it('should support new Timestamp', function () { - assert.deepEqual(convert('{t: new Timestamp(0, 0)}'), { - t: { $timestamp: { i: 0, t: 0 } }, - }); - }); - - it('should support inline regex', function () { - assert.deepEqual(convert('/some.*regex+/i'), { - $regex: 'some.*regex+', - $options: 'i', - }); - }); - - it('should support RegExp', function () { - assert.deepEqual(convert("RegExp('some.*regex+', 'i')"), { - $regex: 'some.*regex+', - $options: 'i', - }); - }); - - it('should support new RegExp', function () { - assert.deepEqual(convert("new RegExp('some.*regex+', 'i')"), { - $regex: 'some.*regex+', - $options: 'i', - }); - }); - - it('should support ObjectId', function () { - assert.deepEqual(convert('ObjectId("58c33a794d08b991e3648fd2")'), { - $oid: '58c33a794d08b991e3648fd2', - }); - }); - - it('should support new ObjectId', function () { - assert.deepEqual(convert('new ObjectId("58c33a794d08b991e3648fd2")'), { - $oid: '58c33a794d08b991e3648fd2', - }); - }); - - it('should support ObjectID', function () { - assert.deepEqual(convert('ObjectID("58c33a794d08b991e3648fd2")'), { - $oid: '58c33a794d08b991e3648fd2', - }); - }); - - it('should support new ObjectID', function () { - assert.deepEqual(convert('new ObjectID("58c33a794d08b991e3648fd2")'), { - $oid: '58c33a794d08b991e3648fd2', - }); - }); - - it('should support NumberLong', function () { - assert.deepEqual(convert('NumberLong("1234567890")'), { - $numberLong: '1234567890', - }); - }); - - it('should support NumberLong > MAX_SAFE_INTEGER', function () { - assert.deepEqual(convert('NumberLong("345678654321234552")'), { - $numberLong: '345678654321234552', - }); - }); - - it('should support new NumberLong', function () { - assert.deepEqual(convert('new NumberLong("1234567890")'), { - $numberLong: '1234567890', - }); - }); - - it('should support NumberInt', function () { - assert.deepEqual(convert('NumberInt("1234567890")'), 1234567890); - }); - - it('should support NumberInt with number', function () { - assert.deepEqual(convert('NumberInt(1234567890)'), 1234567890); - }); - - it('should support NumberDecimal', function () { - assert.deepEqual(convert('NumberDecimal("10.99")'), { - $numberDecimal: '10.99', - }); - }); - - it('should support new NumberDecimal', function () { - assert.deepEqual(convert('new NumberDecimal("10.99")'), { - $numberDecimal: '10.99', - }); - }); - - it('should support MixKey', function () { - assert.deepEqual(convert('MinKey()'), { $minKey: 1 }); - }); - - it('should support MaxKey', function () { - assert.deepEqual(convert('MaxKey()'), { $maxKey: 1 }); - }); - }); - }); - - describe('isFilterValid', function () { - context('when the string contains a NumberLong', function () { - const query = '{value: NumberLong(1)}'; - const parsed = isFilterValid(query); - it('returns the bson long value', function () { - assert.equal(parsed.value.toNumber(), 1); - }); - }); - - context('when turning off validation', function () { - context('when the query is a valid object', function () { - const query = '{value: NumberLong(1)}'; - const parsed = isFilterValid(query); - - it('returns truthy', function () { - assert.equal(parsed.value.toNumber(), 1); - }); - }); - - context('when the query is not a valid object', function () { - const query = '{value: NumberLong(1)'; - const parsed = isFilterValid(query); - - it('returns false', function () { - assert.equal(parsed, false); - }); - }); - }); - }); - - describe('toJSString', function () { - it('should default to two spaces', function () { - assert.equal( - toJSString({ a: { $exists: true } }), - `{ - a: { - $exists: true - } -}`, - ); - }); - - it('should allow falsy indentation', function () { - assert.equal( - toJSString({ a: { $exists: true } }, 0), - '{a:{$exists:true}}', - ); - }); - - it('allows passing custom indent', function () { - assert.equal( - toJSString({ a: { $exists: true } }, 'pineapple'), - `{ -pineapplea: { -pineapplepineapple$exists: true -pineapple} -}`, - ); - }); - - it('retains double spaces and new lines in strings', function () { - assert.equal( - toJSString( - { - a: { - name: `multi-line with s p a c - -e s`, - }, - }, - 0, - ), - "{a:{name:'multi-line with s p a c\\n \\ne s'}}", - ); - }); - - it('escapes quotes, backslashes and newlines in Code', function () { - const code = `a "b" 'c' \\d\ne`; - const jsString = toJSString({ a: new bson.Code(code) }) as string; - assert.deepEqual(parseFilter(jsString), { - a: new bson.Code(code), - }); - }); - - it('escapes Code with a scope', function () { - const code = `');process.exit(1);('`; - const jsString = toJSString({ - a: new bson.Code(code, { b: 1 }), - }) as string; - assert.deepEqual(parseFilter(jsString), { - a: new bson.Code(code, { b: 1 }), - }); - }); - - describe('DBRef', function () { - // The bson types say `oid` is an ObjectId, but a DBRef oid can hold any - // BSON value in practice, which is what we want to cover here. - const dbRef = ( - collection: string, - oid: unknown, - db?: string, - fields?: Record, - ) => new bson.DBRef(collection, oid as bson.ObjectId, db, fields); - - it('preserves the oid type rather than flattening it to a string', function () { - assert.equal( - toJSString({ a: dbRef('col', 1) }, 0), - '{a:DBRef("col", 1)}', - ); - assert.equal( - toJSString({ a: dbRef('col', 'abc') }, 0), - '{a:DBRef("col", \'abc\')}', - ); - assert.equal( - toJSString( - { a: dbRef('col', new bson.ObjectId('507f191e810c19729de860ea')) }, - 0, - ), - '{a:DBRef("col", ObjectId(\'507f191e810c19729de860ea\'))}', - ); - }); - - it('includes the db when present', function () { - assert.equal( - toJSString({ a: dbRef('col', 1, 'db') }, 0), - '{a:DBRef("col", 1, "db")}', - ); - }); - - it('includes the fields when present', function () { - assert.equal( - toJSString({ a: dbRef('col', 1, 'db', { b: 1 }) }, 0), - '{a:DBRef("col", 1, "db", {b:1})}', - ); - }); - - it('passes an undefined db when fields are present without one', function () { - assert.equal( - toJSString({ a: dbRef('col', 1, undefined, { b: 1 }) }, 0), - '{a:DBRef("col", 1, undefined, {b:1})}', - ); - }); - - it('omits empty fields', function () { - assert.equal( - toJSString({ a: dbRef('col', 1, undefined, {}) }, 0), - '{a:DBRef("col", 1)}', - ); - }); - - it('preserves BSON types inside fields', function () { - assert.equal( - toJSString( - { - a: dbRef('col', 1, 'db', { - b: new bson.ObjectId('507f191e810c19729de860ea'), - }), - }, - 0, - ), - '{a:DBRef("col", 1, "db", {b:ObjectId(\'507f191e810c19729de860ea\')})}', - ); - }); - - it('escapes quotes in the collection and db', function () { - assert.equal( - toJSString({ a: dbRef("co'l", 1, 'd"b') }, 0), - '{a:DBRef("co\'l", 1, "d\\"b")}', - ); - }); - - const roundTrips: [string, bson.DBRef][] = [ - ['numeric oid', dbRef('col', 1)], - ['string oid', dbRef('col', 'abc')], - [ - 'ObjectId oid', - dbRef('col', new bson.ObjectId('507f191e810c19729de860ea')), - ], - ['db', dbRef('col', 1, 'db')], - ['quotes', dbRef("co'l", 1, 'd"b')], - ['double spaces', dbRef('a b', 1)], - ['newline', dbRef('a\nb', 1)], - ['nested DBRef oid', dbRef('col', dbRef('inner', 1), 'db')], - ['fields', dbRef('col', 1, 'db', { b: 1 })], - ['fields but no db', dbRef('col', 1, undefined, { b: 1 })], - [ - 'BSON values in fields', - dbRef('col', 1, 'db', { - b: new bson.ObjectId('507f191e810c19729de860ea'), - }), - ], - ]; - - for (const [name, dbref] of roundTrips) { - it(`round-trips a DBRef with ${name}`, function () { - const jsString = toJSString({ a: dbref }, 0) as string; - assert.deepEqual(parseFilter(jsString), { a: dbref }); - }); - } - }); - }); - - describe('toJSString with indent 0', function () { - const compactStringify = (obj: unknown) => toJSString(obj, 0); - - it('should work', function () { - const res = parseFilter('{_id: ObjectId("58c33a794d08b991e3648fd2")}'); - assert.equal( - compactStringify(res), - "{_id:ObjectId('58c33a794d08b991e3648fd2')}", - ); - }); - it('should not add extra space when nesting', function () { - assert.equal( - compactStringify({ a: { $exists: true } }), - '{a:{$exists:true}}', - ); - }); - - it('preserves multi-space and newline values', function () { - assert.equal( - compactStringify({ - a: { - name: `multi-line with s p a c - -e s`, - }, - }), - "{a:{name:'multi-line with s p a c\\n \\ne s'}}", - ); - }); - - context('when providing a long', function () { - it('correctly converts to NumberLong', function () { - assert.equal( - compactStringify({ test: bson.Long.fromNumber(5) }), - "{test:NumberLong('5')}", - ); - - assert.equal( - compactStringify({ test: new bson.Long('123456789123456789') }), - "{test:NumberLong('123456789123456789')}", - ); - }); - }); - - context('when providing a decimal128', function () { - it('correctly converts to NumberDecimal', function () { - assert.equal( - compactStringify({ test: bson.Decimal128.fromString('5.5') }), - "{test:NumberDecimal('5.5')}", - ); - }); - }); - - context('when providing an int32', function () { - it('correctly converts to Int32', function () { - assert.equal( - compactStringify({ test: new bson.Int32(123) }), - "{test:NumberInt('123')}", - ); - }); - }); - - context('when providing a Double', function () { - it('correctly converts to Double', function () { - assert.equal( - compactStringify({ test: new bson.Double(0.8) }), - "{test:Double('0.8')}", - ); - }); - }); - - context('when providing a geo query', function () { - const query = { - coordinates: { - $geoWithin: { - $centerSphere: [[-79, 28], 0.04], - }, - }, - }; - - it('does not add any whitespace', function () { - assert.equal( - compactStringify(query), - '{coordinates:{$geoWithin:{$centerSphere:[[-79,28],0.04]}}}', - ); - }); - }); - - context('when providing a Date', function () { - it('correctly converts to an ISODate', function () { - const res = parseFilter("{test: new Date('2017-01-01T12:35:31.000Z')}"); - assert.equal( - compactStringify(res), - "{test:ISODate('2017-01-01T12:35:31.000Z')}", - ); - }); - - it('falls back to an invalid ISODate if the provided Date is invalid', function () { - const res = parseFilter("{test: new Date('invalid')}"); - assert.equal(compactStringify(res), "{test:ISODate('Invalid Date')}"); - }); - }); - - context('when providing an ISODate', function () { - it('correctly converts to an ISODate', function () { - const res = parseFilter("{test: ISODate('2017-01-01T12:35:31.000Z')}"); - assert.equal( - compactStringify(res), - "{test:ISODate('2017-01-01T12:35:31.000Z')}", - ); - }); - - it('throws if the provided ISODate is invalid', function () { - assert.throws( - () => parseFilter("{test: ISODate('invalid')}"), - /"invalid" is not a valid ISODate/, - ); - }); - }); - - context('when providing a DBRef with (collection, oid)', function () { - it('correctly converts to a DBRef', function () { - const res = parseFilter("{dbref: DBRef('col', 1)}"); - assert.equal(compactStringify(res), '{dbref:DBRef("col", 1)}'); - }); - }); - - context('when providing a DBRef with (db.collection, oid)', function () { - it('correctly converts to a DBRef', function () { - const res = parseFilter("{dbref: DBRef('db.col', 1)}"); - assert.equal(compactStringify(res), '{dbref:DBRef("col", 1, "db")}'); - }); - }); - - context('when providing a DBRef with (collection, oid, db)', function () { - it('correctly converts to a DBRef', function () { - const res = parseFilter("{dbref: DBRef('col', 1, 'db')}"); - assert.equal(compactStringify(res), '{dbref:DBRef("col", 1, "db")}'); - }); - }); - - context('when provided a RegExp', function () { - it('correctly formats the options', function () { - const res = parseFilter('{name: /foo/i}'); - assert.equal(compactStringify(res), '{name:RegExp("foo", \'i\')}'); - }); - - it('escapes quotes', function () { - const res = parseFilter("{name: /'/}"); - assert.equal(compactStringify(res), '{name:RegExp("\'")}'); - }); - - it('handles $regex object format (keeps format)', function () { - const res = parseFilter( - '{"name": {"$regex": "pineapple", "$options": "i"}}', - ); - assert.equal( - compactStringify(res), - "{name:{$regex:'pineapple',$options:'i'}}", - ); - }); - - it('handles /regex/ format', function () { - const res = { - name: /pineapple/, - }; - assert.equal(compactStringify(res), '{name:RegExp("pineapple")}'); - }); - }); - - context('when provided a BSONRegExp', function () { - it('stringifies correctly with options', function () { - const res = { - name: new bson.BSONRegExp('pineapple', 'i'), - }; - assert.equal( - compactStringify(res), - '{name:RegExp("pineapple", \'i\')}', - ); - }); - - it('stringifies correctly with quotes', function () { - const res = { - name: new bson.BSONRegExp('"\'', 'i'), - }; - assert.equal(compactStringify(res), '{name:RegExp("\\"\'", \'i\')}'); - }); - - it('stringifies correctly without options', function () { - const res = { - name: new bson.BSONRegExp('pineapple'), - }; - assert.equal(compactStringify(res), '{name:RegExp("pineapple")}'); - }); - - it('stringifies into BSONRegExp when js RegExp cannot handle an option', function () { - const res = { - name: new bson.BSONRegExp( - 'pineapple', - 'x' /* x flag is not valid in js but valid in BSONRegExp*/, - ), - }; - assert.equal( - compactStringify(res), - '{name:BSONRegExp("pineapple", \'x\')}', - ); - }); - - it('stringifies into BSONRegExp when js RegExp cannot handle the regex', function () { - const res = { - name: new bson.BSONRegExp( - // Perl Compatible Regular Expressions supported feature that isn't in regular - // js RegExp: case-insensitive match. - '(?i)a(?-i)cme', - 'i', - ), - }; - assert.equal( - compactStringify(res), - '{name:BSONRegExp("(?i)a(?-i)cme", \'i\')}', - ); - }); - }); - - context('when provided a Binary', function () { - it('should support BinData', function () { - const res = parseFilter( - `{name: new BinData(${bson.Binary.SUBTYPE_BYTE_ARRAY}, "OyQRAeK7QlWMr0E2xWapYg==")}`, - ); - assert.equal( - compactStringify(res), - `{name:BinData(${bson.Binary.SUBTYPE_BYTE_ARRAY}, 'OyQRAeK7QlWMr0E2xWapYg==')}`, - ); - }); - - it('should support UUID', function () { - const res = parseFilter( - '{name: UUID("3b241101-e2bb-4255-8caf-4136c566a962")}', - ); - assert.equal( - compactStringify(res), - "{name:UUID('3b241101-e2bb-4255-8caf-4136c566a962')}", - ); - }); - - it('does not convert LegacyJavaUUID to UUID', function () { - const res = parseFilter( - '{name: LegacyJavaUUID("00112233-4455-6677-8899-aabbccddeeff")}', - ); - assert.equal( - compactStringify(res), - "{name:BinData(3, 'd2ZVRDMiEQD/7t3Mu6qZiA==')}", - ); - }); - - it('does not convert LegacyCSharpUUID to UUID', function () { - const res = parseFilter( - '{name: LegacyCSharpUUID("00112233-4455-6677-8899-aabbccddeeff")}', - ); - assert.equal( - compactStringify(res), - "{name:BinData(3, 'MyIRAFVEd2aImaq7zN3u/w==')}", - ); - }); - - it('does not convert LegacyPythonUUID to UUID', function () { - const res = parseFilter( - '{name: LegacyPythonUUID("00112233-4455-6677-8899-aabbccddeeff")}', - ); - assert.equal( - compactStringify(res), - "{name:BinData(3, 'ABEiM0RVZneImaq7zN3u/w==')}", - ); - }); - - // https://www.mongodb.com/docs/manual/reference/method/Binary.createFromHexString/ - it('should support Binary.createFromHexString', function () { - const res = parseFilter( - `{name: Binary.createFromHexString("deadbeef", ${bson.Binary.SUBTYPE_BYTE_ARRAY})}`, - ); - assert.equal( - compactStringify(res), - `{name:BinData(${bson.Binary.SUBTYPE_BYTE_ARRAY}, '3q2+7w==')}`, - ); - }); - - // https://www.mongodb.com/docs/manual/reference/method/Binary.createFromBase64/ - it('should support Binary.createFromBase64', function () { - const res = parseFilter( - `{name: Binary.createFromBase64("3q2+7w==", ${bson.Binary.SUBTYPE_BYTE_ARRAY})}`, - ); - assert.equal( - compactStringify(res), - `{name:BinData(${bson.Binary.SUBTYPE_BYTE_ARRAY}, '3q2+7w==')}`, - ); - }); - }); - }); - - describe('project', function () { - it('should default to null', function () { - assert.equal(parseProject(''), null); - assert.equal(parseProject(' '), null); - assert.equal(parseProject('{}'), null); - }); - it('should parse valid project strings', function () { - assert.deepEqual(parseProject('{_id: 1}'), { _id: 1 }); - assert.deepEqual(parseProject('{_id: -1}'), { _id: -1 }); - assert.deepEqual(parseProject('{comments: { $slice: -1 }}'), { - comments: { $slice: -1 }, - }); - }); - it('should allow any project strings', function () { - assert.deepEqual(isProjectValid('{_id: "a"}'), { _id: 'a' }); - assert.deepEqual(isProjectValid('{_id: "1"}'), { _id: '1' }); - }); - it('should reject broken project strings', function () { - assert.equal(isProjectValid('{grabage'), false); - }); - it('should reject non object project values', function () { - assert.equal(isProjectValid('true'), false); - assert.equal(isProjectValid('123'), false); - assert.equal(isProjectValid('"something"'), false); - assert.equal(isProjectValid('null'), false); - }); - }); - - describe('collation', function () { - it('should default to null', function () { - assert.equal(parseCollation(''), null); - assert.equal(parseCollation(' '), null); - assert.equal(parseCollation('{}'), null); - }); - it('should parse valid collation strings', function () { - assert.deepEqual(parseCollation('{locale: "simple"}'), { - locale: 'simple', - }); - assert.deepEqual(parseCollation('{locale: "en_US", strength: 1}'), { - locale: 'en_US', - strength: 1, - }); - }); - it('should detect invalid collation strings', function () { - assert.equal(isCollationValid('{invalid: "simple"}'), false); - assert.equal(isCollationValid('{locale: ""}'), false); - assert.equal(isCollationValid('{locale: "invalid"}'), false); - }); - }); - - describe('hint', function () { - it('should default to null', function () { - assert.equal(isHintValid(''), null); - assert.equal(isHintValid(' '), null); - assert.equal(isHintValid('{}'), null); - }); - - it('should parse hint objects', function () { - assert.deepEqual(isHintValid('{_id: 1}'), { _id: 1 }); - assert.deepEqual(isHintValid('{_id: -1}'), { _id: -1 }); - assert.deepEqual(isHintValid('{pineapple: 1, age: -1}'), { - pineapple: 1, - age: -1, - }); - }); - - it('should accept string index names', function () { - assert.deepEqual(isHintValid('"pineapple"'), 'pineapple'); - assert.deepEqual(isHintValid("'pineapple'"), 'pineapple'); - }); - - it('should not accept arrays', function () { - assert.deepEqual(isHintValid('["pineappleOne", "pineappleTwo"]'), false); - }); - - it('should accept docs with numeric values', function () { - assert.deepEqual(isHintValid('{pineapple: 0}'), { pineapple: 0 }); - assert.deepEqual(isHintValid('{pineapple: -1}'), { pineapple: -1 }); - assert.deepEqual(isHintValid('{pineapple: NaN}'), { pineapple: NaN }); - assert.deepEqual(isHintValid('{pineapple: 2}'), { pineapple: 2 }); - }); - - it('should reject broken objects', function () { - assert.equal(isHintValid('{not_pineapple'), false); - assert.equal(isHintValid('invalid pineapple: }'), false); - assert.equal(isHintValid('{invalid pineapple}'), false); - assert.equal(isHintValid('{invalid pineapple: }'), false); - }); - - it('should reject non-string/non-object hint values', function () { - assert.equal(isHintValid('true'), false); - assert.equal(isHintValid('pineapple'), false); - assert.equal(isHintValid('123'), false); - assert.equal(isHintValid('null'), false); - }); - }); - - describe('sort', function () { - it('should default to null', function () { - assert.equal(parseSort(''), null); - assert.equal(parseSort(' '), null); - assert.equal(parseSort('{}'), null); - }); - it('should work', function () { - assert.deepEqual(parseSort('{_id: 1}'), { _id: 1 }); - assert.deepEqual(parseSort('{_id: -1}'), { _id: -1 }); - }); - it('should allow objects and arrays as values', function () { - assert.deepEqual(isSortValid(''), null); - assert.deepEqual(isSortValid('{_id: 1}'), { _id: 1 }); - assert.deepEqual(isSortValid('{_id: -1}'), { _id: -1 }); - assert.deepEqual(isSortValid('{_id: "asc"}'), { _id: 'asc' }); - assert.deepEqual(isSortValid('{_id: "desc"}'), { _id: 'desc' }); - assert.deepEqual(isSortValid('{ score: { $meta: "textScore" } }'), { - score: { $meta: 'textScore' }, - }); - assert.deepEqual(isSortValid('[["123", -1]]'), [['123', -1]]); - assert.deepEqual(isSortValid('[["bar", 1]]'), [['bar', 1]]); - }); - it('should reject unsupported sort values', function () { - assert.equal(isSortValid('{_id: "a"}'), false); - assert.equal(isSortValid('{_id: "1"}'), false); - assert.equal(isSortValid('{grabage'), false); - assert.equal(isSortValid('[1]'), false); - assert.equal(isSortValid('["foo"]'), false); - assert.equal(isSortValid('[["foo", "bar"]]'), false); - assert.equal(isSortValid('[[123, -1]]'), false); - }); - it('should handle empty, null, and undefined', function () { - assert.equal(isSortValid(''), null); - assert.equal(isSortValid('null'), null); - assert.equal(isSortValid('undefined'), null); - }); - }); - - describe('skip', function () { - it('should work', function () { - assert.equal(isSkipValid('{skip: "a"}'), false); - assert.equal(isSkipValid('0'), 0); - assert.equal(isSkipValid(1), 1); - assert.equal(isSkipValid(' '), DEFAULT_SKIP); - }); - }); - - describe('limit', function () { - it('should work', function () { - assert.equal(isLimitValid('{limit: "a"}'), false); - assert.equal(isLimitValid('0'), 0); - assert.equal(isLimitValid(1), 1); - assert.equal(isLimitValid(' '), DEFAULT_LIMIT); - }); - }); - - describe('maxTimeMS', function () { - it('Validates as a number', function () { - assert.equal(isMaxTimeMSValid('{maxTimeMS: "a"}'), false); - assert.equal(isMaxTimeMSValid('0'), 0); - assert.equal(isMaxTimeMSValid(1), 1); - assert.equal(isMaxTimeMSValid(' '), DEFAULT_MAX_TIME_MS); - }); - }); - - describe('validate', function () { - it('calls the other validation functions', function () { - assert.deepEqual(isSortValid(''), null); - assert.deepEqual(validate('sort', ''), null); - assert.deepEqual(validate('Sort', ''), null); - assert.deepEqual(validate('sort', '[["123", -1]]'), [['123', -1]]); - assert.deepEqual(validate('limit', ' '), DEFAULT_LIMIT); - }); - }); -}); diff --git a/packages/query-parser/src/index.ts b/packages/query-parser/src/index.ts deleted file mode 100644 index b3235d3f..00000000 --- a/packages/query-parser/src/index.ts +++ /dev/null @@ -1,395 +0,0 @@ -import parseShellStringToEJSON, { - ParseMode, -} from '@mongodb-js/shell-bson-parser'; - -import _ from 'lodash'; -import _debug from 'debug'; - -import { COLLATION_OPTIONS } from './constants'; -import { toJSString } from './stringify'; - -const debug = _debug('mongodb-query-parser'); - -/** @public */ -const DEFAULT_FILTER = {}; -/** @public */ -const DEFAULT_SORT = null; -/** @public */ -const DEFAULT_LIMIT = 0; -/** @public */ -const DEFAULT_SKIP = 0; -/** @public */ -const DEFAULT_PROJECT = null; -/** @public */ -const DEFAULT_COLLATION = null; -/** @public */ -const DEFAULT_MAX_TIME_MS = 60000; // 1 minute in ms -/** @public */ -const DEFAULT_HINT = null; - -function isEmpty(input: string | number | null | undefined): boolean { - if (input === null || input === undefined) { - return true; - } - const s = _.trim(typeof input === 'number' ? `${input}` : input); - - if (s === '{}') { - return true; - } - return _.isEmpty(s); -} - -function isNumberValid(input: string | number) { - if (isEmpty(input)) { - return 0; - } - return /^\d+$/.test(`${input}`) ? parseInt(`${input}`, 10) : false; -} - -function _parseProject(input: string) { - return parseShellStringToEJSON(input, { mode: ParseMode.Loose }); -} - -function _parseCollation(input: string) { - return parseShellStringToEJSON(input, { mode: ParseMode.Loose }); -} - -/** @public */ -export function parseSort(input: string) { - if (isEmpty(input)) { - return DEFAULT_SORT; - } - return parseShellStringToEJSON(input, { mode: ParseMode.Loose }); -} - -function isValueOkForHint() { - /** - * Prior to MongoDB 7.0, hint would accept invalid values, like NaN. - * So we're on the looser side of validation here. - */ - return true; -} - -function _parseHint(input: string) { - return parseShellStringToEJSON(input, { mode: ParseMode.Loose }); -} - -function _parseFilter(input: string) { - return parseShellStringToEJSON(input, { - mode: ParseMode.Loose, - allowMethods: true, - }); -} - -/** @public */ -export function parseFilter(input: string) { - if (isEmpty(input)) { - return DEFAULT_FILTER; - } - return _parseFilter(input); -} - -/** @public */ -export function parseCollation(input: string) { - if (isEmpty(input)) { - return DEFAULT_COLLATION; - } - return _parseCollation(input); -} - -/** - * Validation function for a query `filter`. Must be a valid MongoDB query - * according to the query language. - * @public - * - * @return {Boolean|Object} false if not valid, or the parsed filter. - */ -export function isFilterValid(input: string) { - if (isEmpty(input)) { - return DEFAULT_FILTER; - } - try { - return _parseFilter(input); - } catch (e) { - debug('Filter "%s" is invalid', input, e); - return false; - } -} - -/** - * Validation of collation object keys and values. - * @public - * - * @param {Object} collation - * @return {Boolean|Object} false if not valid, otherwise the parsed project. - */ -function _isCollationValid(collation: any) { - for (const [key, value] of Object.entries(collation)) { - if (!COLLATION_OPTIONS[key]) { - debug('Collation "%s" is invalid bc of its keys', collation); - return false; - } - if ( - COLLATION_OPTIONS[key as keyof typeof COLLATION_OPTIONS].includes( - value as string | number | boolean, - ) === false - ) { - debug('Collation "%s" is invalid bc of its values', collation); - return false; - } - } - return collation; -} - -/** - * Validation function for a query `collation`. - * @public - * - * @return {Boolean|Object} false if not valid, or the parsed filter. - */ -export function isCollationValid(input: string) { - if (isEmpty(input)) { - return DEFAULT_COLLATION; - } - try { - const parsed = _parseCollation(input); - return _isCollationValid(parsed); - } catch (e) { - debug('Collation "%s" is invalid', input, e); - return false; - } -} - -function isValueOkForProject() { - /** - * Since server 4.4, project in find queries supports everything that - * aggregations $project supports (which is basically anything at all) so we - * effectively allow everything as a project value and keep this method for - * the context - * - * @see {@link https://docs.mongodb.com/manual/release-notes/4.4/#projection} - */ - return true; -} - -/** @public */ -export function parseProject(input: string) { - if (isEmpty(input)) { - return DEFAULT_PROJECT; - } - return _parseProject(input); -} - -/** - * Validation function for a query `project`. Must only have 0 or 1 as values. - * @public - * - * @return {Boolean|Object} false if not valid, otherwise the parsed project. - */ -export function isProjectValid(input: string) { - if (isEmpty(input)) { - return DEFAULT_PROJECT; - } - - try { - const parsed = _parseProject(input); - - if (!_.isObject(parsed)) { - debug('Project "%s" is invalid. Only documents are allowed', input); - return false; - } - - if (!_.every(parsed, isValueOkForProject)) { - debug('Project "%s" is invalid bc of its values', input); - return false; - } - - return parsed; - } catch (e) { - debug('Project "%s" is invalid', input, e); - return false; - } -} - -const ALLOWED_SORT_VALUES = [1, -1, 'asc', 'desc']; - -function isValueOkForSortDocument(val: any): boolean { - return ( - _.includes(ALLOWED_SORT_VALUES, val) || - !!(_.isObject(val) && (val as { $meta: string }).$meta) - ); -} - -function isValueOkForSortArray(val: any): boolean { - return ( - _.isArray(val) && - val.length === 2 && - _.isString(val[0]) && - isValueOkForSortDocument(val[1]) - ); -} - -/** - * Validation function for a query `sort`. Must only have -1 or 1 as values. - * @public - * - * @return {Boolean|Object} false if not valid, otherwise the cleaned-up sort. - */ -export function isSortValid(input: string) { - try { - const parsed = parseSort(input); - - if (isEmpty(parsed)) { - return DEFAULT_SORT; - } - - if (_.isArray(parsed) && _.every(parsed, isValueOkForSortArray)) { - return parsed; - } - - if ( - _.isObject(parsed) && - !_.isArray(parsed) && - _.every(parsed, isValueOkForSortDocument) - ) { - return parsed; - } - - debug('Sort "%s" is invalid bc of its values', input); - return false; - } catch (e) { - debug('Sort "%s" is invalid', input, e); - return false; - } -} - -/** - * Validation function for a query `hint`. - * Must be a string, array, or a document with only -1 or 1 as values. - * @public - * - * @return false if not valid, otherwise the cleaned-up hint. - */ -export function isHintValid(input: string) { - if (isEmpty(input)) { - return DEFAULT_HINT; - } - - try { - const parsed = _parseHint(input); - - if (_.isString(parsed)) { - return parsed; - } - - if (_.isArray(parsed) || !_.isObject(parsed)) { - debug( - 'Hint "%s" is invalid. Only strings or documents are allowed', - input, - ); - return false; - } - - if (!_.every(parsed, isValueOkForHint)) { - debug('Hint "%s" is invalid bc of its values', input); - return false; - } - - return parsed; - } catch (e) { - debug('Hint "%s" is invalid', input, e); - return false; - } -} - -/** - * Validation function for a query `maxTimeMS`. Must be digits only. - * @public - * - * Returns false if not valid, otherwise the cleaned-up max time ms. - */ -export function isMaxTimeMSValid(input: string | number): number | false { - if (isEmpty(input)) { - return DEFAULT_MAX_TIME_MS; - } - return isNumberValid(input); -} - -/** - * Validation function for a query `skip`. Must be digits only. - * @public - * - * Returns false if not valid, otherwise the cleaned-up skip. - */ -export function isSkipValid(input: string | number): number | false { - if (isEmpty(input)) { - return DEFAULT_SKIP; - } - return isNumberValid(input); -} - -/** - * Validation function for a query `limit`. Must be digits only. - * @public - * - * Returns false if not valid, otherwise the cleaned-up limit. - */ -export function isLimitValid(input: string | number): number | false { - if (isEmpty(input)) { - return DEFAULT_LIMIT; - } - return isNumberValid(input); -} - -const validatorFunctions = { - isMaxTimeMSValid, - isFilterValid, - isProjectValid, - isSortValid, - isLimitValid, - isSkipValid, - isCollationValid, - isNumberValid, - isHintValid, -}; - -/** @public */ -export function validate(what: string, input: string) { - const validator = - validatorFunctions[ - `is${_.upperFirst(what)}Valid` as keyof typeof validatorFunctions - ]; - if (!validator) { - debug('Do not know how to validate `%s`. Returning false.', what); - return false; - } - return validator(input); -} - -/** @public */ -export default function queryParser( - filter: string, - project: string | null = DEFAULT_PROJECT, -) { - if (arguments.length === 1) { - if (_.isString(filter)) { - return _parseFilter(filter); - } - } - return { - filter: _parseFilter(filter), - project: project !== DEFAULT_PROJECT ? _parseProject(project) : project, - }; -} - -export { - toJSString, - DEFAULT_FILTER, - DEFAULT_SORT, - DEFAULT_LIMIT, - DEFAULT_SKIP, - DEFAULT_PROJECT, - DEFAULT_COLLATION, - DEFAULT_MAX_TIME_MS, - DEFAULT_HINT, -}; diff --git a/packages/query-parser/src/stringify.ts b/packages/query-parser/src/stringify.ts deleted file mode 100644 index a6671d8e..00000000 --- a/packages/query-parser/src/stringify.ts +++ /dev/null @@ -1,180 +0,0 @@ -/** - * TODO: lucas: this is now used in several modules (import-export, query-parser, probably others). Refactor into 1 shared place. bson? - */ -import type { - Binary, - BSONRegExp, - BSONValue, - Code, - DBRef, - Decimal128, - Double, - Int32, - Long, - ObjectId, - Timestamp, -} from 'bson'; -import { stringify as toJavascriptString } from 'javascript-stringify'; - -/** - * [`Object.prototype.toString.call(value)`, `string type name`] - * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString#Using_toString_to_detect_object_class - */ -const TYPE_FOR_TO_STRING = new Map([ - ['[object Array]', 'Array'], - ['[object Object]', 'Object'], - ['[object String]', 'String'], - ['[object Date]', 'Date'], - ['[object Number]', 'Number'], - ['[object Function]', 'Function'], - ['[object RegExp]', 'RegExp'], - ['[object Boolean]', 'Boolean'], - ['[object Null]', 'Null'], - ['[object Undefined]', 'Undefined'], -]); - -function detectType(value: BSONValue) { - return TYPE_FOR_TO_STRING.get(Object.prototype.toString.call(value)); -} - -function getTypeDescriptorForValue(value: BSONValue) { - const t = detectType(value); - const _bsontype = t === 'Object' && value._bsontype; - return { - type: _bsontype || t, - isBSON: !!_bsontype, - }; -} - -const BSON_TO_JS_STRING = { - Code: function (v: Code) { - const code = JSON.stringify(v.code); - if (v.scope) { - return `Code(${code},${JSON.stringify(v.scope)})`; - } - return `Code(${code})`; - }, - ObjectID: function (v: ObjectId) { - return `ObjectId('${v.toString('hex')}')`; - }, - ObjectId: function (v: ObjectId) { - return `ObjectId('${v.toString('hex')}')`; - }, - Binary: function (v: Binary) { - const subType = v.sub_type; - if (subType === 4 && v.buffer.length === 16) { - let uuidHex = ''; - try { - // Try to get the pretty hex version of the UUID - uuidHex = v.toUUID().toString(); - } catch { - // If uuid is not following the uuid format converting it to UUID will - // fail, we don't want the UI to fail rendering it and instead will - // just display "unformatted" hex value of the binary whatever it is - uuidHex = v.toString('hex'); - } - return `UUID('${uuidHex}')`; - } - // The `Binary.buffer.toString` type says it doesn't accept - // arguments. However it does, and a test will fail without it. - return `BinData(${subType.toString(10)}, '${v.toString('base64')}')`; - }, - DBRef: function (v: DBRef) { - // `toJSString` only returns undefined for values that stringify to - // nothing, which for an oid can only be `undefined` itself. - const oid = toJSString(v.oid, 0) ?? 'undefined'; - const args = [JSON.stringify(v.collection), oid]; - // `fields` defaults to an empty object rather than being unset, so only - // include it when it actually holds something. - const hasFields = !!v.fields && Object.keys(v.fields).length > 0; - if (v.db || hasFields) { - // `db` has to be present for `fields` to land in the right position. - args.push(v.db === undefined ? 'undefined' : JSON.stringify(v.db)); - } - if (hasFields) { - args.push(toJSString(v.fields, 0) ?? '{}'); - } - return `DBRef(${args.join(', ')})`; - }, - Timestamp: function (v: Timestamp) { - return `Timestamp({ t: ${v.high}, i: ${v.low} })`; - }, - Long: function (v: Long) { - return `NumberLong('${v.toString()}')`; - }, - Decimal128: function (v: Decimal128) { - return `NumberDecimal('${v.toString()}')`; - }, - Double: function (v: Double) { - return `Double('${v.toString()}')`; - }, - Int32: function (v: Int32) { - return `NumberInt('${v.toString()}')`; - }, - MaxKey: function () { - return 'MaxKey()'; - }, - MinKey: function () { - return 'MinKey()'; - }, - Date: function (v: Date) { - return BSON_TO_JS_STRING.ISODate(v); - }, - ISODate: function (v: Date) { - try { - return `ISODate('${v.toISOString()}')`; - } catch (ex) { - return `ISODate('${v.toString()}')`; - } - }, - RegExp: function (v: RegExp) { - let o = ''; - let hasOptions = false; - - if (v.global) { - hasOptions = true; - o += 'g'; - } - if (v.ignoreCase) { - hasOptions = true; - o += 'i'; - } - if (v.multiline) { - hasOptions = true; - o += 'm'; - } - - return `RegExp(${JSON.stringify(v.source)}${hasOptions ? `, '${o}'` : ''})`; - }, - BSONRegExp: function (v: BSONRegExp) { - const hasOptions = v.options && v.options?.length > 0; - let ctor = 'RegExp'; - try { - RegExp(v.pattern, v.options || undefined); - } catch { - ctor = 'BSONRegExp'; - } - return `${ctor}(${JSON.stringify(v.pattern)}${ - hasOptions ? `, '${v.options}'` : '' - })`; - }, -}; - -/** @public */ -export function toJSString( - obj: unknown, - ind: Parameters[2] = 2, -): string | undefined { - return toJavascriptString( - obj, - function (value, indent, stringify) { - const t = getTypeDescriptorForValue(value); - const toJs = BSON_TO_JS_STRING[t.type as keyof typeof BSON_TO_JS_STRING]; - if (!toJs) { - return stringify(value); - } - return toJs(value); - }, - ind, - ); -} diff --git a/packages/query-parser/tsconfig-lint.json b/packages/query-parser/tsconfig-lint.json deleted file mode 100644 index 6bdef84f..00000000 --- a/packages/query-parser/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/query-parser/tsconfig.json b/packages/query-parser/tsconfig.json deleted file mode 100644 index 4e6d1b8b..00000000 --- a/packages/query-parser/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "@mongodb-js/tsconfig-devtools/tsconfig.common.json" -} diff --git a/packages/shell-bson-parser/.depcheckrc b/packages/shell-bson-parser/.depcheckrc index ec3b275b..86cd7d1f 100644 --- a/packages/shell-bson-parser/.depcheckrc +++ b/packages/shell-bson-parser/.depcheckrc @@ -3,7 +3,5 @@ ignores: - '@mongodb-js/tsconfig-devtools' - '@types/chai' - 'sinon' - # cyclic monorepo dependency, but only for the benchmarks - - 'mongodb-query-parser' ignore-patterns: - 'dist' diff --git a/packages/shell-bson-parser/.eslintrc.js b/packages/shell-bson-parser/.eslintrc.cjs similarity index 78% rename from packages/shell-bson-parser/.eslintrc.js rename to packages/shell-bson-parser/.eslintrc.cjs index 83296d73..4b264caf 100644 --- a/packages/shell-bson-parser/.eslintrc.js +++ b/packages/shell-bson-parser/.eslintrc.cjs @@ -5,4 +5,5 @@ module.exports = { tsconfigRootDir: __dirname, project: ['./tsconfig-lint.json'], }, + ignorePatterns: ['webpack.worker.config.cjs'], }; diff --git a/packages/query-parser/.mocharc.js b/packages/shell-bson-parser/.mocharc.cjs similarity index 100% rename from packages/query-parser/.mocharc.js rename to packages/shell-bson-parser/.mocharc.cjs diff --git a/packages/shell-bson-parser/.mocharc.js b/packages/shell-bson-parser/.mocharc.js deleted file mode 100644 index 64afeb1f..00000000 --- a/packages/shell-bson-parser/.mocharc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('@mongodb-js/mocha-config-devtools'); diff --git a/packages/shell-bson-parser/README.md b/packages/shell-bson-parser/README.md index 3f389d55..3b833c3c 100644 --- a/packages/shell-bson-parser/README.md +++ b/packages/shell-bson-parser/README.md @@ -5,6 +5,8 @@ This library does not validate that these queries are correct. It's focus is on This library creates an AST from the proposed input, and then traverses this AST to check if it looks like a valid MongoDB query. If it does, the library will then evaluate the code to produce the parsed query. +Parsing runs off the main thread inside a Worker, so `parse` is asynchronous and returns a `Promise`. See [Worker](#worker) for details. + This library currently supports three different modes for parsing queries: **strict**: [default] Disallows comments and calling methods @@ -12,7 +14,7 @@ This library currently supports three different modes for parsing queries: ```javascript import parse from '@mongodb-js/shell-bson-parser'; -const query = parse( +const query = await parse( `{ _id: ObjectID("132323"), simpleCalc: 6, @@ -31,7 +33,7 @@ const query = parse( ```javascript import parse from '@mongodb-js/shell-bson-parser'; -const query = parse( +const query = await parse( `{ _id: ObjectID("132323"), simpleCalc: Math.max(1,2,3) * Math.min(4,3,2) @@ -49,7 +51,7 @@ const query = parse( ```javascript import parse from '@mongodb-js/shell-bson-parser'; -const query = parse( +const query = await parse( `{ _id: ObjectID("132323"), // a helpful comment simpleCalc: Math.max(1,2,3) * Math.min(4,3,2) @@ -82,3 +84,21 @@ The flags can be set to override the default value from a given mode, ie: ``` This options object will disallow method calls, but will allow comments + +## Worker + +Every call to `parse` is sent to a shared worker (using [`web-worker`](https://github.com/developit/web-worker), so it works in both Node.js and browsers). The worker is created lazily on the first call and reused for later calls. BSON values are serialized across the worker boundary and rebuilt as `bson` instances on the calling side. + +If parsing throws inside the worker, the returned promise rejects with the error message. + +Call `terminateWorker()` to shut down the worker (for example in test teardown or when unmounting). Any pending `parse` calls are rejected. The next `parse` call starts a new worker. + +```javascript +import { parse, terminateWorker } from '@mongodb-js/shell-bson-parser'; + +const query = await parse('{ a: 1 }'); + +terminateWorker(); +``` + +This package is ESM only. diff --git a/packages/shell-bson-parser/benchmark/performance.js b/packages/shell-bson-parser/benchmark/performance.js index b52c7919..ad63a970 100644 --- a/packages/shell-bson-parser/benchmark/performance.js +++ b/packages/shell-bson-parser/benchmark/performance.js @@ -2,7 +2,6 @@ 'use strict'; const Benchmark = require('benchmark'); const { default: parseEJSON } = require('../'); -const { parseFilter } = require('mongodb-query-parser'); const sample = `({ "_id3": { @@ -68,9 +67,6 @@ suite .add('parseEJSON#weak', function () { parseEJSON(sample, { weakParsing: true }); }) - .add('mongodb-query-parser#insecure', function () { - parseFilter(sample); - }) .add('eval', function () { eval(sample); }) diff --git a/packages/shell-bson-parser/package.json b/packages/shell-bson-parser/package.json index 3f8a1362..b4d63505 100644 --- a/packages/shell-bson-parser/package.json +++ b/packages/shell-bson-parser/package.json @@ -22,22 +22,21 @@ "dist" ], "license": "Apache-2.0", + "type": "module", "main": "dist/index.js", "types": "./dist/index.d.ts", "exports": { - "require": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "import": { - "types": "./dist/index.d.ts", - "default": "./dist/.esm-wrapper.mjs" - } + "types": "./dist/index.d.ts", + "require": "./dist/cjs/index.js", + "import": "./dist/index.js", + "default": "./dist/index.js" }, "scripts": { "bootstrap": "npm run compile", "prepublishOnly": "npm run compile", - "compile": "tsc -p tsconfig.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", + "compile": "tsc -p tsconfig.json && npm run bundle-worker && npm run bundle-cjs", + "bundle-worker": "webpack --config webpack.worker.config.cjs", + "bundle-cjs": "webpack --config webpack.cjs.config.cjs && node -e \"require('fs').writeFileSync('dist/cjs/package.json', JSON.stringify({type:'commonjs'})+'\\n')\"", "typecheck": "tsc --noEmit", "eslint": "eslint", "prettier": "prettier", @@ -45,6 +44,7 @@ "depcheck": "depcheck", "check": "npm run typecheck && npm run lint && npm run depcheck", "check-ci": "npm run check", + "pretest": "npm run compile", "test": "mocha", "test-cov": "nyc -x \"**/*.spec.*\" --reporter=lcov --reporter=text --reporter=html npm run test", "test-watch": "npm run test -- --watch", @@ -53,7 +53,8 @@ }, "dependencies": { "acorn": "^8.18.0", - "javascript-stringify": "^2.1.0" + "javascript-stringify": "^2.1.0", + "web-worker": "^1.5.0" }, "peerDependencies": { "bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.0.0" @@ -71,11 +72,11 @@ "chai": "^4.5.0", "depcheck": "^1.4.7", "eslint": "^7.25.0 || ^8.0.0", - "gen-esm-wrapper": "^1.1.3", "mocha": "^8.4.0", "nyc": "^15.1.0", "prettier": "^3.8.1", "sinon": "^9.2.3", - "typescript": "^5.9.3" + "typescript": "^5.9.3", + "webpack": "^5.82.0" } } diff --git a/packages/shell-bson-parser/src/check.ts b/packages/shell-bson-parser/src/check.ts index 3bf149fa..3c3f49b6 100644 --- a/packages/shell-bson-parser/src/check.ts +++ b/packages/shell-bson-parser/src/check.ts @@ -1,7 +1,7 @@ import type { Node, BaseCallExpression, Identifier } from 'estree'; -import { GLOBAL_FUNCTIONS, isMethodWhitelisted, GLOBALS } from './scope'; -import type { Options } from './options'; +import { GLOBAL_FUNCTIONS, isMethodWhitelisted, GLOBALS } from './scope.js'; +import type { Options } from './options.js'; class Checker { constructor(private options: Options) {} diff --git a/packages/shell-bson-parser/src/eval.ts b/packages/shell-bson-parser/src/eval.ts index 51097a8f..ec2ac6b6 100644 --- a/packages/shell-bson-parser/src/eval.ts +++ b/packages/shell-bson-parser/src/eval.ts @@ -6,7 +6,7 @@ import type { FunctionExpression, ArrowFunctionExpression, } from 'estree'; -import { getScopeFunction, getClass, GLOBALS } from './scope'; +import { getScopeFunction, getClass, GLOBALS } from './scope.js'; const unaryExpression = (node: UnaryExpression): any => { if (!node.prefix) throw new Error('Malformed UnaryExpression'); diff --git a/packages/shell-bson-parser/src/index.spec.ts b/packages/shell-bson-parser/src/index.spec.ts new file mode 100644 index 00000000..2f4bc8fa --- /dev/null +++ b/packages/shell-bson-parser/src/index.spec.ts @@ -0,0 +1,53 @@ +import { expect } from 'chai'; + +import * as api from './index.js'; +import { terminateWorker } from './worker-client.js'; +import { PARSE_TEST_CASES } from './../test/parse-test-cases.js'; + +describe('shell-bson-parser with webworker processing', function () { + const initialWorkerScriptUrl = process.env.TEST_WORKER_SCRIPT_URL; + + before(function () { + process.env.TEST_WORKER_SCRIPT_URL = '../dist/worker.js'; + }); + + after(function () { + if (initialWorkerScriptUrl) { + process.env.TEST_WORKER_SCRIPT_URL = initialWorkerScriptUrl; + } else { + delete process.env.TEST_WORKER_SCRIPT_URL; + } + terminateWorker(); + }); + + it('keeps the error type of a failed parse', async function () { + try { + await api.parse('{ a: }'); + expect.fail('Expected parse to throw'); + } catch (err) { + expect(err).to.be.instanceOf(SyntaxError); + expect((err as Error).name).to.equal('SyntaxError'); + } + }); + + describe('parse', function () { + for (const { title, input, options, expected } of PARSE_TEST_CASES) { + it(title, async function () { + const res = await api.parse(input, options); + expect(res).to.deep.equal(expected); + }); + } + }); + + describe('terminateWorker', function () { + it('starts a new worker after termination', async function () { + const res1 = await api.parse('{code: "BER"}'); + expect(res1).to.deep.equal({ code: 'BER' }); + + terminateWorker(); + + const res2 = await api.parse('{city: "berlin"}'); + expect(res2).to.deep.equal({ city: 'berlin' }); + }); + }); +}); diff --git a/packages/shell-bson-parser/src/index.ts b/packages/shell-bson-parser/src/index.ts index b6839857..f639f256 100644 --- a/packages/shell-bson-parser/src/index.ts +++ b/packages/shell-bson-parser/src/index.ts @@ -1,6 +1,12 @@ -import { parse } from './parse'; -import { toJSString } from './stringify'; -import { ParseMode } from './options'; +import type { parse as parseSync } from './parse.js'; +import { toJSString } from './stringify.js'; +import { ParseMode } from './options.js'; +import { callWorker, terminateWorker } from './worker-client.js'; + +export const parse = ( + ...args: Parameters +): Promise> => callWorker(args); + +export { ParseMode, toJSString, terminateWorker }; -export { ParseMode, parse, toJSString }; export default parse; diff --git a/packages/shell-bson-parser/src/parse.spec.ts b/packages/shell-bson-parser/src/parse.spec.ts index ebb043e9..73378772 100644 --- a/packages/shell-bson-parser/src/parse.spec.ts +++ b/packages/shell-bson-parser/src/parse.spec.ts @@ -1,11 +1,11 @@ import * as bson from 'bson'; import { expect } from 'chai'; import type { SinonSandbox } from 'sinon'; -import { createSandbox } from 'sinon'; -import { parse } from './parse'; -import type { Options } from './options'; -import { ParseMode } from './options'; -import { PARSE_TEST_CASES } from '../test/parse-test-cases'; +import sinon from 'sinon'; +import { parse } from './parse.js'; +import type { Options } from './options.js'; +import { ParseMode } from './options.js'; +import { PARSE_TEST_CASES } from '../test/parse-test-cases.js'; describe('parse', function () { for (const { title, input, options, expected } of PARSE_TEST_CASES) { @@ -33,7 +33,7 @@ describe('parse', function () { let sandbox: SinonSandbox; beforeEach(function () { - sandbox = createSandbox(); + sandbox = sinon.createSandbox(); sandbox.replace((bson as any).UUID.prototype, 'toHexString', function () { return '00112233-4455-6677-8899-aabbccddeeff'; @@ -87,7 +87,7 @@ describe('parse', function () { let sandbox: SinonSandbox; beforeEach(function () { - sandbox = createSandbox(); + sandbox = sinon.createSandbox(); }); afterEach(function () { sandbox.restore(); diff --git a/packages/shell-bson-parser/src/parse.ts b/packages/shell-bson-parser/src/parse.ts index ead7942d..6788398c 100644 --- a/packages/shell-bson-parser/src/parse.ts +++ b/packages/shell-bson-parser/src/parse.ts @@ -1,10 +1,10 @@ import { parse as parseAST } from 'acorn'; import type { Node } from 'estree'; -import { checkTree } from './check'; -import { executeAST } from './eval'; -import type { Options } from './options'; -import { buildOptions } from './options'; +import { checkTree } from './check.js'; +import { executeAST } from './eval.js'; +import type { Options } from './options.js'; +import { buildOptions } from './options.js'; function buildAST(input: string): { ast: Node; hasComments: boolean } { let hasComments = false; diff --git a/packages/shell-bson-parser/src/stringify.spec.ts b/packages/shell-bson-parser/src/stringify.spec.ts index dfcb7113..94b5c873 100644 --- a/packages/shell-bson-parser/src/stringify.spec.ts +++ b/packages/shell-bson-parser/src/stringify.spec.ts @@ -1,7 +1,7 @@ import assert from 'assert'; -import bson from 'bson'; -import { toJSString } from './stringify'; -import { parse } from './parse'; +import * as bson from 'bson'; +import { toJSString } from './stringify.js'; +import { parse } from './parse.js'; describe('stringify', function () { describe('toJSString', function () { diff --git a/packages/shell-bson-parser/src/structured-clone-bson.spec.ts b/packages/shell-bson-parser/src/structured-clone-bson.spec.ts new file mode 100644 index 00000000..5aa2b4a8 --- /dev/null +++ b/packages/shell-bson-parser/src/structured-clone-bson.spec.ts @@ -0,0 +1,296 @@ +import * as bson from 'bson'; +import { expect } from 'chai'; + +import { trackBSON, untrackBSON } from './structured-clone-bson.js'; + +function roundTrip(value: T): T { + return untrackBSON(structuredClone(trackBSON(value))); +} + +const bsonValues: [string, unknown][] = [ + ['ObjectId', new bson.ObjectId('5e159ba7eac34211f2252aaa')], + ['Long', bson.Long.fromNumber(123456789)], + ['unsigned Long', bson.Long.fromString('18446744073709551615', true)], + ['Timestamp', new bson.Timestamp({ t: 100, i: 7 })], + ['Decimal128', bson.Decimal128.fromString('1.5')], + ['Double', new bson.Double(10.1)], + ['Int32', new bson.Int32(10)], + ['Code', new bson.Code('function() {}')], + [ + 'Code with scope', + new bson.Code('function() {}', { x: new bson.ObjectId() }), + ], + ['Binary', new bson.Binary(Buffer.from('dGVzdAo=', 'base64'), 3)], + ['UUID', new bson.UUID('3d37923d-ab8e-4931-9e46-93df5fd3599e')], + ['empty Binary', new bson.Binary()], + ['DBRef', new bson.DBRef('tests', new bson.ObjectId(), 'db', { a: 1 })], + ['BSONRegExp', new bson.BSONRegExp('abc', 'i')], + ['BSONSymbol', new bson.BSONSymbol('sym')], + ['MaxKey', new bson.MaxKey()], + ['MinKey', new bson.MinKey()], +]; + +describe('structured-clone-bson', function () { + describe('round-trip', function () { + for (const [title, value] of bsonValues) { + it(`round-trips ${title}`, function () { + const result = roundTrip(value); + expect(result).to.be.instanceOf((value as object).constructor); + expect(result).to.deep.equal(value); + }); + } + + it('round-trips plain JS values untouched', function () { + const value = { + d: new Date(0), + r: /a/g, + n: 1, + s: 'str', + u: undefined, + nul: null, + big: 10n, + nan: NaN, + arr: [1, 'two', [3]], + err: new SyntaxError('fail'), + }; + expect(roundTrip(value)).to.deep.equal(value); + }); + + it('round-trips BSON nested in arrays, objects and Map', function () { + const oid = new bson.ObjectId(); + const value = { + arr: [oid, { deep: [bson.Long.fromNumber(1)] }], + map: new Map([['k', oid]]), + }; + const result = roundTrip(value); + expect(result).to.deep.equal(value); + expect(result.map.get('k')).to.be.instanceOf(bson.ObjectId); + }); + + it('keeps `__proto__` and `constructor` as own keys', function () { + const value = JSON.parse('{"__proto__": {"x": 1}, "constructor": 1}'); + const result = roundTrip(value); + expect(Object.keys(result)).to.deep.equal(['__proto__', 'constructor']); + expect(Object.getPrototypeOf(result)).to.equal(Object.prototype); + }); + + it('restores a plain Binary with sub_type 4 as Binary, not UUID', function () { + const bin = new bson.Binary(Buffer.alloc(16, 7), 4); + const result = roundTrip({ b: bin }) as { b: bson.Binary }; + expect(result.b).to.be.instanceOf(bson.Binary); + expect(result.b).not.to.be.instanceOf(bson.UUID); + }); + + it('handles a Code value with no scope at all', function () { + const code = new bson.Code('function () {}'); + // bson's own `Code` constructor defaults `scope` to `null`, not + // `undefined`, when none is given. + expect(code.scope).to.equal(null); + const result = roundTrip({ c: code }) as { c: bson.Code }; + expect(result.c).to.be.instanceOf(bson.Code); + expect(result.c.scope).to.equal(null); + }); + + it('handles a DBRef with no fields at all', function () { + const ref = new bson.DBRef('coll', new bson.ObjectId()); + // bson's own `DBRef` constructor defaults `fields` to `{}`, not + // `undefined`, when none is given. + expect(ref.fields).to.deep.equal({}); + const result = roundTrip({ ref }) as { ref: bson.DBRef }; + expect(result.ref).to.be.instanceOf(bson.DBRef); + expect(result.ref.fields).to.deep.equal({}); + }); + + it('restores DBRef, including the ObjectId oid and a nested ObjectId in fields', function () { + const oid = new bson.ObjectId(); + const fieldsId = new bson.ObjectId(); + const ref = new bson.DBRef('coll', oid, 'db', { extra: fieldsId }); + const result = roundTrip({ ref }) as { ref: bson.DBRef }; + + expect(result.ref).to.be.instanceOf(bson.DBRef); + expect(result.ref.collection).to.equal('coll'); + expect(result.ref.db).to.equal('db'); + expect(result.ref.oid).to.be.instanceOf(bson.ObjectId); + expect(result.ref.oid.toHexString()).to.equal(oid.toHexString()); + expect(result.ref.fields?.extra).to.be.instanceOf(bson.ObjectId); + expect( + (result.ref.fields?.extra as bson.ObjectId).toHexString(), + ).to.equal(fieldsId.toHexString()); + }); + + it('restores BSON values nested many levels deep across mixed arrays/objects', function () { + const id = new bson.ObjectId(); + const input = { + a: [{ b: { c: [1, 2, { d: { e: [id] } }] } }], + }; + + const result = roundTrip(input); + const found = result.a[0].b.c[2] as { d: { e: [bson.ObjectId] } }; + expect(found.d.e[0]).to.be.instanceOf(bson.ObjectId); + expect(found.d.e[0].toHexString()).to.equal(id.toHexString()); + }); + + it('restores BSON values found inside a Map, as both key and value', function () { + const keyId = new bson.ObjectId(); + const valueId = new bson.ObjectId(); + const input = { m: new Map([[keyId, valueId]]) }; + + const result = roundTrip(input) as { + m: Map; + }; + expect(result.m).to.be.instanceOf(Map); + expect(result.m.size).to.equal(1); + const [[k, v]] = [...result.m]; + expect(k).to.be.instanceOf(bson.ObjectId); + expect(k.toHexString()).to.equal(keyId.toHexString()); + expect(v).to.be.instanceOf(bson.ObjectId); + expect(v.toHexString()).to.equal(valueId.toHexString()); + }); + + it('restores BSON values found inside a Set', function () { + const id = new bson.ObjectId(); + const input = { s: new Set([id, 'plain-value']) }; + + const result = roundTrip(input) as { s: Set }; + expect(result.s).to.be.instanceOf(Set); + const values = [...result.s]; + expect(values).to.have.lengthOf(2); + const restoredId = values.find( + (v) => v instanceof bson.ObjectId, + ) as bson.ObjectId; + expect(restoredId.toHexString()).to.equal(id.toHexString()); + expect(values).to.include('plain-value'); + }); + + it('restores BSON values nested inside a Map value that is itself an array/object', function () { + const id = new bson.ObjectId(); + const input = { m: new Map([['key', { nested: [id] }]]) }; + + const result = roundTrip(input) as { + m: Map; + }; + const nested = result.m.get('key'); + expect(nested?.nested[0]).to.be.instanceOf(bson.ObjectId); + expect(nested?.nested[0].toHexString()).to.equal(id.toHexString()); + }); + + it('restores BSON values nested levels, each with a different BSON type', function () { + const objectId = new bson.ObjectId(); + const dbRefOid = new bson.ObjectId(); + const long = bson.Long.fromNumber(42); + const decimal = bson.Decimal128.fromString('3.14'); + + const input = { + level1: [ + { + level2: new Map([ + [ + 'key', + { + level3: new Set([ + objectId, + { + level4: new bson.DBRef('coll', dbRefOid, undefined, { + long, + decimal, + }), + }, + ]), + }, + ], + ]), + }, + ], + }; + + const result = roundTrip(input); + + expect(result).to.deep.equal(input); + + const [restoredId, { level4 }] = Array.from( + result.level1[0].level2.get('key')?.level3 ?? [], + ) as [bson.ObjectId, { level4: bson.DBRef }]; + + expect(restoredId.toHexString()).to.equal(objectId.toHexString()); + expect(level4).to.be.instanceOf(bson.DBRef); + expect(level4.oid.toHexString()).to.equal(dbRefOid.toHexString()); + expect(level4.fields.long).to.be.instanceOf(bson.Long); + expect((level4.fields.long as bson.Long).toString()).to.equal('42'); + expect(level4.fields.decimal).to.be.instanceOf(bson.Decimal128); + expect((level4.fields.decimal as bson.Decimal128).toString()).to.equal( + '3.14', + ); + }); + + it('leaves a raw Buffer/Uint8Array completely untouched', function () { + const buf = Buffer.from([1, 2, 3]); + const result = roundTrip({ buf }) as { buf: Uint8Array }; + expect(Buffer.from(result.buf).equals(buf)).to.equal(true); + }); + + it('preserves shared-reference identity for a BSON value reachable via two paths', function () { + const id = new bson.ObjectId(); + const input = { a: id, b: id }; + const result = roundTrip(input) as { a: bson.ObjectId; b: bson.ObjectId }; + expect(result.a).to.be.instanceOf(bson.ObjectId); + expect(result.a).to.equal(result.b); + }); + + it('does not hang on a circular reference and preserves the cycle', function () { + const id = new bson.ObjectId(); + const input: { id: bson.ObjectId; self?: unknown } = Object.assign( + Object.create(null), + { id }, + ); + input.self = input; + + const result = roundTrip(input) as { id: bson.ObjectId; self: unknown }; + expect(result.id).to.be.instanceOf(bson.ObjectId); + expect(result.self).to.equal(result); + }); + }); + + describe('trackBSON', function () { + it('collects BSON values into a Map keyed by the instance', function () { + const id = new bson.ObjectId(); + const { bsonTypes } = trackBSON({ _id: id, a: 1 }); + expect(bsonTypes.size).to.equal(1); + expect(bsonTypes.get(id)).to.equal('ObjectId'); + }); + + it('does not collect non-BSON values', function () { + const { bsonTypes } = trackBSON({ a: 1, b: [1, 2, 3] }); + expect(bsonTypes.size).to.equal(0); + }); + + it('records UUID sub_type 4 as UUID, not Binary', function () { + const uuid = new bson.UUID(); + const { bsonTypes } = trackBSON({ u: uuid }); + expect(bsonTypes.get(uuid)).to.equal('UUID'); + }); + + it('records plain Binary with sub_type 4 as Binary', function () { + const bin = new bson.Binary(Buffer.alloc(16), 4); + const { bsonTypes } = trackBSON({ b: bin }); + expect(bsonTypes.get(bin)).to.equal('Binary'); + }); + + it('collects shared references only once', function () { + const id = new bson.ObjectId(); + const { bsonTypes } = trackBSON({ a: id, b: id }); + expect(bsonTypes.size).to.equal(1); + }); + }); + + describe('untrackBSON', function () { + it('throws on unknown serialized types', function () { + const obj = {}; + expect(() => + untrackBSON({ + data: obj, + bsonTypes: new Map([[obj, 'Nope']]), + }), + ).to.throw(/unknown BSON type/); + }); + }); +}); diff --git a/packages/shell-bson-parser/src/structured-clone-bson.ts b/packages/shell-bson-parser/src/structured-clone-bson.ts new file mode 100644 index 00000000..e1fbca01 --- /dev/null +++ b/packages/shell-bson-parser/src/structured-clone-bson.ts @@ -0,0 +1,131 @@ +import { BSON } from 'bson'; + +export type TrackedPayload = { + data: T; + bsonTypes: Map; +}; + +const BSON_PROTOTYPES = new Map([ + ['BSONRegExp', BSON.BSONRegExp.prototype], + ['BSONSymbol', BSON.BSONSymbol.prototype], + ['Binary', BSON.Binary.prototype], + ['Code', BSON.Code.prototype], + ['DBRef', BSON.DBRef.prototype], + ['Decimal128', BSON.Decimal128.prototype], + ['Double', BSON.Double.prototype], + ['Int32', BSON.Int32.prototype], + ['Long', BSON.Long.prototype], + ['MaxKey', BSON.MaxKey.prototype], + ['MinKey', BSON.MinKey.prototype], + ['ObjectId', BSON.ObjectId.prototype], + ['Timestamp', BSON.Timestamp.prototype], + ['UUID', BSON.UUID.prototype], +]); + +function isBsonValue(value: object): value is { _bsontype: string } { + const tag = (value as { _bsontype?: unknown })._bsontype; + return typeof tag === 'string' && BSON_PROTOTYPES.has(tag); +} + +function isMap(m: unknown): m is Map { + return ( + !!m && + typeof m === 'object' && + Symbol.toStringTag in m && + (m as { [Symbol.toStringTag]: unknown })[Symbol.toStringTag] === 'Map' + ); +} + +function isSet(s: unknown): s is Set { + return ( + !!s && + typeof s === 'object' && + Symbol.toStringTag in s && + (s as { [Symbol.toStringTag]: unknown })[Symbol.toStringTag] === 'Set' + ); +} + +/** + * `Code.scope` and `DBRef.oid`/`fields` are themselves ordinary values that + * might contain further BSON (e.g. an `ObjectId` inside a `DBRef`'s `oid` or + * a `Code`'s `scope`). The outer `Code`/`DBRef` is otherwise treated as an + * opaque leaf by the walk, so without this they'd never get visited. + * + * Pushes onto `stack` in place. + */ +function pushNestedBsonDocuments( + tag: string, + item: object, + stack: unknown[], +): void { + if (tag === 'Code') { + stack.push((item as BSON.Code).scope); + } else if (tag === 'DBRef') { + stack.push((item as BSON.DBRef).oid, (item as BSON.DBRef).fields); + } +} + +export function trackBSON(data: T): TrackedPayload { + const bsonTypes = new Map(); + const stack: unknown[] = [data]; + const visited = new Set(); + + while (stack.length > 0) { + const item = stack.pop(); + + if (item === null || typeof item !== 'object') { + continue; + } + if (visited.has(item)) { + continue; + } + visited.add(item); + + if (isBsonValue(item)) { + // UUID and Binary share the _bsontype tag, so preserve their actual + // class using the prototype rather than Binary's subtype. + const tag = item instanceof BSON.UUID ? 'UUID' : item._bsontype; + bsonTypes.set(item, tag); + pushNestedBsonDocuments(tag, item, stack); + continue; + } + + if (Array.isArray(item)) { + for (const value of item) stack.push(value); + continue; + } + + if (isMap(item)) { + for (const [key, value] of item) stack.push(key, value); + continue; + } + + if (isSet(item)) { + for (const value of item) stack.push(value); + continue; + } + + const proto = Object.getPrototypeOf(item); + if (proto === Object.prototype || proto === null) { + for (const value of Object.values(item)) stack.push(value); + } + } + + return { data, bsonTypes }; +} + +export function untrackBSON(payload: TrackedPayload): T { + const { data, bsonTypes } = payload; + + for (const [item, tag] of bsonTypes) { + const prototype = BSON_PROTOTYPES.get(tag); + if (!prototype) { + throw new Error( + `Cannot unmark unknown BSON type crossing the worker boundary: ${tag}`, + ); + } + Object.setPrototypeOf(item, prototype); + } + + return data; +} diff --git a/packages/shell-bson-parser/src/worker-client.ts b/packages/shell-bson-parser/src/worker-client.ts new file mode 100644 index 00000000..8a6ba2a5 --- /dev/null +++ b/packages/shell-bson-parser/src/worker-client.ts @@ -0,0 +1,117 @@ +import * as WebWorkerModule from 'web-worker'; +const WebWorker = (WebWorkerModule as unknown as { default: typeof Worker }) + .default; +import { trackBSON, untrackBSON } from './structured-clone-bson.js'; +import type { WorkerResponse } from './worker-types.js'; + +let worker: Worker | null = null; +let blobUrl: string | null = null; +let nextId = 0; +const pending = new Map< + number, + { resolve: (v: any) => void; reject: (e: Error) => void } +>(); + +const isNodeEnv = + typeof window === 'undefined' && + typeof process !== 'undefined' && + !!process.versions?.node; + +async function getWorkerScriptUrl(): Promise { + if (process.env.TEST_WORKER_SCRIPT_URL) { + return new URL( + process.env.TEST_WORKER_SCRIPT_URL, + import.meta.url, + ).toString(); + } + if (isNodeEnv) { + return new URL('./worker.js', import.meta.url).toString(); + } + + // On browser env we want to fetch and blob so that the worker + // script can run on the web running locally. + const scriptUrl = new URL('./worker.js', import.meta.url); + const response = await fetch(scriptUrl); + if (!response.ok) { + throw new Error( + `Failed to fetch shell-bson-parser worker script: ${response.status} ${response.statusText}`, + ); + } + const code = await response.text(); + blobUrl = URL.createObjectURL(new Blob([code], { type: 'text/javascript' })); + return blobUrl; +} + +async function createWorker(): Promise { + if (worker) { + return worker; + } + + const scriptUrl = await getWorkerScriptUrl(); + worker = new WebWorker(scriptUrl, { type: 'module' }); + + const onMessageHandler = (event: MessageEvent) => { + const response = event.data; + const entry = pending.get(response.id); + if (!entry) { + return; + } + pending.delete(response.id); + if (!response.ok) { + entry.reject(response.error); + return; + } + try { + entry.resolve(untrackBSON(response.result)); + } catch (err) { + entry.reject(err as Error); + } + }; + + const onErrorHandler = (event: ErrorEvent) => { + terminateWorker(new Error(event.message || 'Worker error')); + }; + + const onMessageErrorHandler = () => { + terminateWorker(new Error('Worker message could not be deserialized')); + }; + + worker.addEventListener('message', onMessageHandler); + worker.addEventListener('error', onErrorHandler); + worker.addEventListener('messageerror', onMessageErrorHandler); + + return worker; +} + +export async function callWorker(args: unknown[]): Promise { + const activeWorker = await createWorker(); + const id = nextId++; + const promise = new Promise((resolve, reject) => { + pending.set(id, { resolve, reject }); + }); + try { + activeWorker.postMessage({ + id, + args: trackBSON(args), + }); + } catch (err) { + pending.get(id)?.reject(err as Error); + pending.delete(id); + } + return promise; +} + +export function terminateWorker( + reason: Error = new Error('Worker terminated'), +): void { + if (worker) worker.terminate(); + if (blobUrl) URL.revokeObjectURL(blobUrl); + + worker = null; + blobUrl = null; + + for (const [id, entry] of pending) { + entry.reject(reason); + pending.delete(id); + } +} diff --git a/packages/shell-bson-parser/src/worker-types.ts b/packages/shell-bson-parser/src/worker-types.ts new file mode 100644 index 00000000..311d44d4 --- /dev/null +++ b/packages/shell-bson-parser/src/worker-types.ts @@ -0,0 +1,10 @@ +import type { TrackedPayload } from './structured-clone-bson.js'; + +export type WorkerRequest = { + id: number; + args: TrackedPayload; +}; + +export type WorkerResponse = { id: number } & ( + { ok: true; result: TrackedPayload } | { ok: false; error: Error } +); diff --git a/packages/shell-bson-parser/src/worker.ts b/packages/shell-bson-parser/src/worker.ts new file mode 100644 index 00000000..094278d6 --- /dev/null +++ b/packages/shell-bson-parser/src/worker.ts @@ -0,0 +1,22 @@ +import { parse } from './parse.js'; +import { trackBSON, untrackBSON } from './structured-clone-bson.js'; + +import type { WorkerRequest, WorkerResponse } from './worker-types.js'; + +// Exported for test +export function handleRequest(request: WorkerRequest): WorkerResponse { + const { id, args } = request; + try { + const value = parse(...(untrackBSON(args) as Parameters)); + const result = trackBSON(value); + return { id, ok: true, result }; + } catch (err) { + return { id, ok: false, error: err as Error }; + } +} + +if (typeof self !== 'undefined') { + self.onmessage = (event: MessageEvent) => { + (self as unknown as Worker).postMessage(handleRequest(event.data)); + }; +} diff --git a/packages/shell-bson-parser/test/parse-test-cases.ts b/packages/shell-bson-parser/test/parse-test-cases.ts index 0f500e53..f2db154c 100644 --- a/packages/shell-bson-parser/test/parse-test-cases.ts +++ b/packages/shell-bson-parser/test/parse-test-cases.ts @@ -1,6 +1,6 @@ import * as bson from 'bson'; -import { ParseMode } from '../src'; -import type { Options } from '../src/options'; +import { ParseMode } from '../src/index.js'; +import type { Options } from '../src/options.js'; export type ParseTestCase = { title: string; @@ -96,7 +96,7 @@ export const PARSE_TEST_CASES: ParseTestCase[] = [ MinKey: new bson.MinKey(), ObjectID: new bson.ObjectId('5e159ba7eac34211f2252aaa'), ObjectId: new bson.ObjectId('5e159ba7eac34211f2252aaa'), - Symbol: new (bson as any).BSONSymbol('symbol'), + Symbol: new bson.BSONSymbol('symbol'), Timestamp: new bson.Timestamp({ t: 100, i: 0 }), Timestamp_object: new bson.Timestamp({ t: 1, i: 2 }), Timestamp_long: new bson.Timestamp(bson.Long.fromNumber(8589934593)), @@ -222,32 +222,36 @@ export const PARSE_TEST_CASES: ParseTestCase[] = [ // Flattened from a loop over ParseMode.{Extended,Strict,Loose}, each // asserting 4 separate expressions. ...[ParseMode.Extended, ParseMode.Strict, ParseMode.Loose].flatMap( - (mode): ParseTestCase[] => [ - { - title: `should not allow calling functions that only exist as Object.prototype properties (mode=${mode}) - Date.constructor`, - input: '{ date: Date.constructor("") }', - options: { mode }, - expected: '', - }, - { - title: `should not allow calling functions that only exist as Object.prototype properties (mode=${mode}) - Date.hasOwnProperty`, - input: '{ date: Date.hasOwnProperty("") }', - options: { mode }, - expected: '', - }, - { - title: `should not allow calling functions that only exist as Object.prototype properties (mode=${mode}) - Date.__proto__`, - input: '{ date: Date.__proto__("") }', - options: { mode }, - expected: '', - }, - { - title: `should not allow calling functions that only exist as Object.prototype properties (mode=${mode}) - Code({ toString })`, - input: '{ date: Code({ toString: Date.constructor("throw null;") }) }', - options: { mode }, - expected: '', - }, - ], + (mode: ParseMode): ParseTestCase[] => { + const modeLabel: string = mode; + return [ + { + title: `should not allow calling functions that only exist as Object.prototype properties (mode=${modeLabel}) - Date.constructor`, + input: '{ date: Date.constructor("") }', + options: { mode }, + expected: '', + }, + { + title: `should not allow calling functions that only exist as Object.prototype properties (mode=${modeLabel}) - Date.hasOwnProperty`, + input: '{ date: Date.hasOwnProperty("") }', + options: { mode }, + expected: '', + }, + { + title: `should not allow calling functions that only exist as Object.prototype properties (mode=${modeLabel}) - Date.__proto__`, + input: '{ date: Date.__proto__("") }', + options: { mode }, + expected: '', + }, + { + title: `should not allow calling functions that only exist as Object.prototype properties (mode=${modeLabel}) - Code({ toString })`, + input: + '{ date: Code({ toString: Date.constructor("throw null;") }) }', + options: { mode }, + expected: '', + }, + ]; + }, ), { diff --git a/packages/shell-bson-parser/tsconfig.json b/packages/shell-bson-parser/tsconfig.json index 4e6d1b8b..0d6ceb80 100644 --- a/packages/shell-bson-parser/tsconfig.json +++ b/packages/shell-bson-parser/tsconfig.json @@ -1,3 +1,7 @@ { - "extends": "@mongodb-js/tsconfig-devtools/tsconfig.common.json" + "extends": "@mongodb-js/tsconfig-devtools/tsconfig.common.json", + "compilerOptions": { + "module": "nodenext", + "moduleResolution": "nodenext" + } } diff --git a/packages/shell-bson-parser/webpack.cjs.config.cjs b/packages/shell-bson-parser/webpack.cjs.config.cjs new file mode 100644 index 00000000..ca44bb9e --- /dev/null +++ b/packages/shell-bson-parser/webpack.cjs.config.cjs @@ -0,0 +1,25 @@ +'use strict'; +const path = require('path'); + +// Bundles the already tsc-compiled ESM dist/index.js into a standalone +// CommonJS entry point. The worker file stays same as in the ESM build. +module.exports = { + mode: 'production', + target: 'node', + entry: path.resolve(__dirname, 'dist', 'index.js'), + output: { + path: path.resolve(__dirname, 'dist', 'cjs'), + filename: 'index.js', + library: { + type: 'commonjs2', + }, + }, + externals: [/^acorn/, /^javascript-stringify/, /^web-worker/, /^bson/], + module: { + parser: { + javascript: { + importExportsPresence: 'warn', + }, + }, + }, +}; diff --git a/packages/shell-bson-parser/webpack.worker.config.cjs b/packages/shell-bson-parser/webpack.worker.config.cjs new file mode 100644 index 00000000..0f736a65 --- /dev/null +++ b/packages/shell-bson-parser/webpack.worker.config.cjs @@ -0,0 +1,21 @@ +'use strict'; +const path = require('path'); + +module.exports = { + mode: 'production', + target: 'webworker', + entry: path.resolve(__dirname, 'dist', 'worker.js'), + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'worker.js', + }, + module: { + parser: { + javascript: { + // `bson.Map` trips webpack's static named-export check + // Reducing error to warn + importExportsPresence: 'warn', + }, + }, + }, +};