diff --git a/package-lock.json b/package-lock.json index 0b5954f114c..be504844480 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5427,20 +5427,20 @@ } }, "node_modules/@leafygreen-ui/card": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/card/-/card-12.0.2.tgz", - "integrity": "sha512-4ffWqzDG3u4APPH4d8nWfdrE7xXedGOvbIAJK2JGhsZcSTLaUpxOF8MYTgYDzRLnRye3bhLA/Om7AUY28aGsMA==", + "version": "12.0.9", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/card/-/card-12.0.9.tgz", + "integrity": "sha512-Go3ys8cimZQ8yStVt/++/jiK4O/r5cRqYXyM8ZAEH16nVTgMaumtSjiEiCuF1jWO2WaNCf9fNU4A8WOi7WZvUw==", "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^4.0.9", - "@leafygreen-ui/lib": "^14.0.2", - "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/polymorphic": "^2.0.5", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "@leafygreen-ui/polymorphic": "^2.0.9", + "@leafygreen-ui/tokens": "^2.12.2", "polished": "^4.2.2" }, "peerDependencies": { - "@leafygreen-ui/leafygreen-provider": "^4.0.2" + "@leafygreen-ui/leafygreen-provider": "^4.0.7" } }, "node_modules/@leafygreen-ui/checkbox": { @@ -5572,9 +5572,9 @@ } }, "node_modules/@leafygreen-ui/emotion": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/emotion/-/emotion-4.0.9.tgz", - "integrity": "sha512-f9ffU9FbIRi5KeOGO1YfAScQs3tXHgx6230xix0dWkuiTmwg+TFY8xfNdjptIkFuyODCcjAjIQDqOx98lpGvwQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/emotion/-/emotion-4.1.1.tgz", + "integrity": "sha512-h9eVJTt5WXnDyKUPiJ6Qn8lhhagPd1biaUbPrCWJb4UF5VnIUf2eJwXPkDgRzc8fIKX26Qw9k+YJiFjmnqSOyQ==", "license": "Apache-2.0", "dependencies": { "@emotion/css": "^11.1.3", @@ -5625,22 +5625,22 @@ } }, "node_modules/@leafygreen-ui/hooks": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/hooks/-/hooks-8.3.4.tgz", - "integrity": "sha512-Qbhn4RRCQo/t5BkfCkN4uCYtKJlysI1yrLuaeftf6Mj3oB92e7pPJibzWCVwy5WwNK3vAqB9YV5IM+zDzjJZJQ==", + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/hooks/-/hooks-8.4.1.tgz", + "integrity": "sha512-WZ1p+HeYqqbWVDGTffkRLDE83K/GbjRDYW8jcSYgznba0NAkOkWT9n/+MJp83rd55iyPhBopOKx7270s/sIH4A==", "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^14.2.0", "lodash": "^4.17.21" } }, "node_modules/@leafygreen-ui/icon": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/icon/-/icon-13.1.2.tgz", - "integrity": "sha512-/TM731pMS8sVRfF8ciAkdZwCKExPNHJ3aDplhLRlnVuC66x+zDbxaiyAlkztFEGBtKeTug4TAMSVaOPeR54ZWg==", + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/icon/-/icon-13.3.0.tgz", + "integrity": "sha512-//vun0KJrtMAN6pTCmQGT3brTFEpSE2LbNnwlJ+l8klG6bwEmcPF9xPCS+XU98/b3UhMhtXHpwbzYN29UteAYg==", "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/emotion": "^4.1.1", "lodash": "^4.17.21" } }, @@ -5715,20 +5715,20 @@ } }, "node_modules/@leafygreen-ui/leafygreen-provider": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/leafygreen-provider/-/leafygreen-provider-4.0.2.tgz", - "integrity": "sha512-dZ9wYv8Sj/8yTDOvx6q6XQZLzDsz5eWoUlDTfGxwMS+hyIrReiPkqZdE87sBVongcKVwD8OBlPdr+XGeoeaLBw==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/leafygreen-provider/-/leafygreen-provider-4.0.7.tgz", + "integrity": "sha512-By2Ov+V/YP+pmNn9DwaayXCP31oq7NKO/CwEoqyjE1j58S2Ti6u1Eacywt2a18pIjKPbnIqralX8Bhmd6BAl5Q==", "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/hooks": "^8.3.4", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/hooks": "^8.4.1", + "@leafygreen-ui/lib": "^14.2.0", "react-transition-group": "^4.4.5" } }, "node_modules/@leafygreen-ui/lib": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-14.0.2.tgz", - "integrity": "sha512-ZmyBeY1wj6cg9Tam0TaH6LejJKx+5uobeyCtwwdoNdMdqXOxcPOYWmUKLTjXh3P/TXVjXgYIpLiANUHN7OtMbw==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-14.2.0.tgz", + "integrity": "sha512-JWHFwtWXY52YL1uNFpHWvRUWVl5tkXQzyq2uEMFHyZQKYUG0of9o5V+Zc6vAXdMvvAhE3DeYvDjTpaQbUk1PrQ==", "license": "Apache-2.0", "dependencies": { "lodash": "^4.17.21" @@ -5816,9 +5816,9 @@ } }, "node_modules/@leafygreen-ui/palette": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/palette/-/palette-4.1.3.tgz", - "integrity": "sha512-lg76FxP4ThMiDCzhJwdENcvtyQeLE/T+tExmkclXnuXME4mmo4AVoH+iKScHiztDGgQrF4Hg2rGLUJf5IiR2Iw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/palette/-/palette-4.1.4.tgz", + "integrity": "sha512-pkNeNzlstEM7ceoLG1mG7PUGunEdUYemKjzVOcaCbNEoyZvX3Lf0KWb8tDmAEloSPdXxMOlO8hoef9JGNdEIcw==", "license": "Apache-2.0" }, "node_modules/@leafygreen-ui/pipeline": { @@ -5841,12 +5841,12 @@ } }, "node_modules/@leafygreen-ui/polymorphic": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/polymorphic/-/polymorphic-2.0.5.tgz", - "integrity": "sha512-ajtFuGgydvsKjyft+4FvCptzgoMK+QKcbWs21zLidMZSuQjTq1pt3IC01lFuog05+vVXdvZwHSkEbPzQ4qp3/A==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/polymorphic/-/polymorphic-2.0.9.tgz", + "integrity": "sha512-oeAzARBPXZkZeStTuPdXDKdfyBlmkK5AiJUeehwbI5p6uTidH1GPGti+y1sDtxUPkavwEmGlPL304QoXXeHB6Q==", "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^14.2.0", "lodash": "^4.17.21" } }, @@ -6003,6 +6003,25 @@ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "license": "MIT" }, + "node_modules/@leafygreen-ui/skeleton-loader": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/skeleton-loader/-/skeleton-loader-2.0.11.tgz", + "integrity": "sha512-QDG5ppaMGT4cXnOEtmFfFYC3jNljIvHg9+5+tZas3gcbt6W6BV9v7OdyFX0ZsU5F+kDed5d2SsgipMp4bCgjJw==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/card": "^12.0.9", + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/icon": "^13.3.0", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "@leafygreen-ui/tokens": "^2.12.2", + "@leafygreen-ui/typography": "^20.1.8", + "lodash": "^4.17.21" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^4.0.7" + } + }, "node_modules/@leafygreen-ui/split-button": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@leafygreen-ui/split-button/-/split-button-4.1.5.tgz", @@ -6144,13 +6163,14 @@ } }, "node_modules/@leafygreen-ui/tokens": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-2.11.3.tgz", - "integrity": "sha512-IQJ0uJQldPNzedOCsoPKtwWsKPP9vtD2cCx/DC9HtLmdQbiuWXdcAw67psOjTBWCXYxRJo2Pd8RZ2LnAIrywug==", + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-2.12.2.tgz", + "integrity": "sha512-eVHQOk7lExNjGPVpLv2sGMUmAH0ZIpmu86NHe4n3RzHNQ2ziJUnw1CN94N6Y09qv00LvrZ2I05kbxLfX+kktvw==", "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/lib": "^14.0.2", - "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", "polished": "^4.2.2" } }, @@ -6176,20 +6196,20 @@ } }, "node_modules/@leafygreen-ui/typography": { - "version": "20.1.1", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/typography/-/typography-20.1.1.tgz", - "integrity": "sha512-kdGZaCfdb/UcvKg0yl0YAOYEw0DAY1O5ZCez88vpzxx64KlpivwaM++25Tu414jZFyL8TYG8OqobgH7LWVRxQQ==", + "version": "20.1.8", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/typography/-/typography-20.1.8.tgz", + "integrity": "sha512-roZz/Dopv/5A2TAvc5ysvi+s+R9SXkvPeIfJEcz9s9ZTi4RO5y7B8D81w3px0h1Fj8WnZDl8+bEPms4oak0EUw==", "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^4.0.9", - "@leafygreen-ui/icon": "^13.1.2", - "@leafygreen-ui/lib": "^14.0.2", - "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/polymorphic": "^2.0.5", - "@leafygreen-ui/tokens": "^2.11.3" + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/icon": "^13.3.0", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "@leafygreen-ui/polymorphic": "^2.0.9", + "@leafygreen-ui/tokens": "^2.12.2" }, "peerDependencies": { - "@leafygreen-ui/leafygreen-provider": "^4.0.2" + "@leafygreen-ui/leafygreen-provider": "^4.0.7" } }, "node_modules/@leichtgewicht/base64-codec": { @@ -23686,6 +23706,12 @@ "node": ">= 0.12" } }, + "node_modules/format-util": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/format-util/-/format-util-1.0.5.tgz", + "integrity": "sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg==", + "license": "MIT" + }, "node_modules/formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -31637,6 +31663,15 @@ "bson": "6.x" } }, + "node_modules/mongodb-mql-engines": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/mongodb-mql-engines/-/mongodb-mql-engines-0.0.4.tgz", + "integrity": "sha512-QPOSUnp4eeR4vVcF68VTsx1jutZbmNJYVFpbQ8loPPrynLJlxrtLr1PWV6UMunkHVLGMDJuGcsWQ/7JI5RzOcA==", + "license": "Apache-2.0", + "dependencies": { + "format-util": "^1.0.5" + } + }, "node_modules/mongodb-ns": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/mongodb-ns/-/mongodb-ns-2.4.2.tgz", @@ -43757,6 +43792,7 @@ "@leafygreen-ui/search-input": "^5.0.2", "@leafygreen-ui/segmented-control": "^10.0.2", "@leafygreen-ui/select": "^14.0.2", + "@leafygreen-ui/skeleton-loader": "^2.0.11", "@leafygreen-ui/split-button": "^4.1.5", "@leafygreen-ui/table": "^13.0.1", "@leafygreen-ui/tabs": "^14.0.2", @@ -45602,6 +45638,7 @@ "mongodb": "^6.14.1", "mongodb-collection-model": "^5.26.0", "mongodb-data-service": "^22.25.8", + "mongodb-mql-engines": "^0.0.4", "mongodb-ns": "^2.4.2", "mongodb-query-parser": "^4.3.0", "numeral": "^2.0.6", @@ -53979,15 +54016,15 @@ } }, "@leafygreen-ui/card": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/card/-/card-12.0.2.tgz", - "integrity": "sha512-4ffWqzDG3u4APPH4d8nWfdrE7xXedGOvbIAJK2JGhsZcSTLaUpxOF8MYTgYDzRLnRye3bhLA/Om7AUY28aGsMA==", - "requires": { - "@leafygreen-ui/emotion": "^4.0.9", - "@leafygreen-ui/lib": "^14.0.2", - "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/polymorphic": "^2.0.5", - "@leafygreen-ui/tokens": "^2.11.3", + "version": "12.0.9", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/card/-/card-12.0.9.tgz", + "integrity": "sha512-Go3ys8cimZQ8yStVt/++/jiK4O/r5cRqYXyM8ZAEH16nVTgMaumtSjiEiCuF1jWO2WaNCf9fNU4A8WOi7WZvUw==", + "requires": { + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "@leafygreen-ui/polymorphic": "^2.0.9", + "@leafygreen-ui/tokens": "^2.12.2", "polished": "^4.2.2" } }, @@ -54096,9 +54133,9 @@ } }, "@leafygreen-ui/emotion": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/emotion/-/emotion-4.0.9.tgz", - "integrity": "sha512-f9ffU9FbIRi5KeOGO1YfAScQs3tXHgx6230xix0dWkuiTmwg+TFY8xfNdjptIkFuyODCcjAjIQDqOx98lpGvwQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/emotion/-/emotion-4.1.1.tgz", + "integrity": "sha512-h9eVJTt5WXnDyKUPiJ6Qn8lhhagPd1biaUbPrCWJb4UF5VnIUf2eJwXPkDgRzc8fIKX26Qw9k+YJiFjmnqSOyQ==", "requires": { "@emotion/css": "^11.1.3", "@emotion/server": "^11.4.0" @@ -54140,20 +54177,20 @@ } }, "@leafygreen-ui/hooks": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/hooks/-/hooks-8.3.4.tgz", - "integrity": "sha512-Qbhn4RRCQo/t5BkfCkN4uCYtKJlysI1yrLuaeftf6Mj3oB92e7pPJibzWCVwy5WwNK3vAqB9YV5IM+zDzjJZJQ==", + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/hooks/-/hooks-8.4.1.tgz", + "integrity": "sha512-WZ1p+HeYqqbWVDGTffkRLDE83K/GbjRDYW8jcSYgznba0NAkOkWT9n/+MJp83rd55iyPhBopOKx7270s/sIH4A==", "requires": { - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^14.2.0", "lodash": "^4.17.21" } }, "@leafygreen-ui/icon": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/icon/-/icon-13.1.2.tgz", - "integrity": "sha512-/TM731pMS8sVRfF8ciAkdZwCKExPNHJ3aDplhLRlnVuC66x+zDbxaiyAlkztFEGBtKeTug4TAMSVaOPeR54ZWg==", + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/icon/-/icon-13.3.0.tgz", + "integrity": "sha512-//vun0KJrtMAN6pTCmQGT3brTFEpSE2LbNnwlJ+l8klG6bwEmcPF9xPCS+XU98/b3UhMhtXHpwbzYN29UteAYg==", "requires": { - "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/emotion": "^4.1.1", "lodash": "^4.17.21" } }, @@ -54212,19 +54249,19 @@ } }, "@leafygreen-ui/leafygreen-provider": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/leafygreen-provider/-/leafygreen-provider-4.0.2.tgz", - "integrity": "sha512-dZ9wYv8Sj/8yTDOvx6q6XQZLzDsz5eWoUlDTfGxwMS+hyIrReiPkqZdE87sBVongcKVwD8OBlPdr+XGeoeaLBw==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/leafygreen-provider/-/leafygreen-provider-4.0.7.tgz", + "integrity": "sha512-By2Ov+V/YP+pmNn9DwaayXCP31oq7NKO/CwEoqyjE1j58S2Ti6u1Eacywt2a18pIjKPbnIqralX8Bhmd6BAl5Q==", "requires": { - "@leafygreen-ui/hooks": "^8.3.4", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/hooks": "^8.4.1", + "@leafygreen-ui/lib": "^14.2.0", "react-transition-group": "^4.4.5" } }, "@leafygreen-ui/lib": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-14.0.2.tgz", - "integrity": "sha512-ZmyBeY1wj6cg9Tam0TaH6LejJKx+5uobeyCtwwdoNdMdqXOxcPOYWmUKLTjXh3P/TXVjXgYIpLiANUHN7OtMbw==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-14.2.0.tgz", + "integrity": "sha512-JWHFwtWXY52YL1uNFpHWvRUWVl5tkXQzyq2uEMFHyZQKYUG0of9o5V+Zc6vAXdMvvAhE3DeYvDjTpaQbUk1PrQ==", "requires": { "lodash": "^4.17.21" } @@ -54295,9 +54332,9 @@ } }, "@leafygreen-ui/palette": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/palette/-/palette-4.1.3.tgz", - "integrity": "sha512-lg76FxP4ThMiDCzhJwdENcvtyQeLE/T+tExmkclXnuXME4mmo4AVoH+iKScHiztDGgQrF4Hg2rGLUJf5IiR2Iw==" + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/palette/-/palette-4.1.4.tgz", + "integrity": "sha512-pkNeNzlstEM7ceoLG1mG7PUGunEdUYemKjzVOcaCbNEoyZvX3Lf0KWb8tDmAEloSPdXxMOlO8hoef9JGNdEIcw==" }, "@leafygreen-ui/pipeline": { "version": "7.0.2", @@ -54315,11 +54352,11 @@ } }, "@leafygreen-ui/polymorphic": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/polymorphic/-/polymorphic-2.0.5.tgz", - "integrity": "sha512-ajtFuGgydvsKjyft+4FvCptzgoMK+QKcbWs21zLidMZSuQjTq1pt3IC01lFuog05+vVXdvZwHSkEbPzQ4qp3/A==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/polymorphic/-/polymorphic-2.0.9.tgz", + "integrity": "sha512-oeAzARBPXZkZeStTuPdXDKdfyBlmkK5AiJUeehwbI5p6uTidH1GPGti+y1sDtxUPkavwEmGlPL304QoXXeHB6Q==", "requires": { - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^14.2.0", "lodash": "^4.17.21" } }, @@ -54448,6 +54485,21 @@ } } }, + "@leafygreen-ui/skeleton-loader": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/skeleton-loader/-/skeleton-loader-2.0.11.tgz", + "integrity": "sha512-QDG5ppaMGT4cXnOEtmFfFYC3jNljIvHg9+5+tZas3gcbt6W6BV9v7OdyFX0ZsU5F+kDed5d2SsgipMp4bCgjJw==", + "requires": { + "@leafygreen-ui/card": "^12.0.9", + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/icon": "^13.3.0", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "@leafygreen-ui/tokens": "^2.12.2", + "@leafygreen-ui/typography": "^20.1.8", + "lodash": "^4.17.21" + } + }, "@leafygreen-ui/split-button": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@leafygreen-ui/split-button/-/split-button-4.1.5.tgz", @@ -54563,12 +54615,13 @@ } }, "@leafygreen-ui/tokens": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-2.11.3.tgz", - "integrity": "sha512-IQJ0uJQldPNzedOCsoPKtwWsKPP9vtD2cCx/DC9HtLmdQbiuWXdcAw67psOjTBWCXYxRJo2Pd8RZ2LnAIrywug==", + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-2.12.2.tgz", + "integrity": "sha512-eVHQOk7lExNjGPVpLv2sGMUmAH0ZIpmu86NHe4n3RzHNQ2ziJUnw1CN94N6Y09qv00LvrZ2I05kbxLfX+kktvw==", "requires": { - "@leafygreen-ui/lib": "^14.0.2", - "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", "polished": "^4.2.2" } }, @@ -54590,16 +54643,16 @@ } }, "@leafygreen-ui/typography": { - "version": "20.1.1", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/typography/-/typography-20.1.1.tgz", - "integrity": "sha512-kdGZaCfdb/UcvKg0yl0YAOYEw0DAY1O5ZCez88vpzxx64KlpivwaM++25Tu414jZFyL8TYG8OqobgH7LWVRxQQ==", + "version": "20.1.8", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/typography/-/typography-20.1.8.tgz", + "integrity": "sha512-roZz/Dopv/5A2TAvc5ysvi+s+R9SXkvPeIfJEcz9s9ZTi4RO5y7B8D81w3px0h1Fj8WnZDl8+bEPms4oak0EUw==", "requires": { - "@leafygreen-ui/emotion": "^4.0.9", - "@leafygreen-ui/icon": "^13.1.2", - "@leafygreen-ui/lib": "^14.0.2", - "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/polymorphic": "^2.0.5", - "@leafygreen-ui/tokens": "^2.11.3" + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/icon": "^13.3.0", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "@leafygreen-ui/polymorphic": "^2.0.9", + "@leafygreen-ui/tokens": "^2.12.2" } }, "@leichtgewicht/base64-codec": { @@ -56221,6 +56274,7 @@ "@leafygreen-ui/search-input": "^5.0.2", "@leafygreen-ui/segmented-control": "^10.0.2", "@leafygreen-ui/select": "^14.0.2", + "@leafygreen-ui/skeleton-loader": "^2.0.11", "@leafygreen-ui/split-button": "^4.1.5", "@leafygreen-ui/table": "^13.0.1", "@leafygreen-ui/tabs": "^14.0.2", @@ -57450,6 +57504,7 @@ "mongodb": "^6.14.1", "mongodb-collection-model": "^5.26.0", "mongodb-data-service": "^22.25.8", + "mongodb-mql-engines": "^0.0.4", "mongodb-ns": "^2.4.2", "mongodb-query-parser": "^4.3.0", "numeral": "^2.0.6", @@ -72771,6 +72826,11 @@ "mime-types": "^2.1.12" } }, + "format-util": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/format-util/-/format-util-1.0.5.tgz", + "integrity": "sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg==" + }, "formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -80147,6 +80207,14 @@ "heap-js": "^2.3.0" } }, + "mongodb-mql-engines": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/mongodb-mql-engines/-/mongodb-mql-engines-0.0.4.tgz", + "integrity": "sha512-QPOSUnp4eeR4vVcF68VTsx1jutZbmNJYVFpbQ8loPPrynLJlxrtLr1PWV6UMunkHVLGMDJuGcsWQ/7JI5RzOcA==", + "requires": { + "format-util": "^1.0.5" + } + }, "mongodb-ns": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/mongodb-ns/-/mongodb-ns-2.4.2.tgz", diff --git a/packages/compass-components/package.json b/packages/compass-components/package.json index 9569841ade3..35898a445d2 100644 --- a/packages/compass-components/package.json +++ b/packages/compass-components/package.json @@ -63,6 +63,7 @@ "@leafygreen-ui/search-input": "^5.0.2", "@leafygreen-ui/segmented-control": "^10.0.2", "@leafygreen-ui/select": "^14.0.2", + "@leafygreen-ui/skeleton-loader": "^2.0.11", "@leafygreen-ui/split-button": "^4.1.5", "@leafygreen-ui/table": "^13.0.1", "@leafygreen-ui/tabs": "^14.0.2", diff --git a/packages/compass-components/src/index.ts b/packages/compass-components/src/index.ts index 194031fef0f..690fea17850 100644 --- a/packages/compass-components/src/index.ts +++ b/packages/compass-components/src/index.ts @@ -211,3 +211,4 @@ export { type ItemRenderer as VirtualListItemRenderer, } from './components/virtual-list'; export { SelectTable } from './components/select-table'; +export { ParagraphSkeleton } from '@leafygreen-ui/skeleton-loader'; diff --git a/packages/compass-indexes/package.json b/packages/compass-indexes/package.json index 014bf8d7340..c63e1814511 100644 --- a/packages/compass-indexes/package.json +++ b/packages/compass-indexes/package.json @@ -84,6 +84,7 @@ "mongodb": "^6.14.1", "mongodb-collection-model": "^5.26.0", "mongodb-data-service": "^22.25.8", + "mongodb-mql-engines": "^0.0.4", "mongodb-query-parser": "^4.3.0", "mongodb-ns": "^2.4.2", "numeral": "^2.0.6", diff --git a/packages/compass-indexes/src/components/create-index-form/mdb-code-viewer.tsx b/packages/compass-indexes/src/components/create-index-form/mdb-code-viewer.tsx index 44c4c9d1900..e3d7f46f913 100644 --- a/packages/compass-indexes/src/components/create-index-form/mdb-code-viewer.tsx +++ b/packages/compass-indexes/src/components/create-index-form/mdb-code-viewer.tsx @@ -24,7 +24,7 @@ const generateCode = ({ }: { dbName: string; collectionName: string; - indexNameTypeMap: { [key: string]: string }; + indexNameTypeMap: Record; }) => { let codeStr = `db.getSiblingDB("${dbName}").getCollection("${escapeText( collectionName @@ -32,7 +32,7 @@ const generateCode = ({ Object.entries(indexNameTypeMap).forEach(([name, type], index) => { // Replacing everything inside the parenthesis i.e. (asc) - let parsedType = escapeText(type.replace(/\(.*?\)/g, '')).trim(); + let parsedType = escapeText(`${type}`.replace(/\(.*?\)/g, '')).trim(); if (!NUMERIC_INDEX_TYPES.includes(Number(parsedType))) { parsedType = `"${parsedType}"`; } @@ -59,7 +59,7 @@ const MDBCodeViewer = ({ }: { dbName: string; collectionName: string; - indexNameTypeMap: { [key: string]: string }; + indexNameTypeMap: Record; dataTestId?: string; }) => { const GeneratedCode = generateCode({ diff --git a/packages/compass-indexes/src/components/create-index-form/query-flow-section.spec.tsx b/packages/compass-indexes/src/components/create-index-form/query-flow-section.spec.tsx index 5dddd31366e..2582dbd03d2 100644 --- a/packages/compass-indexes/src/components/create-index-form/query-flow-section.spec.tsx +++ b/packages/compass-indexes/src/components/create-index-form/query-flow-section.spec.tsx @@ -2,35 +2,83 @@ import React from 'react'; import { render, screen } from '@mongodb-js/testing-library-compass'; import QueryFlowSection from './query-flow-section'; import { expect } from 'chai'; +import { Provider } from 'react-redux'; +import { setupStore } from '../../../test/setup-store'; +import { ActionTypes } from '../../modules/create-index'; describe('QueryFlowSection', () => { + const store = setupStore(); + const dbName = 'fakeDBName'; + const collectionName = 'fakeCollectionName'; const renderComponent = () => { render( - + + + ); }; - it('renders the input query section with a code editor', () => { - renderComponent(); - const codeEditor = screen.getByTestId('query-flow-section-code-editor'); - expect(codeEditor).to.be.visible; + + describe('in the initial state', () => { + beforeEach(() => { + renderComponent(); + }); + it('renders the input query section with a code editor', () => { + const codeEditor = screen.getByTestId('query-flow-section-code-editor'); + expect(codeEditor).to.be.visible; + }); + + it('renders the "Show suggested index" button', () => { + const buttonElement = screen.getByText('Show suggested index'); + expect(buttonElement).to.be.visible; + }); + it('does not render the suggested index section with formatted index code', () => { + const codeElement = screen.queryByTestId( + 'query-flow-section-suggested-index' + ); + expect(codeElement).to.be.null; + }); }); - it('renders the "Show suggested index" button', () => { - renderComponent(); - const buttonElement = screen.getByText('Show suggested index'); - expect(buttonElement).to.be.visible; + describe('when fetching for index suggestions', () => { + beforeEach(() => { + renderComponent(); + + store.dispatch({ + type: ActionTypes.SuggestedIndexesRequested, + }); + }); + it('renders a loader for the code section', () => { + const loader = screen.getByTestId('query-flow-section-code-loader'); + expect(loader).to.be.visible; + }); }); - it('renders the suggested index section with formatted index code', () => { - renderComponent(); - const codeElement = screen.getByTestId( - 'query-flow-section-suggested-index' - ); - expect(codeElement).to.be.visible; + describe('when index suggestions is fetched', () => { + beforeEach(() => { + renderComponent(); + + store.dispatch({ + type: ActionTypes.SuggestedIndexesFetched, + sampleDocs: [], + indexSuggestions: { a: 1, b: 2 }, + fetchingSuggestionsError: null, + indexSuggestionsState: 'success', + }); + }); + + it('renders the suggested index section with formatted index code', () => { + const codeElement = screen.getByTestId( + 'query-flow-section-suggested-index' + ); + expect(codeElement).to.be.visible; + expect(codeElement).to.have.text( + `db.getSiblingDB("${dbName}").getCollection("${collectionName}").createIndex({ "a": 1, "b": "2"});` + ); + }); }); }); diff --git a/packages/compass-indexes/src/components/create-index-form/query-flow-section.tsx b/packages/compass-indexes/src/components/create-index-form/query-flow-section.tsx index 2beb70201ce..b9b973c3017 100644 --- a/packages/compass-indexes/src/components/create-index-form/query-flow-section.tsx +++ b/packages/compass-indexes/src/components/create-index-form/query-flow-section.tsx @@ -4,17 +4,24 @@ import { Body, cx, useFocusRing, + ParagraphSkeleton, } from '@mongodb-js/compass-components'; -import React, { useMemo } from 'react'; +import React, { useMemo, useCallback } from 'react'; import { css, spacing } from '@mongodb-js/compass-components'; import { CodemirrorMultilineEditor, createQueryAutocompleter, } from '@mongodb-js/compass-editor'; import MDBCodeViewer from './mdb-code-viewer'; +import type { RootState } from '../../modules'; +import { fetchIndexSuggestions } from '../../modules/create-index'; +import type { + IndexSuggestionState, + SuggestedIndexFetchedProps, +} from '../../modules/create-index'; +import { connect } from 'react-redux'; const inputQueryContainerStyles = css({ - marginBottom: spacing[600], display: 'flex', flexDirection: 'column', }); @@ -60,16 +67,34 @@ const codeEditorStyles = css({ }, }); +const indexSuggestionsLoaderStyles = css({ + marginBottom: spacing[600], + padding: spacing[600], + background: palette.gray.light3, + border: `1px solid ${palette.gray.light2}`, + borderRadius: editorContainerRadius, +}); + const QueryFlowSection = ({ schemaFields, serverVersion, dbName, collectionName, + onSuggestedIndexButtonClick, + indexSuggestions, + fetchingSuggestionsState, }: { schemaFields: { name: string; description?: string }[]; serverVersion: string; dbName: string; collectionName: string; + onSuggestedIndexButtonClick: ({ + dbName, + collectionName, + inputQuery, + }: SuggestedIndexFetchedProps) => Promise; + indexSuggestions: Record | null; + fetchingSuggestionsState: IndexSuggestionState; }) => { const [inputQuery, setInputQuery] = React.useState(''); const completer = useMemo( @@ -88,6 +113,18 @@ const QueryFlowSection = ({ radius: editorContainerRadius, }); + const handleSuggestedIndexButtonClick = useCallback(() => { + const sanitizedInputQuery = inputQuery.trim(); + + void onSuggestedIndexButtonClick({ + dbName, + collectionName, + inputQuery: sanitizedInputQuery, + }); + }, [inputQuery, dbName, collectionName, onSuggestedIndexButtonClick]); + + const isFetchingIndexSuggestions = fetchingSuggestionsState === 'fetching'; + return ( <> @@ -116,9 +153,7 @@ const QueryFlowSection = ({
- - Suggested Index - {' '} -
- {/* TODO in CLOUDP-311786, replace hardcoded values with actual data */} - + Suggested Index + + )} + + {isFetchingIndexSuggestions ? ( + -
+ ) : ( + indexSuggestions && ( + <> +
+ +
+ + ) + )} ); }; -export default QueryFlowSection; +const mapState = ({ createIndex }: RootState) => { + const { indexSuggestions, sampleDocs, fetchingSuggestionsState } = + createIndex; + return { + indexSuggestions, + sampleDocs, + fetchingSuggestionsState, + }; +}; + +const mapDispatch = { + onSuggestedIndexButtonClick: fetchIndexSuggestions, +}; + +export default connect(mapState, mapDispatch)(QueryFlowSection); diff --git a/packages/compass-indexes/src/modules/create-index.tsx b/packages/compass-indexes/src/modules/create-index.tsx index ad78e8da201..33c21cfbc6c 100644 --- a/packages/compass-indexes/src/modules/create-index.tsx +++ b/packages/compass-indexes/src/modules/create-index.tsx @@ -1,3 +1,4 @@ +import type { Document } from 'mongodb'; import { EJSON, ObjectId } from 'bson'; import type { CreateIndexesOptions, IndexDirection } from 'mongodb'; import { isCollationValid } from 'mongodb-query-parser'; @@ -8,6 +9,8 @@ import { isAction } from '../utils/is-action'; import type { IndexesThunkAction } from '.'; import type { RootState } from '.'; import { createRegularIndex } from './regular-indexes'; +import * as mql from 'mongodb-mql-engines'; +import _parseShellBSON, { ParseMode } from '@mongodb-js/shell-bson-parser'; export enum ActionTypes { FieldAdded = 'compass-indexes/create-index/fields/field-added', @@ -27,6 +30,9 @@ export enum ActionTypes { CreateIndexFormSubmitted = 'compass-indexes/create-index/create-index-form-submitted', TabUpdated = 'compass-indexes/create-index/tab-updated', + + SuggestedIndexesRequested = 'compass-indexes/create-index/suggested-indexes-requested', + SuggestedIndexesFetched = 'compass-indexes/create-index/suggested-indexes-fetched', } // fields @@ -274,7 +280,7 @@ const INITIAL_OPTIONS_STATE = Object.fromEntries( }) ) as Options; -// other +export type IndexSuggestionState = 'initial' | 'fetching' | 'success' | 'error'; export type State = { // A unique id assigned to the create index modal on open, will be used when @@ -296,6 +302,18 @@ export type State = { // current tab that user is on (Query Flow or Index Flow) currentTab: Tab; + + // state of the index suggestions + fetchingSuggestionsState: IndexSuggestionState; + + // error specific to fetching index suggestions + fetchingSuggestionsError: string | null; + + // index suggestions in a format such as {fieldName: 1} + indexSuggestions: Record | null; + + // sample documents used for getting index suggestions + sampleDocs: Array | null; }; export const INITIAL_STATE: State = { @@ -305,6 +323,10 @@ export const INITIAL_STATE: State = { fields: INITIAL_FIELDS_STATE, options: INITIAL_OPTIONS_STATE, currentTab: 'IndexFlow', + fetchingSuggestionsState: 'initial', + fetchingSuggestionsError: null, + indexSuggestions: null, + sampleDocs: null, }; function getInitialState(): State { @@ -337,6 +359,106 @@ export type CreateIndexSpec = { [key: string]: string | number; }; +type SuggestedIndexesRequestedAction = { + type: ActionTypes.SuggestedIndexesRequested; +}; + +export type SuggestedIndexFetchedAction = { + type: ActionTypes.SuggestedIndexesFetched; + sampleDocs: Array; + indexSuggestions: { [key: string]: number } | null; + fetchingSuggestionsError: string | null; + indexSuggestionsState: IndexSuggestionState; +}; + +export type SuggestedIndexFetchedProps = { + dbName: string; + collectionName: string; + inputQuery: string; +}; + +export const fetchIndexSuggestions = ({ + dbName, + collectionName, + inputQuery, +}: { + dbName: string; + collectionName: string; + inputQuery: string; +}): IndexesThunkAction< + Promise, + SuggestedIndexFetchedAction | SuggestedIndexesRequestedAction +> => { + return async (dispatch, getState, { dataService }) => { + dispatch({ + type: ActionTypes.SuggestedIndexesRequested, + }); + const namespace = `${dbName}.${collectionName}`; + + // Get sample documents from state if it's already there, otherwise fetch it + let sampleDocuments: Array | null = + getState().createIndex.sampleDocs || null; + + // If it's null, that means it has not been fetched before + if (sampleDocuments === null) { + try { + sampleDocuments = + (await dataService.sample(namespace, { size: 50 })) || []; + } catch (e) { + // Swallow the error because mql package still will work fine with empty sampleDocuments + sampleDocuments = []; + } + } + + // Analyze namespace and fetch suggestions + try { + const analyzedNamespace = mql.analyzeNamespace( + { database: dbName, collection: collectionName }, + sampleDocuments + ); + + const query = mql.parseQuery( + _parseShellBSON(inputQuery, { mode: ParseMode.Loose }), + analyzedNamespace + ); + const results = await mql.suggestIndex([query]); + const indexSuggestions = results?.index || null; + + // TODO in CLOUDP-311787: add info banner and update the current error banner to take in fetchingSuggestionsError as well + if (!indexSuggestions) { + dispatch({ + type: ActionTypes.SuggestedIndexesFetched, + sampleDocs: sampleDocuments, + indexSuggestions, + fetchingSuggestionsError: + 'No suggested index found. Please choose "Start with an Index" at the top to continue.', + indexSuggestionsState: 'error', + }); + return; + } + + dispatch({ + type: ActionTypes.SuggestedIndexesFetched, + sampleDocs: sampleDocuments, + indexSuggestions, + fetchingSuggestionsError: null, + indexSuggestionsState: 'success', + }); + } catch (e: unknown) { + dispatch({ + type: ActionTypes.SuggestedIndexesFetched, + sampleDocs: sampleDocuments, + indexSuggestions: null, + fetchingSuggestionsError: + e instanceof Error + ? 'Error parsing query. Please follow query structure. ' + e.message + : 'Error parsing query. Please follow query structure.', + indexSuggestionsState: 'error', + }); + } + }; +}; + function isEmptyValue(value: unknown) { if (value === '') { return true; @@ -629,6 +751,35 @@ const reducer: Reducer = (state = INITIAL_STATE, action) => { }; } + if ( + isAction( + action, + ActionTypes.SuggestedIndexesRequested + ) + ) { + return { + ...state, + fetchingSuggestionsState: 'fetching', + fetchingSuggestionsError: null, + indexSuggestions: null, + }; + } + + if ( + isAction( + action, + ActionTypes.SuggestedIndexesFetched + ) + ) { + return { + ...state, + fetchingSuggestionsState: action.indexSuggestionsState, + fetchingSuggestionsError: action.fetchingSuggestionsError, + indexSuggestions: action.indexSuggestions, + sampleDocs: action.sampleDocs, + }; + } + return state; }; diff --git a/packages/compass-indexes/src/stores/store.ts b/packages/compass-indexes/src/stores/store.ts index ad9cde454e7..61db381a116 100644 --- a/packages/compass-indexes/src/stores/store.ts +++ b/packages/compass-indexes/src/stores/store.ts @@ -44,6 +44,7 @@ export type IndexesDataServiceProps = | 'createSearchIndex' | 'updateSearchIndex' | 'dropSearchIndex' + | 'sample' // Required for collection model (fetching stats) | 'collectionStats' | 'collectionInfo' diff --git a/packages/compass-indexes/test/setup-store.ts b/packages/compass-indexes/test/setup-store.ts index 32ee2231143..1d935f40cd8 100644 --- a/packages/compass-indexes/test/setup-store.ts +++ b/packages/compass-indexes/test/setup-store.ts @@ -86,6 +86,10 @@ const NOOP_DATA_PROVIDER: IndexesDataService = { listCollections(databaseName, filter, options) { return Promise.resolve([]); }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + sample(namespace: string) { + return Promise.resolve([]); + }, }; class FakeInstance extends EventEmitter {