diff --git a/package-lock.json b/package-lock.json index da7c70ec737..6667b0d0ed3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5200,9 +5200,9 @@ "license": "MIT" }, "node_modules/@emotion/styled": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", - "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.3", @@ -8597,6 +8597,88 @@ "url": "https://opencollective.com/node-fetch" } }, + "node_modules/@mongodb-js/diagramming": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/diagramming/-/diagramming-1.2.0.tgz", + "integrity": "sha512-U8tSC21RDtwWstoPPuW27I/ZOqieOoemTObpiz/mKnOZsGeos0GOT4Fph2sXwFQocaQUp5jkwLWzksf8ZOwLcA==", + "license": "MIT", + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@leafygreen-ui/icon": "^13.2.0", + "@leafygreen-ui/leafygreen-provider": "^5.0.0", + "@leafygreen-ui/palette": "^5.0.0", + "@leafygreen-ui/tokens": "^3.0.0", + "@leafygreen-ui/typography": "^20.1.4", + "@xyflow/react": "^12.5.1", + "d3-path": "^3.1.0", + "elkjs": "^0.10.0", + "react": "17.0.2", + "react-dom": "17.0.2" + } + }, + "node_modules/@mongodb-js/diagramming/node_modules/@leafygreen-ui/emotion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/emotion/-/emotion-5.0.0.tgz", + "integrity": "sha512-MOfouBCmHuFa6UObhUl03CUFqXvD2PP+nI7CLk0ny8/UKOLgAX4N+JuuSX606u+Efxk4lI2m3FZiyCrfi6oeFQ==", + "license": "Apache-2.0", + "dependencies": { + "@emotion/css": "^11.1.3", + "@emotion/server": "^11.4.0" + } + }, + "node_modules/@mongodb-js/diagramming/node_modules/@leafygreen-ui/hooks": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/hooks/-/hooks-9.1.0.tgz", + "integrity": "sha512-2hesC3HLL8mETJMgZJfNMYjVraTGP4FAikUa3qpI1yi155xQBVlqrLCDzLEXCiUT4+agUTMaf/VHeyM7LTfwfg==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/lib": "^15.1.0", + "@leafygreen-ui/tokens": "^3.1.1", + "lodash": "^4.17.21" + } + }, + "node_modules/@mongodb-js/diagramming/node_modules/@leafygreen-ui/leafygreen-provider": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/leafygreen-provider/-/leafygreen-provider-5.0.1.tgz", + "integrity": "sha512-fWAAwgs0SOt5T0yrPZTnLOwdygoPd6TLKwb8TOz5pTgJUY9tU4sKvKEhiIS9vsk5sz0Dr8dMs4lgD2bvuLe6/g==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/hooks": "^9.1.0", + "@leafygreen-ui/lib": "^15.1.0", + "react-transition-group": "^4.4.5" + } + }, + "node_modules/@mongodb-js/diagramming/node_modules/@leafygreen-ui/lib": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-15.1.0.tgz", + "integrity": "sha512-pUSPxbFz7/8zDFJc6E4DCeDijD7HGBEMuhqzpP7t9WJEgQU2U+cDLvvB6C7rasYZEjBK9Sx76MTrAxrdq2dxnQ==", + "license": "Apache-2.0", + "dependencies": { + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0" + } + }, + "node_modules/@mongodb-js/diagramming/node_modules/@leafygreen-ui/palette": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/palette/-/palette-5.0.0.tgz", + "integrity": "sha512-RHQy165X7lKMlNU+2BkvGCNuo8fP3bS5NVOJ6thSKingoksYrz1a6SNAzuHDIkww+njf0GaKiXYT64og2Xm4Fw==", + "license": "Apache-2.0" + }, + "node_modules/@mongodb-js/diagramming/node_modules/@leafygreen-ui/tokens": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-3.1.1.tgz", + "integrity": "sha512-2uSNd0vnUGTh/MqJuYu1A8evGC86vNtF2Ds4ksYBcwRCiZxfp5HA1DXS5SHmgiKpYnFusXKq2NzKGMhBsmwt7Q==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/emotion": "^5.0.0", + "@leafygreen-ui/lib": "^15.1.0", + "@leafygreen-ui/palette": "^5.0.0", + "polished": "^4.2.2" + } + }, "node_modules/@mongodb-js/dl-center": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@mongodb-js/dl-center/-/dl-center-1.3.0.tgz", @@ -14857,12 +14939,12 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, "node_modules/@xyflow/react": { - "version": "12.7.0", - "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.7.0.tgz", - "integrity": "sha512-U6VMEbYjiCg1byHrR7S+b5ZdHTjgCFX4KpBc634G/WtEBUvBLoMQdlCD6uJHqodnOAxpt3+G2wiDeTmXAFJzgQ==", + "version": "12.8.1", + "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.8.1.tgz", + "integrity": "sha512-t5Rame4Gc/540VcOZd28yFe9Xd8lyjKUX+VTiyb1x4ykNXZH5zyDmsu+lj9je2O/jGBVb0pj1Vjcxrxyn+Xk2g==", "license": "MIT", "dependencies": { - "@xyflow/system": "0.0.62", + "@xyflow/system": "0.0.65", "classcat": "^5.0.3", "zustand": "^4.4.0" }, @@ -14872,9 +14954,9 @@ } }, "node_modules/@xyflow/system": { - "version": "0.0.62", - "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.62.tgz", - "integrity": "sha512-Z2ufbnvuYxIOCGyzE/8eX8TAEM8Lpzc/JafjD1Tzy6ZJs/E7KGVU17Q1F5WDHVW+dbztJAdyXMG0ejR9bwSUAA==", + "version": "0.0.65", + "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.65.tgz", + "integrity": "sha512-AliQPQeurQMoNlOdySnRoDQl9yDSA/1Lqi47Eo0m98lHcfrTdD9jK75H0tiGj+0qRC10SKNUXyMkT0KL0opg4g==", "license": "MIT", "dependencies": { "@types/d3-drag": "^3.0.7", @@ -16846,6 +16928,13 @@ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, + "node_modules/bmp-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", + "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==", + "dev": true, + "license": "MIT" + }, "node_modules/bn.js": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", @@ -25339,6 +25428,12 @@ "node": ">= 12" } }, + "node_modules/html-to-image": { + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.11.tgz", + "integrity": "sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==", + "license": "MIT" + }, "node_modules/html-tokenize": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-tokenize/-/html-tokenize-2.0.1.tgz", @@ -25700,6 +25795,13 @@ "url": "https://opencollective.com/postcss/" } }, + "node_modules/idb-keyval": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz", + "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/identity-obj-proxy": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", @@ -26741,6 +26843,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "dev": true, + "license": "MIT" + }, "node_modules/is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -33392,6 +33501,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "dev": true, + "license": "MIT", + "bin": { + "opencollective-postinstall": "index.js" + } + }, "node_modules/opener": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", @@ -37182,6 +37301,13 @@ "node": ">=4" } }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true, + "license": "MIT" + }, "node_modules/regenerator-transform": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", @@ -39837,6 +39963,78 @@ } } }, + "node_modules/tesseract.js": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tesseract.js/-/tesseract.js-6.0.1.tgz", + "integrity": "sha512-/sPvMvrCtgxnNRCjbTYbr7BRu0yfWDsMZQ2a/T5aN/L1t8wUQN6tTWv6p6FwzpoEBA0jrN2UD2SX4QQFRdoDbA==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "bmp-js": "^0.1.0", + "idb-keyval": "^6.2.0", + "is-url": "^1.2.4", + "node-fetch": "^2.6.9", + "opencollective-postinstall": "^2.0.3", + "regenerator-runtime": "^0.13.3", + "tesseract.js-core": "^6.0.0", + "wasm-feature-detect": "^1.2.11", + "zlibjs": "^0.3.1" + } + }, + "node_modules/tesseract.js-core": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-6.0.0.tgz", + "integrity": "sha512-1Qncm/9oKM7xgrQXZXNB+NRh19qiXGhxlrR8EwFbK5SaUbPZnS5OMtP/ghtqfd23hsr1ZvZbZjeuAGcMxd/ooA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tesseract.js/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/tesseract.js/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tesseract.js/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/tesseract.js/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -41272,6 +41470,13 @@ "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-1.0.0.tgz", "integrity": "sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg==" }, + "node_modules/wasm-feature-detect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz", + "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/watchpack": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", @@ -42901,6 +43106,16 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" }, + "node_modules/zlibjs": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz", + "integrity": "sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/zod": { "version": "3.23.8", "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", @@ -44234,15 +44449,18 @@ "@mongodb-js/compass-logging": "^1.7.4", "@mongodb-js/compass-telemetry": "^1.10.2", "@mongodb-js/compass-user-data": "^0.7.4", + "@mongodb-js/compass-utils": "^0.9.2", "@mongodb-js/compass-workspaces": "^0.44.0", - "@mongodb-js/diagramming": "^1.1.0", + "@mongodb-js/diagramming": "^1.2.0", "bson": "^6.10.3", "compass-preferences-model": "^2.43.0", + "html-to-image": "1.11.11", "lodash": "^4.17.21", "mongodb": "^6.14.1", "mongodb-ns": "^2.4.2", "mongodb-schema": "^12.6.2", "react": "^17.0.2", + "react-dom": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" @@ -44263,117 +44481,11 @@ "depcheck": "^1.4.1", "mocha": "^10.2.0", "nyc": "^15.1.0", - "react-dom": "^17.0.2", "sinon": "^17.0.1", "typescript": "^5.0.4", "xvfb-maybe": "^0.2.1" } }, - "packages/compass-data-modeling/node_modules/@leafygreen-ui/emotion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/emotion/-/emotion-5.0.0.tgz", - "integrity": "sha512-MOfouBCmHuFa6UObhUl03CUFqXvD2PP+nI7CLk0ny8/UKOLgAX4N+JuuSX606u+Efxk4lI2m3FZiyCrfi6oeFQ==", - "license": "Apache-2.0", - "dependencies": { - "@emotion/css": "^11.1.3", - "@emotion/server": "^11.4.0" - } - }, - "packages/compass-data-modeling/node_modules/@leafygreen-ui/hooks": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/hooks/-/hooks-9.0.0.tgz", - "integrity": "sha512-IdnrYIg/5BW/zGIiDjT4fEHtfsoadzP8J8IUKC1FytX7b03fVCtY0zwAcfZh0B/+CkYcXFkSiBvTUpP7LLHLFg==", - "license": "Apache-2.0", - "dependencies": { - "@leafygreen-ui/lib": "^15.0.0", - "lodash": "^4.17.21" - } - }, - "packages/compass-data-modeling/node_modules/@leafygreen-ui/hooks/node_modules/@leafygreen-ui/lib": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-15.0.0.tgz", - "integrity": "sha512-FTUwi67diYnVJsUY9+mcJE9HlPEGTXq5eu6y6daOS1n/qY51sVOhVh8ZNfWuGO0Ztgn6mJJWK/a8I/MwHfoQ7Q==", - "license": "Apache-2.0", - "dependencies": { - "lodash": "^4.17.21" - }, - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0" - } - }, - "packages/compass-data-modeling/node_modules/@leafygreen-ui/leafygreen-provider": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/leafygreen-provider/-/leafygreen-provider-5.0.0.tgz", - "integrity": "sha512-FOsoX+o4C+D7vZpaRokM5j7RJ2U+c/Z0ZS11T1RIaWGJFPQOA9cobD4kJKXHWaiyUnjhA951W5+umSJT2mvDrg==", - "license": "Apache-2.0", - "dependencies": { - "@leafygreen-ui/hooks": "^9.0.0", - "@leafygreen-ui/lib": "^15.0.0", - "react-transition-group": "^4.4.5" - } - }, - "packages/compass-data-modeling/node_modules/@leafygreen-ui/leafygreen-provider/node_modules/@leafygreen-ui/lib": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-15.0.0.tgz", - "integrity": "sha512-FTUwi67diYnVJsUY9+mcJE9HlPEGTXq5eu6y6daOS1n/qY51sVOhVh8ZNfWuGO0Ztgn6mJJWK/a8I/MwHfoQ7Q==", - "license": "Apache-2.0", - "dependencies": { - "lodash": "^4.17.21" - }, - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0" - } - }, - "packages/compass-data-modeling/node_modules/@leafygreen-ui/palette": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/palette/-/palette-5.0.0.tgz", - "integrity": "sha512-RHQy165X7lKMlNU+2BkvGCNuo8fP3bS5NVOJ6thSKingoksYrz1a6SNAzuHDIkww+njf0GaKiXYT64og2Xm4Fw==", - "license": "Apache-2.0" - }, - "packages/compass-data-modeling/node_modules/@leafygreen-ui/tokens": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-3.1.0.tgz", - "integrity": "sha512-FctYPmcFYoeLi8zOv7o7MiCCkNxeT7KQ7U4ZT150rYKdIIls8k68vzkePsPtFSTufJ++dSlm7Xbgegl1yAsKfQ==", - "license": "Apache-2.0", - "dependencies": { - "@leafygreen-ui/emotion": "^5.0.0", - "@leafygreen-ui/lib": "^15.0.0", - "@leafygreen-ui/palette": "^5.0.0", - "polished": "^4.2.2" - } - }, - "packages/compass-data-modeling/node_modules/@leafygreen-ui/tokens/node_modules/@leafygreen-ui/lib": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-15.0.0.tgz", - "integrity": "sha512-FTUwi67diYnVJsUY9+mcJE9HlPEGTXq5eu6y6daOS1n/qY51sVOhVh8ZNfWuGO0Ztgn6mJJWK/a8I/MwHfoQ7Q==", - "license": "Apache-2.0", - "dependencies": { - "lodash": "^4.17.21" - }, - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0" - } - }, - "packages/compass-data-modeling/node_modules/@mongodb-js/diagramming": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/diagramming/-/diagramming-1.1.0.tgz", - "integrity": "sha512-HRv2L7xC8i7sMweUu1ZoBRRKde45gKFyPMFxHpuanjf44qlNVkHM2ExrIi0VNb82pZHxE85gSe7yT13AQfiFrQ==", - "license": "MIT", - "dependencies": { - "@emotion/react": "^11.14.0", - "@emotion/styled": "^11.14.0", - "@leafygreen-ui/icon": "^13.2.0", - "@leafygreen-ui/leafygreen-provider": "^5.0.0", - "@leafygreen-ui/palette": "^5.0.0", - "@leafygreen-ui/tokens": "^3.0.0", - "@leafygreen-ui/typography": "^20.1.4", - "@xyflow/react": "^12.5.1", - "d3-path": "^3.1.0", - "elkjs": "^0.10.0", - "react": "17.0.2", - "react-dom": "17.0.2" - } - }, "packages/compass-data-modeling/node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", @@ -44539,6 +44651,7 @@ "puppeteer-core": "^23.10.3", "resolve-mongodb-srv": "^1.1.5", "semver": "^7.6.3", + "tesseract.js": "^6.0.1", "tree-kill": "^1.2.2", "ts-node": "^10.9.1", "webdriverio": "^9.4.1", @@ -53436,9 +53549,9 @@ "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" }, "@emotion/styled": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", - "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", "requires": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -56783,8 +56896,9 @@ "@mongodb-js/compass-logging": "^1.7.4", "@mongodb-js/compass-telemetry": "^1.10.2", "@mongodb-js/compass-user-data": "^0.7.4", + "@mongodb-js/compass-utils": "^0.9.2", "@mongodb-js/compass-workspaces": "^0.44.0", - "@mongodb-js/diagramming": "^1.1.0", + "@mongodb-js/diagramming": "^1.2.0", "@mongodb-js/eslint-config-compass": "^1.4.0", "@mongodb-js/mocha-config-compass": "^1.6.8", "@mongodb-js/prettier-config-compass": "^1.2.8", @@ -56800,6 +56914,7 @@ "chai": "^4.3.6", "compass-preferences-model": "^2.43.0", "depcheck": "^1.4.1", + "html-to-image": "1.11.11", "lodash": "^4.17.21", "mocha": "^10.2.0", "mongodb": "^6.14.1", @@ -56816,99 +56931,6 @@ "xvfb-maybe": "^0.2.1" }, "dependencies": { - "@leafygreen-ui/emotion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/emotion/-/emotion-5.0.0.tgz", - "integrity": "sha512-MOfouBCmHuFa6UObhUl03CUFqXvD2PP+nI7CLk0ny8/UKOLgAX4N+JuuSX606u+Efxk4lI2m3FZiyCrfi6oeFQ==", - "requires": { - "@emotion/css": "^11.1.3", - "@emotion/server": "^11.4.0" - } - }, - "@leafygreen-ui/hooks": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/hooks/-/hooks-9.0.0.tgz", - "integrity": "sha512-IdnrYIg/5BW/zGIiDjT4fEHtfsoadzP8J8IUKC1FytX7b03fVCtY0zwAcfZh0B/+CkYcXFkSiBvTUpP7LLHLFg==", - "requires": { - "@leafygreen-ui/lib": "^15.0.0", - "lodash": "^4.17.21" - }, - "dependencies": { - "@leafygreen-ui/lib": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-15.0.0.tgz", - "integrity": "sha512-FTUwi67diYnVJsUY9+mcJE9HlPEGTXq5eu6y6daOS1n/qY51sVOhVh8ZNfWuGO0Ztgn6mJJWK/a8I/MwHfoQ7Q==", - "requires": { - "lodash": "^4.17.21" - } - } - } - }, - "@leafygreen-ui/leafygreen-provider": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/leafygreen-provider/-/leafygreen-provider-5.0.0.tgz", - "integrity": "sha512-FOsoX+o4C+D7vZpaRokM5j7RJ2U+c/Z0ZS11T1RIaWGJFPQOA9cobD4kJKXHWaiyUnjhA951W5+umSJT2mvDrg==", - "requires": { - "@leafygreen-ui/hooks": "^9.0.0", - "@leafygreen-ui/lib": "^15.0.0", - "react-transition-group": "^4.4.5" - }, - "dependencies": { - "@leafygreen-ui/lib": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-15.0.0.tgz", - "integrity": "sha512-FTUwi67diYnVJsUY9+mcJE9HlPEGTXq5eu6y6daOS1n/qY51sVOhVh8ZNfWuGO0Ztgn6mJJWK/a8I/MwHfoQ7Q==", - "requires": { - "lodash": "^4.17.21" - } - } - } - }, - "@leafygreen-ui/palette": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/palette/-/palette-5.0.0.tgz", - "integrity": "sha512-RHQy165X7lKMlNU+2BkvGCNuo8fP3bS5NVOJ6thSKingoksYrz1a6SNAzuHDIkww+njf0GaKiXYT64og2Xm4Fw==" - }, - "@leafygreen-ui/tokens": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-3.1.0.tgz", - "integrity": "sha512-FctYPmcFYoeLi8zOv7o7MiCCkNxeT7KQ7U4ZT150rYKdIIls8k68vzkePsPtFSTufJ++dSlm7Xbgegl1yAsKfQ==", - "requires": { - "@leafygreen-ui/emotion": "^5.0.0", - "@leafygreen-ui/lib": "^15.0.0", - "@leafygreen-ui/palette": "^5.0.0", - "polished": "^4.2.2" - }, - "dependencies": { - "@leafygreen-ui/lib": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-15.0.0.tgz", - "integrity": "sha512-FTUwi67diYnVJsUY9+mcJE9HlPEGTXq5eu6y6daOS1n/qY51sVOhVh8ZNfWuGO0Ztgn6mJJWK/a8I/MwHfoQ7Q==", - "requires": { - "lodash": "^4.17.21" - } - } - } - }, - "@mongodb-js/diagramming": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/diagramming/-/diagramming-1.1.0.tgz", - "integrity": "sha512-HRv2L7xC8i7sMweUu1ZoBRRKde45gKFyPMFxHpuanjf44qlNVkHM2ExrIi0VNb82pZHxE85gSe7yT13AQfiFrQ==", - "requires": { - "@emotion/react": "^11.14.0", - "@emotion/styled": "^11.14.0", - "@leafygreen-ui/icon": "^13.2.0", - "@leafygreen-ui/leafygreen-provider": "^5.0.0", - "@leafygreen-ui/palette": "^5.0.0", - "@leafygreen-ui/tokens": "^3.0.0", - "@leafygreen-ui/typography": "^20.1.4", - "@xyflow/react": "^12.5.1", - "d3-path": "^3.1.0", - "elkjs": "^0.10.0", - "react": "17.0.2", - "react-dom": "17.0.2" - } - }, "@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", @@ -59964,6 +59986,80 @@ } } }, + "@mongodb-js/diagramming": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/diagramming/-/diagramming-1.2.0.tgz", + "integrity": "sha512-U8tSC21RDtwWstoPPuW27I/ZOqieOoemTObpiz/mKnOZsGeos0GOT4Fph2sXwFQocaQUp5jkwLWzksf8ZOwLcA==", + "requires": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@leafygreen-ui/icon": "^13.2.0", + "@leafygreen-ui/leafygreen-provider": "^5.0.0", + "@leafygreen-ui/palette": "^5.0.0", + "@leafygreen-ui/tokens": "^3.0.0", + "@leafygreen-ui/typography": "^20.1.4", + "@xyflow/react": "^12.5.1", + "d3-path": "^3.1.0", + "elkjs": "^0.10.0", + "react": "^17.0.2", + "react-dom": "^17.0.2" + }, + "dependencies": { + "@leafygreen-ui/emotion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/emotion/-/emotion-5.0.0.tgz", + "integrity": "sha512-MOfouBCmHuFa6UObhUl03CUFqXvD2PP+nI7CLk0ny8/UKOLgAX4N+JuuSX606u+Efxk4lI2m3FZiyCrfi6oeFQ==", + "requires": { + "@emotion/css": "^11.1.3", + "@emotion/server": "^11.4.0" + } + }, + "@leafygreen-ui/hooks": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/hooks/-/hooks-9.1.0.tgz", + "integrity": "sha512-2hesC3HLL8mETJMgZJfNMYjVraTGP4FAikUa3qpI1yi155xQBVlqrLCDzLEXCiUT4+agUTMaf/VHeyM7LTfwfg==", + "requires": { + "@leafygreen-ui/lib": "^15.1.0", + "@leafygreen-ui/tokens": "^3.1.1", + "lodash": "^4.17.21" + } + }, + "@leafygreen-ui/leafygreen-provider": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/leafygreen-provider/-/leafygreen-provider-5.0.1.tgz", + "integrity": "sha512-fWAAwgs0SOt5T0yrPZTnLOwdygoPd6TLKwb8TOz5pTgJUY9tU4sKvKEhiIS9vsk5sz0Dr8dMs4lgD2bvuLe6/g==", + "requires": { + "@leafygreen-ui/hooks": "^9.1.0", + "@leafygreen-ui/lib": "^15.1.0", + "react-transition-group": "^4.4.5" + } + }, + "@leafygreen-ui/lib": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-15.1.0.tgz", + "integrity": "sha512-pUSPxbFz7/8zDFJc6E4DCeDijD7HGBEMuhqzpP7t9WJEgQU2U+cDLvvB6C7rasYZEjBK9Sx76MTrAxrdq2dxnQ==", + "requires": { + "lodash": "^4.17.21" + } + }, + "@leafygreen-ui/palette": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/palette/-/palette-5.0.0.tgz", + "integrity": "sha512-RHQy165X7lKMlNU+2BkvGCNuo8fP3bS5NVOJ6thSKingoksYrz1a6SNAzuHDIkww+njf0GaKiXYT64og2Xm4Fw==" + }, + "@leafygreen-ui/tokens": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-3.1.1.tgz", + "integrity": "sha512-2uSNd0vnUGTh/MqJuYu1A8evGC86vNtF2Ds4ksYBcwRCiZxfp5HA1DXS5SHmgiKpYnFusXKq2NzKGMhBsmwt7Q==", + "requires": { + "@leafygreen-ui/emotion": "^5.0.0", + "@leafygreen-ui/lib": "^15.1.0", + "@leafygreen-ui/palette": "^5.0.0", + "polished": "^4.2.2" + } + } + } + }, "@mongodb-js/dl-center": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@mongodb-js/dl-center/-/dl-center-1.3.0.tgz", @@ -65572,19 +65668,19 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, "@xyflow/react": { - "version": "12.7.0", - "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.7.0.tgz", - "integrity": "sha512-U6VMEbYjiCg1byHrR7S+b5ZdHTjgCFX4KpBc634G/WtEBUvBLoMQdlCD6uJHqodnOAxpt3+G2wiDeTmXAFJzgQ==", + "version": "12.8.1", + "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.8.1.tgz", + "integrity": "sha512-t5Rame4Gc/540VcOZd28yFe9Xd8lyjKUX+VTiyb1x4ykNXZH5zyDmsu+lj9je2O/jGBVb0pj1Vjcxrxyn+Xk2g==", "requires": { - "@xyflow/system": "0.0.62", + "@xyflow/system": "0.0.65", "classcat": "^5.0.3", "zustand": "^4.4.0" } }, "@xyflow/system": { - "version": "0.0.62", - "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.62.tgz", - "integrity": "sha512-Z2ufbnvuYxIOCGyzE/8eX8TAEM8Lpzc/JafjD1Tzy6ZJs/E7KGVU17Q1F5WDHVW+dbztJAdyXMG0ejR9bwSUAA==", + "version": "0.0.65", + "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.65.tgz", + "integrity": "sha512-AliQPQeurQMoNlOdySnRoDQl9yDSA/1Lqi47Eo0m98lHcfrTdD9jK75H0tiGj+0qRC10SKNUXyMkT0KL0opg4g==", "requires": { "@types/d3-drag": "^3.0.7", "@types/d3-interpolate": "^3.0.4", @@ -67077,6 +67173,12 @@ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, + "bmp-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", + "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==", + "dev": true + }, "bn.js": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", @@ -68103,6 +68205,7 @@ "puppeteer-core": "^23.10.3", "resolve-mongodb-srv": "^1.1.5", "semver": "^7.6.3", + "tesseract.js": "^6.0.1", "tree-kill": "^1.2.2", "ts-node": "^10.9.1", "webdriverio": "^9.4.1", @@ -74670,6 +74773,11 @@ } } }, + "html-to-image": { + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.11.tgz", + "integrity": "sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==" + }, "html-tokenize": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-tokenize/-/html-tokenize-2.0.1.tgz", @@ -74924,6 +75032,12 @@ } } }, + "idb-keyval": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz", + "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==", + "dev": true + }, "identity-obj-proxy": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", @@ -75628,6 +75742,12 @@ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==" }, + "is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "dev": true + }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -81167,6 +81287,12 @@ "is-wsl": "^2.2.0" } }, + "opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "dev": true + }, "opener": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", @@ -84047,6 +84173,12 @@ "regenerate": "^1.4.2" } }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true + }, "regenerator-transform": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", @@ -86088,6 +86220,62 @@ "terser": "^5.26.0" } }, + "tesseract.js": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tesseract.js/-/tesseract.js-6.0.1.tgz", + "integrity": "sha512-/sPvMvrCtgxnNRCjbTYbr7BRu0yfWDsMZQ2a/T5aN/L1t8wUQN6tTWv6p6FwzpoEBA0jrN2UD2SX4QQFRdoDbA==", + "dev": true, + "requires": { + "bmp-js": "^0.1.0", + "idb-keyval": "^6.2.0", + "is-url": "^1.2.4", + "node-fetch": "^2.6.9", + "opencollective-postinstall": "^2.0.3", + "regenerator-runtime": "^0.13.3", + "tesseract.js-core": "^6.0.0", + "wasm-feature-detect": "^1.2.11", + "zlibjs": "^0.3.1" + }, + "dependencies": { + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, + "tesseract.js-core": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-6.0.0.tgz", + "integrity": "sha512-1Qncm/9oKM7xgrQXZXNB+NRh19qiXGhxlrR8EwFbK5SaUbPZnS5OMtP/ghtqfd23hsr1ZvZbZjeuAGcMxd/ooA==", + "dev": true + }, "test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -87169,6 +87357,12 @@ "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-1.0.0.tgz", "integrity": "sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg==" }, + "wasm-feature-detect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz", + "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==", + "dev": true + }, "watchpack": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", @@ -88329,6 +88523,12 @@ } } }, + "zlibjs": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz", + "integrity": "sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==", + "dev": true + }, "zod": { "version": "3.23.8", "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", diff --git a/packages/compass-data-modeling/package.json b/packages/compass-data-modeling/package.json index 41c5efd2d2f..3e27044ca2c 100644 --- a/packages/compass-data-modeling/package.json +++ b/packages/compass-data-modeling/package.json @@ -61,15 +61,18 @@ "@mongodb-js/compass-logging": "^1.7.4", "@mongodb-js/compass-telemetry": "^1.10.2", "@mongodb-js/compass-user-data": "^0.7.4", + "@mongodb-js/compass-utils": "^0.9.2", "@mongodb-js/compass-workspaces": "^0.44.0", - "@mongodb-js/diagramming": "^1.1.0", + "@mongodb-js/diagramming": "^1.2.0", "bson": "^6.10.3", "compass-preferences-model": "^2.43.0", + "html-to-image": "1.11.11", "lodash": "^4.17.21", "mongodb": "^6.14.1", "mongodb-ns": "^2.4.2", "mongodb-schema": "^12.6.2", "react": "^17.0.2", + "react-dom": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" @@ -90,7 +93,6 @@ "depcheck": "^1.4.1", "mocha": "^10.2.0", "nyc": "^15.1.0", - "react-dom": "^17.0.2", "sinon": "^17.0.1", "typescript": "^5.0.4", "xvfb-maybe": "^0.2.1" diff --git a/packages/compass-data-modeling/src/components/export-diagram-modal.tsx b/packages/compass-data-modeling/src/components/export-diagram-modal.tsx index 91bde0a96a7..fee01ecb8e6 100644 --- a/packages/compass-data-modeling/src/components/export-diagram-modal.tsx +++ b/packages/compass-data-modeling/src/components/export-diagram-modal.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { Button, css, @@ -12,6 +12,8 @@ import { Radio, RadioGroup, spacing, + SpinLoader, + openToast, } from '@mongodb-js/compass-components'; import { closeExportModal, @@ -21,7 +23,9 @@ import { import { connect } from 'react-redux'; import type { DataModelingState } from '../store/reducer'; import type { StaticModel } from '../services/data-model-storage'; -import { exportToJson } from '../services/export-diagram'; +import { exportToJson, exportToPng } from '../services/export-diagram'; +import { useDiagram } from '@mongodb-js/diagramming'; +import { isCancelError } from '@mongodb-js/compass-utils'; const nbsp = '\u00a0'; @@ -59,20 +63,68 @@ const ExportDiagramModal = ({ model, onCloseClick, }: ExportDiagramModalProps) => { - const [exportFormat, setExportFormat] = useState<'json' | null>(null); - - const onExport = useCallback(() => { - if (!exportFormat || !model) { - return; + const [exportFormat, setExportFormat] = useState<'png' | 'json' | null>(null); + const diagram = useDiagram(); + const [isExporting, setIsExporting] = useState(false); + const abortControllerRef = useRef(null); + useEffect(() => { + const cleanup = () => { + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + abortControllerRef.current = null; + } + }; + const abortController = new AbortController(); + if (isModalOpen) { + abortControllerRef.current = abortController; + } else { + cleanup(); } - exportToJson(diagramLabel, model); + return cleanup; + }, [isModalOpen]); + + const onClose = useCallback(() => { + setIsExporting(false); + abortControllerRef.current?.abort(); + abortControllerRef.current = null; onCloseClick(); - }, [exportFormat, onCloseClick, model, diagramLabel]); + }, [onCloseClick]); + + const onExport = useCallback(async () => { + try { + if (!exportFormat || !model) { + return; + } + setIsExporting(true); + if (exportFormat === 'json') { + exportToJson(diagramLabel, model); + } else if (exportFormat === 'png') { + await exportToPng( + diagramLabel, + diagram, + abortControllerRef.current?.signal + ); + } + } catch (error) { + if (isCancelError(error)) { + return; + } + openToast('export-diagram-error', { + variant: 'warning', + title: 'Export failed', + description: `An error occurred while exporting the diagram: ${ + (error as Error).message + }`, + }); + } finally { + onClose(); + } + }, [exportFormat, onClose, model, diagram, diagramLabel]); return ( +
+ + setExportFormat('png')} + > + PNG + +
void onExport()} data-testid="export-button" + disabled={!exportFormat || !model} + loadingIndicator={} + isLoading={isExporting} > Export - diff --git a/packages/compass-data-modeling/src/services/export-diagram.ts b/packages/compass-data-modeling/src/services/export-diagram.ts deleted file mode 100644 index 53eb166aa69..00000000000 --- a/packages/compass-data-modeling/src/services/export-diagram.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { StaticModel } from './data-model-storage'; - -export function exportToJson(fileName: string, model: StaticModel) { - const json = getExportJsonFromModel(model); - const blob = new Blob([JSON.stringify(json, null, 2)], { - type: 'application/json', - }); - const url = window.URL.createObjectURL(blob); - const link = document.createElement('a'); - link.href = url; - link.download = fileName; - link.click(); - setTimeout(() => { - window.URL.revokeObjectURL(url); - link.remove(); - }, 0); -} - -export function getExportJsonFromModel({ - collections, - relationships, -}: StaticModel) { - return { - collections: Object.fromEntries( - collections.map((collection) => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { ns, jsonSchema, ...ignoredProps } = collection; - return [ns, { ns, jsonSchema }]; - }) - ), - relationships, - }; -} diff --git a/packages/compass-data-modeling/src/services/export-diagram.tsx b/packages/compass-data-modeling/src/services/export-diagram.tsx new file mode 100644 index 00000000000..0c34afdcc80 --- /dev/null +++ b/packages/compass-data-modeling/src/services/export-diagram.tsx @@ -0,0 +1,168 @@ +import React from 'react'; +import { + getNodesBounds, + getViewportForBounds, + DiagramProvider, + Diagram, +} from '@mongodb-js/diagramming'; +import type { DiagramInstance } from '@mongodb-js/diagramming'; +import type { StaticModel } from './data-model-storage'; +import ReactDOM from 'react-dom'; +import { toPng } from 'html-to-image'; +import { rafraf, spacing } from '@mongodb-js/compass-components'; +import { raceWithAbort } from '@mongodb-js/compass-utils'; + +function moveSvgDefsToViewportElement( + container: Element, + targetElement: Element +) { + const markerDef = container.querySelector('svg defs'); + if (!markerDef) { + return; + } + const diagramSvgElements = targetElement.querySelectorAll('svg'); + diagramSvgElements.forEach((svg) => { + const pathsWithMarkers = svg.querySelectorAll( + 'path[marker-end], path[marker-start]' + ); + if (pathsWithMarkers.length !== 0) { + const clonedDef = markerDef.cloneNode(true); + svg.insertBefore(clonedDef, svg.firstChild); + } + }); + markerDef.remove(); +} + +export async function exportToPng( + fileName: string, + diagram: DiagramInstance, + signal?: AbortSignal +) { + const dataUri = await raceWithAbort( + getExportPngDataUri(diagram), + signal ?? new AbortController().signal + ); + downloadFile(dataUri, fileName); +} + +export function getExportPngDataUri(diagram: DiagramInstance): Promise { + return new Promise((resolve, _reject) => { + const bounds = getNodesBounds(diagram.getNodes()); + + const container = document.createElement('div'); + container.setAttribute('data-testid', 'export-diagram-container'); + // Push it out of the viewport + container.style.position = 'fixed'; + container.style.top = '100vh'; + container.style.left = '100vw'; + container.style.width = `${bounds.width}px`; + container.style.height = `${bounds.height}px`; + document.body.appendChild(container); + + const edges = diagram.getEdges(); + const nodes = diagram.getNodes().map((node) => ({ + ...node, + selected: false, // Dont show selected state (blue border) + })); + + const reject = (error: Error) => { + document.body.removeChild(container); + _reject(error); + }; + + ReactDOM.render( + + + , + container, + () => { + rafraf(() => { + // For export we are selecting react-flow__viewport element, + // which contains the export canvas. It excludes diagram + // title, minmap, and other UI elements. However, it also + // excludes the svg defs that are currently outside of this element. + // So, when exporting, we need to include those defs as well so that + // edge markers are exported correctly. + const viewportElement = container.querySelector( + '.react-flow__viewport' + ); + if (!viewportElement) { + return reject(new Error('Diagram element not found')); + } + + const transform = getViewportForBounds( + bounds, + bounds.width, + bounds.height, + 0.5, // Minimum zoom + 2, // Maximum zoom + `${spacing[400]}px` // 16px padding + ); + + // Moving svg defs to the viewport element + moveSvgDefsToViewportElement(container, viewportElement); + rafraf(() => { + toPng(viewportElement as HTMLElement, { + backgroundColor: '#fff', + pixelRatio: 2, + width: bounds.width, + height: bounds.height, + style: { + width: `${bounds.width}px`, + height: `${bounds.height}px`, + transform: `translate(${transform.x}px, ${transform.y}px) scale(${transform.zoom})`, + }, + }) + .then(resolve) + .catch(reject) + .finally(() => { + document.body.removeChild(container); + }); + }); + }); + } + ); + }); +} + +export function exportToJson(fileName: string, model: StaticModel) { + const json = getExportJsonFromModel(model); + const blob = new Blob([JSON.stringify(json, null, 2)], { + type: 'application/json', + }); + const url = window.URL.createObjectURL(blob); + downloadFile(url, fileName, () => { + window.URL.revokeObjectURL(url); + }); +} + +export function getExportJsonFromModel({ + collections, + relationships, +}: StaticModel) { + return { + collections: Object.fromEntries( + collections.map((collection) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { ns, jsonSchema, ...ignoredProps } = collection; + return [ns, { ns, jsonSchema }]; + }) + ), + relationships, + }; +} + +function downloadFile(uri: string, fileName: string, cleanup?: () => void) { + const link = document.createElement('a'); + link.download = fileName; + link.href = uri; + link.click(); + setTimeout(() => { + link.remove(); + cleanup?.(); + }, 0); +} diff --git a/packages/compass-e2e-tests/helpers/commands/hide-visible-toasts.ts b/packages/compass-e2e-tests/helpers/commands/hide-visible-toasts.ts index d7843d35745..acd54b23286 100644 --- a/packages/compass-e2e-tests/helpers/commands/hide-visible-toasts.ts +++ b/packages/compass-e2e-tests/helpers/commands/hide-visible-toasts.ts @@ -27,8 +27,9 @@ export async function hideAllVisibleToasts( const toasts = browser.$(Selectors.LGToastContainer).$$('div'); for (const _toast of toasts) { - // if they all went away at some point, just stop - if (!(await isToastContainerVisible(browser))) { + // if they all went away at some point + // or if that toast is not visible anymore just stop + if (!(await isToastContainerVisible(browser)) || !_toast) { return; } diff --git a/packages/compass-e2e-tests/helpers/compass.ts b/packages/compass-e2e-tests/helpers/compass.ts index 919ef2650f6..492b2a02ea2 100644 --- a/packages/compass-e2e-tests/helpers/compass.ts +++ b/packages/compass-e2e-tests/helpers/compass.ts @@ -781,6 +781,8 @@ export async function startBrowser( 'browser.download.folderList': 2, 'browser.download.manager.showWhenStarting': false, 'browser.helperApps.neverAsk.saveToDisk': '*/*', + // Hide the download (progress) panel + 'browser.download.alwaysOpenPanel': false, }, }, }, diff --git a/packages/compass-e2e-tests/helpers/selectors.ts b/packages/compass-e2e-tests/helpers/selectors.ts index 5e9838fb166..dcd7b57b351 100644 --- a/packages/compass-e2e-tests/helpers/selectors.ts +++ b/packages/compass-e2e-tests/helpers/selectors.ts @@ -1450,6 +1450,7 @@ export const DataModelUndoButton = 'button[aria-label="Undo"]'; export const DataModelRedoButton = 'button[aria-label="Redo"]'; export const DataModelExportButton = 'button[aria-label="Export"]'; export const DataModelExportModal = '[data-testid="export-diagram-modal"]'; +export const DataModelExportPngOption = `${DataModelExportModal} input[aria-label="PNG"]`; export const DataModelExportJsonOption = `${DataModelExportModal} input[aria-label="JSON"]`; export const DataModelExportModalConfirmButton = '[data-testid="export-button"]'; diff --git a/packages/compass-e2e-tests/package.json b/packages/compass-e2e-tests/package.json index c8f2bc3c450..81e080462e7 100644 --- a/packages/compass-e2e-tests/package.json +++ b/packages/compass-e2e-tests/package.json @@ -68,6 +68,7 @@ "puppeteer-core": "^23.10.3", "resolve-mongodb-srv": "^1.1.5", "semver": "^7.6.3", + "tesseract.js": "^6.0.1", "tree-kill": "^1.2.2", "ts-node": "^10.9.1", "webdriverio": "^9.4.1", diff --git a/packages/compass-e2e-tests/tests/data-modeling-tab.test.ts b/packages/compass-e2e-tests/tests/data-modeling-tab.test.ts index ead65345527..b60956e6840 100644 --- a/packages/compass-e2e-tests/tests/data-modeling-tab.test.ts +++ b/packages/compass-e2e-tests/tests/data-modeling-tab.test.ts @@ -17,6 +17,10 @@ import { waitForFileDownload, } from '../helpers/downloads'; import { readFileSync } from 'fs'; +import { recognize } from 'tesseract.js'; +import path from 'path'; +import os from 'os'; +import fs from 'fs/promises'; interface Node { id: string; @@ -90,10 +94,16 @@ describe('Data Modeling tab', function () { let compass: Compass; let browser: CompassBrowser; let exportFileName: string; + let tmpdir: string; before(async function () { compass = await init(this.test?.fullTitle()); browser = compass.browser; + tmpdir = path.join( + os.tmpdir(), + `compass-data-modeling-${Date.now().toString(32)}` + ); + await fs.mkdir(tmpdir, { recursive: true }); }); beforeEach(async function () { @@ -102,13 +112,14 @@ describe('Data Modeling tab', function () { if (exportFileName) { cleanUpDownloadedFile(exportFileName); } - await createNumbersStringCollection('testCollection1'); - await createNumbersCollection('testCollection2'); + await createNumbersStringCollection('testCollection-one'); + await createNumbersCollection('testCollection-two'); await browser.disconnectAll(); await browser.connectToDefaults(); }); after(async function () { + await fs.rmdir(tmpdir, { recursive: true }); if (compass) { await cleanup(compass); } @@ -134,15 +145,15 @@ describe('Data Modeling tab', function () { const nodes = await getDiagramNodes(browser); expect(nodes).to.have.lengthOf(2); - expect(nodes[0].id).to.equal('test.testCollection1'); - expect(nodes[1].id).to.equal('test.testCollection2'); + expect(nodes[0].id).to.equal('test.testCollection-one'); + expect(nodes[1].id).to.equal('test.testCollection-two'); // Apply change to the model // react flow uses its own coordinate system, // so we get the node element location for the pointer action const testCollection1 = browser.$( - Selectors.DataModelPreviewCollection('test.testCollection1') + Selectors.DataModelPreviewCollection('test.testCollection-one') ); const startPosition = await testCollection1.getLocation(); const nodeSize = await testCollection1.getSize(); @@ -203,7 +214,7 @@ describe('Data Modeling tab', function () { }); it('exports the data model to JSON', async function () { - const dataModelName = 'Test Export Model'; + const dataModelName = 'Test Export Model - JSON'; exportFileName = `${dataModelName}.json`; await setupDiagram(browser, { diagramName: dataModelName, @@ -230,8 +241,8 @@ describe('Data Modeling tab', function () { // Within beforeEach hook, we create these two collections expect(model).to.deep.equal({ collections: { - 'test.testCollection1': { - ns: 'test.testCollection1', + 'test.testCollection-one': { + ns: 'test.testCollection-one', jsonSchema: { bsonType: 'object', required: ['_id', 'i', 'iString', 'j'], @@ -251,8 +262,8 @@ describe('Data Modeling tab', function () { }, }, }, - 'test.testCollection2': { - ns: 'test.testCollection2', + 'test.testCollection-two': { + ns: 'test.testCollection-two', jsonSchema: { bsonType: 'object', required: ['_id', 'i', 'j'], @@ -273,4 +284,50 @@ describe('Data Modeling tab', function () { relationships: [], }); }); + + it('exports the data model to PNG', async function () { + if (process.platform === 'win32') { + console.warn('Skipping PNG export test on Windows'); + this.skip(); + } + const dataModelName = 'Test Export Model - PNG'; + exportFileName = `${dataModelName}.png`; + await setupDiagram(browser, { + diagramName: dataModelName, + connectionName: DEFAULT_CONNECTION_NAME_1, + databaseName: 'test', + }); + + await browser.clickVisible(Selectors.DataModelExportButton); + const exportModal = browser.$(Selectors.DataModelExportModal); + await exportModal.waitForDisplayed(); + + await browser.clickParent(Selectors.DataModelExportPngOption); + await browser.clickVisible(Selectors.DataModelExportModalConfirmButton); + + const { fileExists, filePath } = await waitForFileDownload( + exportFileName, + browser + ); + expect(fileExists).to.be.true; + + const { data } = await recognize(filePath, 'eng', { + cachePath: tmpdir, + }); + + const text = data.text.toLowerCase(); + + console.log(`Recognized PNG export text:`, text); + + expect(text).to.include('testCollection-one'.toLowerCase()); + expect(text).to.include('testCollection-two'.toLowerCase()); + + expect(text).to.include('id objectId'.toLowerCase()); + expect(text).to.include('i int'); + expect(text).to.include('j int'); + // it does not correctly recognize `iString` and only returns `String`. + // its already good enough to verify this for now and if it flakes + // more, we may need to revisit this test. + expect(text).to.include('String string'.toLowerCase()); + }); });