From cfbafad8598e2ce56762cd365f0df1d0ad3a66a5 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 24 Jan 2024 17:58:53 +0100 Subject: [PATCH 1/3] [Do Not Merge] PoC for shell-api autocomplete type definitions ```sh cd packages/shell-api && \ npm run compile && \ npx api-extractor run ; \ npx ts-node bin/api-postprocess.ts ; \ cat lib/api-processed.d.ts ``` --- package-lock.json | 490 +++++++++++++++++- packages/shell-api/api-extractor.json | 50 ++ packages/shell-api/bin/api-postprocess.ts | 141 +++++ packages/shell-api/package.json | 2 + packages/shell-api/src/collection.ts | 30 +- packages/shell-api/src/database.ts | 30 +- packages/shell-api/src/decorators.ts | 3 + packages/shell-api/src/helpers.ts | 11 + packages/shell-api/src/index.ts | 41 +- packages/shell-api/src/mongo.ts | 44 +- .../shell-api/src/shell-instance-state.ts | 4 +- packages/shell-api/src/stream-processor.ts | 3 +- 12 files changed, 768 insertions(+), 81 deletions(-) create mode 100644 packages/shell-api/api-extractor.json create mode 100644 packages/shell-api/bin/api-postprocess.ts diff --git a/package-lock.json b/package-lock.json index ecbf0ac61..a5c829dc1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7058,6 +7058,80 @@ "@lezer/common": "^1.0.0" } }, + "node_modules/@microsoft/api-extractor": { + "version": "7.39.3", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.39.3.tgz", + "integrity": "sha512-Gh1NBS4Hh0fIfu8lfneaiBUq8IOm/0/yCy0t7+qq6XQEEfgVQNweCx7De0ISfhyqjL7J42qSIix4v77EWubCYg==", + "dev": true, + "dependencies": { + "@microsoft/api-extractor-model": "7.28.6", + "@microsoft/tsdoc": "0.14.2", + "@microsoft/tsdoc-config": "~0.16.1", + "@rushstack/node-core-library": "3.64.1", + "@rushstack/rig-package": "0.5.1", + "@rushstack/ts-command-line": "4.17.1", + "colors": "~1.2.1", + "lodash": "~4.17.15", + "resolve": "~1.22.1", + "semver": "~7.5.4", + "source-map": "~0.6.1", + "typescript": "5.3.3" + }, + "bin": { + "api-extractor": "bin/api-extractor" + } + }, + "node_modules/@microsoft/api-extractor-model": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.28.6.tgz", + "integrity": "sha512-Jmz0PkQNvxMDpdL2Kea7GDLt5RL/nbKqI5lMkeftIfCAx9OtLqMnWHzKYBPFh/AR5GhmyLT3bJ/2Ml/ykCF8qQ==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.14.2", + "@microsoft/tsdoc-config": "~0.16.1", + "@rushstack/node-core-library": "3.64.1" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@microsoft/tsdoc": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", + "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", + "dev": true + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", + "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.14.2", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "dependencies": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/@mongodb-js/compass-components": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@mongodb-js/compass-components/-/compass-components-1.8.0.tgz", @@ -9541,6 +9615,83 @@ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" } }, + "node_modules/@rushstack/node-core-library": { + "version": "3.64.1", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.64.1.tgz", + "integrity": "sha512-eC2OdAvH+1siC+H2gqS8QP+QWv9EUU0mfY0+84lWojl8wamYa5WIdVETIaHSc5gDlRTz4lk+sJUOoWCARpj5dw==", + "dev": true, + "dependencies": { + "colors": "~1.2.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.22.1", + "semver": "~7.5.4", + "z-schema": "~5.0.2" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/@rushstack/rig-package": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.1.tgz", + "integrity": "sha512-pXRYSe29TjRw7rqxD4WS3HN/sRSbfr+tJs4a9uuaSIBAITbUggygdhuG0VrO0EO+QqH91GhYMN4S6KRtOEmGVA==", + "dev": true, + "dependencies": { + "resolve": "~1.22.1", + "strip-json-comments": "~3.1.1" + } + }, + "node_modules/@rushstack/ts-command-line": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.17.1.tgz", + "integrity": "sha512-2jweO1O57BYP5qdBGl6apJLB+aRIn5ccIRTPDyULh0KMwVzFqWtw6IZWt1qtUoZD/pD2RNkIOosH6Cq45rIYeg==", + "dev": true, + "dependencies": { + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "colors": "~1.2.1", + "string-argv": "~0.3.1" + } + }, "node_modules/@segment/loosely-validate-event": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz", @@ -9836,6 +9987,12 @@ "integrity": "sha512-y9KJUf19SBowoaqhWdQNnErxFMNsKbuair2i3SGv5Su1ExLPAJz37iPXLHKIFQGYkHGxsSe45rNt8ZekXxJwUw==", "dev": true }, + "node_modules/@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "dev": true + }, "node_modules/@types/babel__core": { "version": "7.20.1", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", @@ -13013,6 +13170,15 @@ "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", "dev": true }, + "node_modules/colors": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", + "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/columnify": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz", @@ -16413,9 +16579,12 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/function.prototype.name": { "version": "1.1.5", @@ -17209,6 +17378,18 @@ "node": ">=8" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "devOptional": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -17715,6 +17896,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/import-local": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", @@ -18074,12 +18264,12 @@ } }, "node_modules/is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "devOptional": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -18778,6 +18968,12 @@ "node": ">= 10.13.0" } }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true + }, "node_modules/jmespath": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", @@ -26855,12 +27051,12 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "devOptional": true, "dependencies": { - "is-core-module": "^2.8.1", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -28250,6 +28446,15 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -29600,6 +29805,15 @@ "builtins": "^1.0.3" } }, + "node_modules/validator": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", + "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -30756,6 +30970,36 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "dev": true, + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "commander": "^9.4.1" + } + }, + "node_modules/z-schema/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "optional": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, "packages/arg-parser": { "name": "@mongosh/arg-parser", "version": "0.0.0-dev.0", @@ -31554,6 +31798,7 @@ "mongodb-redact": "^0.2.2" }, "devDependencies": { + "@microsoft/api-extractor": "^7.39.3", "@mongodb-js/eslint-config-mongosh": "^1.0.0", "@mongodb-js/prettier-config-devtools": "^1.0.1", "@mongodb-js/tsconfig-mongosh": "^1.0.0", @@ -37326,6 +37571,75 @@ "@lezer/common": "^1.0.0" } }, + "@microsoft/api-extractor": { + "version": "7.39.3", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.39.3.tgz", + "integrity": "sha512-Gh1NBS4Hh0fIfu8lfneaiBUq8IOm/0/yCy0t7+qq6XQEEfgVQNweCx7De0ISfhyqjL7J42qSIix4v77EWubCYg==", + "dev": true, + "requires": { + "@microsoft/api-extractor-model": "7.28.6", + "@microsoft/tsdoc": "0.14.2", + "@microsoft/tsdoc-config": "~0.16.1", + "@rushstack/node-core-library": "3.64.1", + "@rushstack/rig-package": "0.5.1", + "@rushstack/ts-command-line": "4.17.1", + "colors": "~1.2.1", + "lodash": "~4.17.15", + "resolve": "~1.22.1", + "semver": "~7.5.4", + "source-map": "~0.6.1", + "typescript": "5.3.3" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@microsoft/api-extractor-model": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.28.6.tgz", + "integrity": "sha512-Jmz0PkQNvxMDpdL2Kea7GDLt5RL/nbKqI5lMkeftIfCAx9OtLqMnWHzKYBPFh/AR5GhmyLT3bJ/2Ml/ykCF8qQ==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.14.2", + "@microsoft/tsdoc-config": "~0.16.1", + "@rushstack/node-core-library": "3.64.1" + } + }, + "@microsoft/tsdoc": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", + "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", + "dev": true + }, + "@microsoft/tsdoc-config": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", + "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.14.2", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" + }, + "dependencies": { + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + } + } + }, "@mongodb-js/compass-components": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@mongodb-js/compass-components/-/compass-components-1.8.0.tgz", @@ -38332,6 +38646,7 @@ "@mongosh/shell-api": { "version": "file:packages/shell-api", "requires": { + "@microsoft/api-extractor": "^7.39.3", "@mongodb-js/eslint-config-mongosh": "^1.0.0", "@mongodb-js/prettier-config-devtools": "^1.0.1", "@mongodb-js/tsconfig-mongosh": "^1.0.0", @@ -39809,6 +40124,71 @@ "@react-types/shared": "^3.17.0" } }, + "@rushstack/node-core-library": { + "version": "3.64.1", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.64.1.tgz", + "integrity": "sha512-eC2OdAvH+1siC+H2gqS8QP+QWv9EUU0mfY0+84lWojl8wamYa5WIdVETIaHSc5gDlRTz4lk+sJUOoWCARpj5dw==", + "dev": true, + "requires": { + "colors": "~1.2.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.22.1", + "semver": "~7.5.4", + "z-schema": "~5.0.2" + }, + "dependencies": { + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + } + } + }, + "@rushstack/rig-package": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.1.tgz", + "integrity": "sha512-pXRYSe29TjRw7rqxD4WS3HN/sRSbfr+tJs4a9uuaSIBAITbUggygdhuG0VrO0EO+QqH91GhYMN4S6KRtOEmGVA==", + "dev": true, + "requires": { + "resolve": "~1.22.1", + "strip-json-comments": "~3.1.1" + } + }, + "@rushstack/ts-command-line": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.17.1.tgz", + "integrity": "sha512-2jweO1O57BYP5qdBGl6apJLB+aRIn5ccIRTPDyULh0KMwVzFqWtw6IZWt1qtUoZD/pD2RNkIOosH6Cq45rIYeg==", + "dev": true, + "requires": { + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "colors": "~1.2.1", + "string-argv": "~0.3.1" + } + }, "@segment/loosely-validate-event": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz", @@ -40073,6 +40453,12 @@ "integrity": "sha512-y9KJUf19SBowoaqhWdQNnErxFMNsKbuair2i3SGv5Su1ExLPAJz37iPXLHKIFQGYkHGxsSe45rNt8ZekXxJwUw==", "dev": true }, + "@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "dev": true + }, "@types/babel__core": { "version": "7.20.1", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", @@ -42661,6 +43047,12 @@ "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", "dev": true }, + "colors": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", + "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==", + "dev": true + }, "columnify": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz", @@ -45312,9 +45704,9 @@ "optional": true }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" }, "function.prototype.name": { "version": "1.1.5", @@ -45921,6 +46313,15 @@ } } }, + "hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "devOptional": true, + "requires": { + "function-bind": "^1.1.2" + } + }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -46317,6 +46718,12 @@ "resolve-from": "^4.0.0" } }, + "import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true + }, "import-local": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", @@ -46598,12 +47005,12 @@ } }, "is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "devOptional": true, "requires": { - "has": "^1.0.3" + "hasown": "^2.0.0" } }, "is-date-object": { @@ -47087,6 +47494,12 @@ "supports-color": "^7.0.0" } }, + "jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true + }, "jmespath": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", @@ -53336,12 +53749,12 @@ "dev": true }, "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "devOptional": true, "requires": { - "is-core-module": "^2.8.1", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -54451,6 +54864,12 @@ "safe-buffer": "~5.2.0" } }, + "string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -55457,6 +55876,12 @@ "builtins": "^1.0.3" } }, + "validator": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", + "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "dev": true + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -56287,6 +56712,27 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true + }, + "z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "dev": true, + "requires": { + "commander": "^9.4.1", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "dependencies": { + "commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "optional": true + } + } } } } diff --git a/packages/shell-api/api-extractor.json b/packages/shell-api/api-extractor.json new file mode 100644 index 000000000..34e777b6e --- /dev/null +++ b/packages/shell-api/api-extractor.json @@ -0,0 +1,50 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "mainEntryPointFilePath": "/lib/index.d.ts", + "apiReport": { + "enabled": false + }, + "docModel": { + "enabled": false + }, + "bundledPackages": [ + "@mongosh/service-provider-core", + "@mongosh/types", + "mongodb", + "bson" + ], + "dtsRollup": { + "enabled": true, + "untrimmedFilePath": "", + "publicTrimmedFilePath": "/lib/api-raw.d.ts" + }, + "tsdocMetadata": { + "enabled": false + }, + "newlineKind": "lf", + "messages": { + "compilerMessageReporting": { + "default": { + "logLevel": "error" + } + }, + "extractorMessageReporting": { + "default": { + "logLevel": "error" + }, + "ae-internal-missing-underscore": { + "logLevel": "none", + "addToApiReportFile": false + }, + "ae-forgotten-export": { + "logLevel": "error", + "addToApiReportFile": false + } + }, + "tsdocMessageReporting": { + "default": { + "logLevel": "none" + } + } + } +} diff --git a/packages/shell-api/bin/api-postprocess.ts b/packages/shell-api/bin/api-postprocess.ts new file mode 100644 index 000000000..3539aa99c --- /dev/null +++ b/packages/shell-api/bin/api-postprocess.ts @@ -0,0 +1,141 @@ +import * as babel from '@babel/core'; +import type * as BabelTypes from '@babel/types'; +import { promises as fs } from 'fs'; +import path from 'path'; +import { signatures } from '../'; + +function applyAsyncRewriterChanges() { + return ({ + types: t, + }: { + types: typeof BabelTypes; + }): babel.PluginObj<{ + processedMethods: [string, string, BabelTypes.ClassBody][]; + }> => { + return { + pre() { + this.processedMethods = []; + }, + post() { + for (const className of Object.keys(signatures)) { + for (const methodName of Object.keys( + signatures[className].attributes ?? {} + )) { + if ( + signatures[className].attributes?.[methodName].returnsPromise && + !signatures[className].attributes?.[methodName].inherited + ) { + if ( + !this.processedMethods.find( + ([cls, method]) => cls === className && method === methodName + ) + ) { + console.error( + `Expected to find and transpile type for @returnsPromise-annotated method ${className}.${methodName}` + ); + } + } + } + } + }, + visitor: { + TSDeclareMethod(path) { + if ('isMongoshAsyncRewrittenMethod' in path.node) return; + + if (path.parent.type !== 'ClassBody') return; + if (path.parentPath.parent.type !== 'ClassDeclaration') return; + const classId = path.parentPath.parent.id; + if (classId?.type !== 'Identifier') return; + const className = classId.name; + if (path.node.key.type !== 'Identifier') return; + const methodName = path.node.key.name; + + if ( + this.processedMethods.find( + ([cls, method, classBody]) => + cls === className && + method === methodName && + classBody !== path.parent + ) + ) { + throw new Error(`Duplicate method: ${className}.${methodName}`); + } + this.processedMethods.push([className, methodName, path.parent]); + + if (!signatures[className]?.attributes?.[methodName]?.returnsPromise) + return; + + const { returnType } = path.node; + if (returnType?.type !== 'TSTypeAnnotation') return; + if (returnType.typeAnnotation.type !== 'TSTypeReference') return; + if (returnType.typeAnnotation.typeName.type !== 'Identifier') return; + if (returnType.typeAnnotation.typeName.name !== 'Promise') return; + if (!returnType.typeAnnotation.typeParameters?.params.length) return; + path.replaceWith({ + ...path.node, + returnType: { + ...returnType, + typeAnnotation: + returnType.typeAnnotation.typeParameters.params[0], + }, + isMongoshAsyncRewrittenMethod: true, + }); + }, + }, + }; + }; +} + +async function main() { + const apiRaw = await fs.readFile( + path.resolve(__dirname, '..', 'lib', 'api-raw.d.ts'), + 'utf8' + ); + const result = babel.transformSync(apiRaw, { + code: true, + ast: false, + configFile: false, + babelrc: false, + browserslistConfigFile: false, + compact: false, + sourceType: 'module', + plugins: [applyAsyncRewriterChanges()], + parserOpts: { + plugins: ['typescript'], + }, + }); + let code = result?.code ?? ''; + code += ` +// REPLACEME +type MongodbServerSchema = { + admin: {}, + config: {}, + test: { + test: { + schema: { + _id: ObjectId; + foo: number; + } + } + } +} +// REPLACEME + +declare global { + // second argument optional + var db: Database; + + var use: (collection: StringKey) => void; +} +`; + await fs.writeFile( + path.resolve(__dirname, '..', 'lib', 'api-processed.d.ts'), + code + ); +} + +main().catch((err) => + process.nextTick(() => { + throw err; + }) +); diff --git a/packages/shell-api/package.json b/packages/shell-api/package.json index ce9971de9..c9a980b87 100644 --- a/packages/shell-api/package.json +++ b/packages/shell-api/package.json @@ -13,6 +13,7 @@ }, "scripts": { "compile": "tsc -p tsconfig.json", + "api-generate": "api-extractor run ; ts-node bin/api-postprocess.ts", "pretest": "npm run compile", "eslint": "eslint", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -48,6 +49,7 @@ "mongodb-redact": "^0.2.2" }, "devDependencies": { + "@microsoft/api-extractor": "^7.39.3", "@mongodb-js/eslint-config-mongosh": "^1.0.0", "@mongodb-js/prettier-config-devtools": "^1.0.1", "@mongodb-js/tsconfig-mongosh": "^1.0.0", diff --git a/packages/shell-api/src/collection.ts b/packages/shell-api/src/collection.ts index c74a033db..1664bf9c9 100644 --- a/packages/shell-api/src/collection.ts +++ b/packages/shell-api/src/collection.ts @@ -22,6 +22,10 @@ import type { FindAndModifyMethodShellOptions, RemoveShellOptions, MapReduceShellOptions, + GenericCollectionSchema, + GenericDatabaseSchema, + GenericServerSideSchema, + StringKey, } from './helpers'; import { adaptAggregateOptions, @@ -90,11 +94,15 @@ import { ShellApiErrors } from './error-codes'; @shellApiClassDefault @addSourceToResults -export default class Collection extends ShellApiWithMongoClass { - _mongo: Mongo; - _database: Database; - _name: string; - constructor(mongo: Mongo, database: Database, name: string) { +export class Collection< + M extends GenericServerSideSchema = GenericServerSideSchema, + D extends GenericDatabaseSchema = M[keyof M], + C extends GenericCollectionSchema = D[keyof D] +> extends ShellApiWithMongoClass { + _mongo: Mongo; + _database: Database; + _name: StringKey; + constructor(mongo: Mongo, database: Database, name: StringKey) { super(); this._mongo = mongo; this._database = database; @@ -513,7 +521,7 @@ export default class Collection extends ShellApiWithMongoClass { query: Document = {}, projection?: Document, options: FindOptions = {} - ): Promise { + ): Promise { if (projection) { options.projection = projection; } @@ -1406,7 +1414,7 @@ export default class Collection extends ShellApiWithMongoClass { * @return {Database} */ @returnType('Database') - getDB(): Database { + getDB(): Database { this._emitCollectionApiCall('getDB'); return this._database; } @@ -1417,7 +1425,7 @@ export default class Collection extends ShellApiWithMongoClass { * @return {Mongo} */ @returnType('Mongo') - getMongo(): Mongo { + getMongo(): Mongo { this._emitCollectionApiCall('getMongo'); return this._mongo; } @@ -1754,7 +1762,7 @@ export default class Collection extends ShellApiWithMongoClass { } const ns = `${this._database._name}.${this._name}`; - const config = this._mongo.getDB('config'); + const config = this._mongo.getDB('config' as StringKey); if (collStats[0].shard) { result.shards = shardStats; } @@ -2061,7 +2069,7 @@ export default class Collection extends ShellApiWithMongoClass { this._emitCollectionApiCall('getShardDistribution', {}); const result = {} as Document; - const config = this._mongo.getDB('config'); + const config = this._mongo.getDB('config' as StringKey); const ns = `${this._database._name}.${this._name}`; const configCollectionsInfo = await config @@ -2396,3 +2404,5 @@ export default class Collection extends ShellApiWithMongoClass { ); } } + +export default Collection; diff --git a/packages/shell-api/src/database.ts b/packages/shell-api/src/database.ts index 2dee29051..854e41d0e 100644 --- a/packages/shell-api/src/database.ts +++ b/packages/shell-api/src/database.ts @@ -11,6 +11,11 @@ import { ShellApiWithMongoClass, } from './decorators'; import { asPrintable, ServerVersions, Topologies } from './enums'; +import type { + GenericDatabaseSchema, + GenericServerSideSchema, + StringKey, +} from './helpers'; import { adaptAggregateOptions, adaptOptions, @@ -66,15 +71,18 @@ type AuthDoc = { }; @shellApiClassDefault -export default class Database extends ShellApiWithMongoClass { - _mongo: Mongo; - _name: string; +export default class Database< + M extends GenericServerSideSchema = GenericServerSideSchema, + D extends GenericDatabaseSchema = M[keyof M] +> extends ShellApiWithMongoClass { + _mongo: Mongo; + _name: StringKey; _collections: Record; _session: Session | undefined; - _cachedCollectionNames: string[] = []; + _cachedCollectionNames: StringKey[] = []; _cachedHello: Document | null = null; - constructor(mongo: Mongo, name: string, session?: Session) { + constructor(mongo: Mongo, name: StringKey, session?: Session) { super(); this._mongo = mongo; this._name = name; @@ -308,11 +316,11 @@ export default class Database extends ShellApiWithMongoClass { } @returnType('Mongo') - getMongo(): Mongo { + getMongo(): Mongo { return this._mongo; } - getName(): string { + getName(): StringKey { return this._name; } @@ -323,9 +331,9 @@ export default class Database extends ShellApiWithMongoClass { */ @returnsPromise @apiVersions([1]) - async getCollectionNames(): Promise { + async getCollectionNames(): Promise[]> { this._emitDatabaseApiCall('getCollectionNames'); - return this._getCollectionNames(); + return (await this._getCollectionNames()) as StringKey[]; } /** @@ -437,7 +445,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnType('Database') - getSiblingDB(db: string): Database { + getSiblingDB>(db: K): Database { assertArgsDefinedType([db], ['string'], 'Database.getSiblingDB'); this._emitDatabaseApiCall('getSiblingDB', { db }); if (this._session) { @@ -447,7 +455,7 @@ export default class Database extends ShellApiWithMongoClass { } @returnType('Collection') - getCollection(coll: string): Collection { + getCollection>(coll: K): Collection { assertArgsDefinedType([coll], ['string'], 'Database.getColl'); this._emitDatabaseApiCall('getCollection', { coll }); if (!isValidCollectionName(coll)) { diff --git a/packages/shell-api/src/decorators.ts b/packages/shell-api/src/decorators.ts index 7b13e014a..7071d77ed 100644 --- a/packages/shell-api/src/decorators.ts +++ b/packages/shell-api/src/decorators.ts @@ -381,6 +381,7 @@ export interface TypeSignature { isDirectShellCommand?: boolean; acceptsRawInput?: boolean; shellCommandCompleter?: ShellCommandCompleter; + inherited?: boolean; } /** @@ -426,6 +427,7 @@ type ClassSignature = { isDirectShellCommand: boolean; acceptsRawInput?: boolean; shellCommandCompleter?: ShellCommandCompleter; + inherited?: true; }; }; }; @@ -574,6 +576,7 @@ function shellApiClassGeneric( isDirectShellCommand: method.isDirectShellCommand, acceptsRawInput: method.acceptsRawInput, shellCommandCompleter: method.shellCommandCompleter, + inherited: true, }; const attributeHelpKeyPrefix = `${superClassHelpKeyPrefix}.attributes.${propertyName}`; diff --git a/packages/shell-api/src/helpers.ts b/packages/shell-api/src/helpers.ts index bb5d78168..31fb9a1c6 100644 --- a/packages/shell-api/src/helpers.ts +++ b/packages/shell-api/src/helpers.ts @@ -1163,3 +1163,14 @@ export function buildConfigChunksCollectionMatch( ? { uuid: configCollectionsInfo.uuid } // new format : { ns: configCollectionsInfo._id }; // old format } + +export interface GenericCollectionSchema { + schema: Document; +} +export interface GenericDatabaseSchema { + [key: string]: GenericCollectionSchema; +} +export interface GenericServerSideSchema { + [key: string]: GenericDatabaseSchema; +} +export type StringKey = keyof T & string; diff --git a/packages/shell-api/src/index.ts b/packages/shell-api/src/index.ts index e5f0fb406..84d35555f 100644 --- a/packages/shell-api/src/index.ts +++ b/packages/shell-api/src/index.ts @@ -1,21 +1,24 @@ import AggregationCursor from './aggregation-cursor'; import RunCommandCursor from './run-command-cursor'; -import Collection from './collection'; +export { Collection } from './collection'; import Cursor from './cursor'; import Database, { CollectionNamesWithTypes } from './database'; import Explainable from './explainable'; import ExplainableCursor from './explainable-cursor'; import Help, { HelpProperties } from './help'; -import ShellInstanceState, { +export { + ShellInstanceState, EvaluationListener, ShellCliOptions, OnLoadResult, ShellPlugin, + AutocompleteParameters, } from './shell-instance-state'; +export type { ShellBson } from './shell-bson'; import Shard from './shard'; import ReplicaSet from './replica-set'; import ShellApi from './shell-api'; -import { +export { BulkWriteResult, CommandResult, CursorIterationResult, @@ -25,49 +28,43 @@ import { UpdateResult, } from './result'; import Mongo from './mongo'; -import { +export { signatures, ShellResult, toShellResult, getShellApiType, TypeSignature, + Namespace, } from './decorators'; import { Topologies, ServerVersions } from './enums'; import { InterruptFlag } from './interruptor'; +export type { + GenericCollectionSchema, + GenericDatabaseSchema, + GenericServerSideSchema, + StringKey, + FindAndModifyMethodShellOptions, + FindAndModifyShellOptions, + RemoveShellOptions, +} from './helpers'; +export type { Streams } from './streams'; +export type { StreamProcessor } from './stream-processor'; export { AggregationCursor, RunCommandCursor, CollectionNamesWithTypes, Cursor, - CursorIterationResult, Database, - Collection, Explainable, ExplainableCursor, Help, HelpProperties, - ShellInstanceState, - EvaluationListener, - BulkWriteResult, - CommandResult, - DeleteResult, - InsertManyResult, - InsertOneResult, Mongo, Shard, ReplicaSet, - UpdateResult, - signatures, ShellApi, ServerVersions, Topologies, - toShellResult, - getShellApiType, - ShellResult, - ShellCliOptions, - TypeSignature, - OnLoadResult, - ShellPlugin, InterruptFlag, }; diff --git a/packages/shell-api/src/mongo.ts b/packages/shell-api/src/mongo.ts index 49e003423..2fbe47877 100644 --- a/packages/shell-api/src/mongo.ts +++ b/packages/shell-api/src/mongo.ts @@ -49,6 +49,7 @@ import { CommandResult } from './result'; import { redactURICredentials } from '@mongosh/history'; import { asPrintable, ServerVersions, Topologies } from './enums'; import Session from './session'; +import type { GenericServerSideSchema, StringKey } from './helpers'; import { assertArgsDefinedType, processFLEOptions, @@ -69,16 +70,19 @@ type Mutable = { @shellApiClassDefault @classPlatforms(['CLI']) -export default class Mongo extends ShellApiClass { +export default class Mongo< + M extends GenericServerSideSchema = GenericServerSideSchema +> extends ShellApiClass { private __serviceProvider: ServiceProvider | null = null; - public readonly _databases: Record = Object.create(null); + public readonly _databases: Record, Database> = + Object.create(null); public _instanceState: ShellInstanceState; public _connectionInfo: ConnectionInfo; private _explicitEncryptionOnly = false; private _keyVault: KeyVault | undefined; // need to keep it around so that the ShellApi ClientEncryption class can access it private _clientEncryption: ClientEncryption | undefined; private _readPreferenceWasExplicitlyRequested = false; - private _cachedDatabaseNames: string[] = []; + private _cachedDatabaseNames: StringKey[] = []; constructor( instanceState: ShellInstanceState, @@ -248,7 +252,7 @@ export default class Mongo extends ShellApiClass { } } - _getDb(name: string): Database { + _getDb>(name: K): Database { assertArgsDefinedType([name], ['string']); if (!isValidDatabaseName(name)) { throw new MongoshInvalidInputError( @@ -264,14 +268,16 @@ export default class Mongo extends ShellApiClass { } @returnType('Database') - getDB(db: string): Database { + getDB>(db: K): Database { assertArgsDefinedType([db], ['string'], 'Mongo.getDB'); this._instanceState.messageBus.emit('mongosh:getDB', { db }); return this._getDb(db); } @returnType('Collection') - getCollection(name: string): Collection { + getCollection, KC extends StringKey>( + name: `${KD}.${KC}` + ): Collection { assertArgsDefinedType([name], ['string']); const { db, coll } = /^(?[^.]+)\.(?.+)$/.exec(name)?.groups ?? {}; if (!db || !coll) { @@ -280,14 +286,14 @@ export default class Mongo extends ShellApiClass { CommonErrors.InvalidArgument ); } - return this._getDb(db).getCollection(coll); + return this._getDb(db as StringKey).getCollection(coll); } getURI(): string { return this._uri; } - use(db: string): string { + use(db: StringKey): string { assertArgsDefinedType([db], ['string'], 'Mongo.use'); this._instanceState.messageBus.emit('mongosh:use', { db }); @@ -356,9 +362,13 @@ export default class Mongo extends ShellApiClass { @returnsPromise @apiVersions([1]) - async getDBNames(options: ListDatabasesOptions = {}): Promise { + async getDBNames( + options: ListDatabasesOptions = {} + ): Promise[]> { this._emitMongoApiCall('getDBNames', { options }); - return (await this._listDatabases(options)).databases.map((db) => db.name); + return (await this._listDatabases(options)).databases.map( + (db) => db.name as StringKey + ); } @returnsPromise @@ -830,17 +840,23 @@ export default class Mongo extends ShellApiClass { for (const approach of [ // Try $documents if available (NB: running $documents on an empty db requires SERVER-63811 i.e. 6.0.3+). () => - this.getDB('_fakeDbForMongoshCSKTH').aggregate([ + this.getDB('_fakeDbForMongoshCSKTH' as StringKey).aggregate([ + { $documents: [{}] }, + ...pipeline, + ]), + () => + this.getDB('admin' as StringKey).aggregate([ { $documents: [{}] }, ...pipeline, ]), - () => this.getDB('admin').aggregate([{ $documents: [{}] }, ...pipeline]), // If that fails, try a default collection like admin.system.version. () => - this.getDB('admin').getCollection('system.version').aggregate(pipeline), + this.getDB('admin' as StringKey) + .getCollection('system.version') + .aggregate(pipeline), // If that fails, try using $collStats for local.oplog.rs. () => - this.getDB('local') + this.getDB('local' as StringKey) .getCollection('oplog.rs') .aggregate([{ $collStats: {} }, ...pipeline]), ]) { diff --git a/packages/shell-api/src/shell-instance-state.ts b/packages/shell-api/src/shell-instance-state.ts index 3ec67224c..c3c5b91de 100644 --- a/packages/shell-api/src/shell-instance-state.ts +++ b/packages/shell-api/src/shell-instance-state.ts @@ -132,7 +132,7 @@ export interface ShellPlugin { * shell API is concerned) and keeps track of all open connections (a.k.a. Mongo * instances). */ -export default class ShellInstanceState { +export class ShellInstanceState { public currentCursor: | Cursor | AggregationCursor @@ -652,3 +652,5 @@ export default class ShellInstanceState { } } } + +export default ShellInstanceState; diff --git a/packages/shell-api/src/stream-processor.ts b/packages/shell-api/src/stream-processor.ts index 26590d542..5afb223f9 100644 --- a/packages/shell-api/src/stream-processor.ts +++ b/packages/shell-api/src/stream-processor.ts @@ -11,7 +11,7 @@ import { import type { Streams } from './streams'; @shellApiClassDefault -export default class StreamProcessor extends ShellApiWithMongoClass { +export class StreamProcessor extends ShellApiWithMongoClass { constructor(public _streams: Streams, public name: string) { super(); } @@ -108,3 +108,4 @@ export default class StreamProcessor extends ShellApiWithMongoClass { return; } } +export default StreamProcessor; From 0e17bc2cbdbab68ae67373f27efd07b9d6459f85 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 24 Jan 2024 19:07:40 +0100 Subject: [PATCH 2/3] fixup: add index signature support --- packages/shell-api/bin/api-postprocess.ts | 7 ++++ packages/shell-api/src/collection.ts | 43 ++++++++++++++------ packages/shell-api/src/database.ts | 48 ++++++++++++++++------ packages/shell-api/src/mongo.ts | 49 +++++++++++++---------- packages/shell-api/src/session.ts | 24 +++++++---- 5 files changed, 116 insertions(+), 55 deletions(-) diff --git a/packages/shell-api/bin/api-postprocess.ts b/packages/shell-api/bin/api-postprocess.ts index 3539aa99c..54d73bdea 100644 --- a/packages/shell-api/bin/api-postprocess.ts +++ b/packages/shell-api/bin/api-postprocess.ts @@ -116,6 +116,13 @@ type MongodbServerSchema = { _id: ObjectId; foo: number; } + }, + with: { schema: never }, + 'with.dots': { + schema: { + _id: ObjectId; + bar: string; + } } } } diff --git a/packages/shell-api/src/collection.ts b/packages/shell-api/src/collection.ts index 1664bf9c9..6ed3bdce5 100644 --- a/packages/shell-api/src/collection.ts +++ b/packages/shell-api/src/collection.ts @@ -92,17 +92,36 @@ import PlanCache from './plan-cache'; import ChangeStreamCursor from './change-stream-cursor'; import { ShellApiErrors } from './error-codes'; +export type Collection< + M extends GenericServerSideSchema = GenericServerSideSchema, + D extends GenericDatabaseSchema = M[keyof M], + C extends GenericCollectionSchema = D[keyof D], + N extends StringKey = StringKey +> = CollectionImpl & { + [k in keyof D as k extends `${N}.${infer S}` ? S : never]: Collection< + M, + D, + D[k] + >; +}; + @shellApiClassDefault @addSourceToResults -export class Collection< +export class CollectionImpl< M extends GenericServerSideSchema = GenericServerSideSchema, D extends GenericDatabaseSchema = M[keyof M], - C extends GenericCollectionSchema = D[keyof D] + C extends GenericCollectionSchema = D[keyof D], + N extends StringKey = StringKey > extends ShellApiWithMongoClass { _mongo: Mongo; _database: Database; - _name: StringKey; - constructor(mongo: Mongo, database: Database, name: StringKey) { + _name: N; + + _typeLaunder(): Collection { + return this as Collection; + } + + constructor(mongo: Mongo, database: Database, name: N) { super(); this._mongo = mongo; this._database = database; @@ -1555,9 +1574,9 @@ export class Collection< return `${this._database._name}.${this._name}`; } - getName(): string { + getName(): N { this._emitCollectionApiCall('getName'); - return `${this._name}`; + return this._name; } @returnsPromise @@ -1604,7 +1623,7 @@ export class Collection< explain(verbosity: ExplainVerbosityLike = 'queryPlanner'): Explainable { verbosity = validateExplainableVerbosity(verbosity); this._emitCollectionApiCall('explain', { verbosity }); - return new Explainable(this._mongo, this, verbosity); + return new Explainable(this._mongo, this._typeLaunder(), verbosity); } /** @@ -1972,7 +1991,7 @@ export class Collection< true, await this._database._baseOptions() ); - return new Bulk(this, innerBulk, true); + return new Bulk(this._typeLaunder(), innerBulk, true); } @returnsPromise @@ -1986,14 +2005,14 @@ export class Collection< false, await this._database._baseOptions() ); - return new Bulk(this, innerBulk); + return new Bulk(this._typeLaunder(), innerBulk); } @returnType('PlanCache') @apiVersions([]) getPlanCache(): PlanCache { this._emitCollectionApiCall('getPlanCache'); - return new PlanCache(this); + return new PlanCache(this._typeLaunder()); } @returnsPromise @@ -2242,7 +2261,7 @@ export class Collection< @apiVersions([1]) async hideIndex(index: string | Document): Promise { this._emitCollectionApiCall('hideIndex'); - return setHideIndex(this, index, true); + return setHideIndex(this._typeLaunder(), index, true); } @serverVersions(['4.4.0', ServerVersions.latest]) @@ -2250,7 +2269,7 @@ export class Collection< @apiVersions([1]) async unhideIndex(index: string | Document): Promise { this._emitCollectionApiCall('unhideIndex'); - return setHideIndex(this, index, false); + return setHideIndex(this._typeLaunder(), index, false); } @serverVersions(['7.0.0', ServerVersions.latest]) diff --git a/packages/shell-api/src/database.ts b/packages/shell-api/src/database.ts index 854e41d0e..b83288e7f 100644 --- a/packages/shell-api/src/database.ts +++ b/packages/shell-api/src/database.ts @@ -1,5 +1,6 @@ import type Mongo from './mongo'; -import Collection from './collection'; +import type Collection from './collection'; +import { CollectionImpl } from './collection'; import { returnsPromise, returnType, @@ -70,26 +71,37 @@ type AuthDoc = { mechanism?: string; }; +export type Database< + M extends GenericServerSideSchema = GenericServerSideSchema, + D extends GenericDatabaseSchema = GenericDatabaseSchema +> = DatabaseImpl & { + [k in StringKey]: Collection; +}; + @shellApiClassDefault -export default class Database< +export class DatabaseImpl< M extends GenericServerSideSchema = GenericServerSideSchema, - D extends GenericDatabaseSchema = M[keyof M] + D extends GenericDatabaseSchema = GenericDatabaseSchema > extends ShellApiWithMongoClass { _mongo: Mongo; _name: StringKey; - _collections: Record; + _collections: Record>; _session: Session | undefined; _cachedCollectionNames: StringKey[] = []; _cachedHello: Document | null = null; + _typeLaunder(): Database { + return this as Database; + } + constructor(mongo: Mongo, name: StringKey, session?: Session) { super(); this._mongo = mongo; this._name = name; - const collections: Record = Object.create(null); + const collections: Record> = Object.create(null); this._collections = collections; this._session = session; - const proxy = new Proxy(this, { + const proxy = new Proxy(this._typeLaunder(), { get: (target, prop): any => { if (prop in target) { return (target as any)[prop]; @@ -104,7 +116,11 @@ export default class Database< } if (!collections[prop]) { - collections[prop] = new Collection(mongo, proxy, prop); + collections[prop] = new CollectionImpl( + mongo, + proxy, + prop + )._typeLaunder(); } return collections[prop]; @@ -449,13 +465,13 @@ export default class Database< assertArgsDefinedType([db], ['string'], 'Database.getSiblingDB'); this._emitDatabaseApiCall('getSiblingDB', { db }); if (this._session) { - return this._session.getDatabase(db); + return this._session.getDatabase(db) as Database; } return this._mongo._getDb(db); } @returnType('Collection') - getCollection>(coll: K): Collection { + getCollection>(coll: K): Collection { assertArgsDefinedType([coll], ['string'], 'Database.getColl'); this._emitDatabaseApiCall('getCollection', { coll }); if (!isValidCollectionName(coll)) { @@ -465,13 +481,17 @@ export default class Database< ); } - const collections: Record = this._collections; + const collections: Record> = this._collections; if (!collections[coll]) { - collections[coll] = new Collection(this._mongo, this, coll); + collections[coll] = new CollectionImpl( + this._mongo, + this._typeLaunder(), + coll + )._typeLaunder(); } - return collections[coll]; + return collections[coll] as Collection; } @returnsPromise @@ -1471,7 +1491,7 @@ export default class Database< async printShardingStatus(verbose = false): Promise { this._emitDatabaseApiCall('printShardingStatus', { verbose }); const result = await getPrintableShardStatus( - await getConfigDB(this), + await getConfigDB(this._typeLaunder()), verbose ); return new CommandResult('StatsResult', result); @@ -1760,3 +1780,5 @@ export default class Database< }); } } + +export default Database; diff --git a/packages/shell-api/src/mongo.ts b/packages/shell-api/src/mongo.ts index 2fbe47877..d0c16ad06 100644 --- a/packages/shell-api/src/mongo.ts +++ b/packages/shell-api/src/mongo.ts @@ -19,31 +19,31 @@ import { topologies, deprecated, } from './decorators'; -import type { - ChangeStreamOptions, - ClientSessionOptions, - CommandOperationOptions, - Document, - ListDatabasesOptions, - ReadConcernLevel, - ReadPreference, - ReadPreferenceLike, - ReadPreferenceMode, - ServiceProvider, - TransactionOptions, - MongoClientOptions, - AutoEncryptionOptions as SPAutoEncryption, - ServerApi, - ServerApiVersion, - WriteConcern, +import { + type ChangeStreamOptions, + type ClientSessionOptions, + type CommandOperationOptions, + type Document, + type ListDatabasesOptions, + type ReadConcernLevel, + type ReadPreference, + type ReadPreferenceLike, + type ReadPreferenceMode, + type ServiceProvider, + type TransactionOptions, + type MongoClientOptions, + type AutoEncryptionOptions as SPAutoEncryption, + type ServerApi, + type ServerApiVersion, + type WriteConcern, } from '@mongosh/service-provider-core'; import type { ConnectionInfo } from '@mongosh/arg-parser'; import { mapCliToDriver, generateConnectionInfoFromCliArgs, } from '@mongosh/arg-parser'; -import type Collection from './collection'; -import Database from './database'; +import type { Database } from './database'; +import { DatabaseImpl } from './database'; import type ShellInstanceState from './shell-instance-state'; import { CommandResult } from './result'; import { redactURICredentials } from '@mongosh/history'; @@ -62,6 +62,7 @@ import { KeyVault, ClientEncryption } from './field-level-encryption'; import { ShellApiErrors } from './error-codes'; import type { LogEntry } from './log-entry'; import { parseAnyLogEntry } from './log-entry'; +import type { Collection } from './collection'; /* Utility, inverse of Readonly */ type Mutable = { @@ -262,9 +263,9 @@ export default class Mongo< } if (!(name in this._databases)) { - this._databases[name] = new Database(this, name); + this._databases[name] = new DatabaseImpl(this, name)._typeLaunder(); } - return this._databases[name]; + return this._databases[name] as Database; } @returnType('Database') @@ -286,7 +287,11 @@ export default class Mongo< CommonErrors.InvalidArgument ); } - return this._getDb(db as StringKey).getCollection(coll); + return this._getDb(db as StringKey).getCollection(coll) as Collection< + M, + M[KD], + M[KD][KC] + >; } getURI(): string { diff --git a/packages/shell-api/src/session.ts b/packages/shell-api/src/session.ts index cdae162d5..cd6e06201 100644 --- a/packages/shell-api/src/session.ts +++ b/packages/shell-api/src/session.ts @@ -14,21 +14,25 @@ import type { } from '@mongosh/service-provider-core'; import { asPrintable } from './enums'; import type Mongo from './mongo'; -import Database from './database'; +import type Database from './database'; +import { DatabaseImpl } from './database'; import { CommonErrors, MongoshInvalidInputError } from '@mongosh/errors'; +import type { GenericServerSideSchema, StringKey } from './helpers'; import { assertArgsDefinedType, isValidDatabaseName } from './helpers'; @shellApiClassDefault @classPlatforms(['CLI']) -export default class Session extends ShellApiWithMongoClass { +export default class Session< + M extends GenericServerSideSchema = GenericServerSideSchema +> extends ShellApiWithMongoClass { public id: ServerSessionId | undefined; public _session: ClientSession; public _options: ClientSessionOptions; - public _mongo: Mongo; - private _databases: Record; + public _mongo: Mongo; + private _databases: Record>; constructor( - mongo: Mongo, + mongo: Mongo, options: ClientSessionOptions, session: ClientSession ) { @@ -47,7 +51,7 @@ export default class Session extends ShellApiWithMongoClass { return this._session.id; } - getDatabase(name: string): Database { + getDatabase>(name: K): Database { assertArgsDefinedType([name], ['string'], 'Session.getDatabase'); if (!isValidDatabaseName(name)) { @@ -58,9 +62,13 @@ export default class Session extends ShellApiWithMongoClass { } if (!(name in this._databases)) { - this._databases[name] = new Database(this._mongo, name, this); + this._databases[name] = new DatabaseImpl( + this._mongo, + name, + this + )._typeLaunder(); } - return this._databases[name]; + return this._databases[name] as Database; } advanceOperationTime(ts: TimestampType): void { From 4d722788ea5cb5bebc900225554b656fe04af3c0 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 24 Jan 2024 19:19:47 +0100 Subject: [PATCH 3/3] fixup --- packages/shell-api/src/collection.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/shell-api/src/collection.ts b/packages/shell-api/src/collection.ts index 6ed3bdce5..d46a4ec8d 100644 --- a/packages/shell-api/src/collection.ts +++ b/packages/shell-api/src/collection.ts @@ -98,10 +98,11 @@ export type Collection< C extends GenericCollectionSchema = D[keyof D], N extends StringKey = StringKey > = CollectionImpl & { - [k in keyof D as k extends `${N}.${infer S}` ? S : never]: Collection< + [k in StringKey as k extends `${N}.${infer S}` ? S : never]: Collection< M, D, - D[k] + D[k], + k >; };