diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index b5867bcf056f..517333012e8f 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -141,7 +141,7 @@ jobs: if: runner.os == 'Windows' shell: bash run: | - smctl credentials save ${SM_API_KEY} ${SM_CLIENT_CERT_PASSWORD} + # smctl credentials save ${SM_API_KEY} ${SM_CLIENT_CERT_PASSWORD} NODE_OPTIONS='--max_old_space_size=6144' npm run package:windows:unpacked -w insomnia env: SM_HOST: ${{ vars.DIGICERT_SM_HOST }} diff --git a/.vscode/settings.json b/.vscode/settings.json index 914fc1596c06..184ba76b1bdd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -43,5 +43,6 @@ ], "[cpp]": { "editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd" - } + }, + "editor.formatOnPaste": true } diff --git a/package-lock.json b/package-lock.json index 2b265c50ca31..571c4f14f4e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11460,6 +11460,18 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -11579,6 +11591,29 @@ "dev": true, "license": "MIT" }, + "node_modules/apache-crypt": { + "version": "1.2.6", + "resolved": "https://registry.npmmirror.com/apache-crypt/-/apache-crypt-1.2.6.tgz", + "integrity": "sha512-072WetlM4blL8PREJVeY+WHiUh1R5VNt2HfceGS8aKqttPHcmqE5pkKuXPz/ULmJOFkc8Hw3kfKl6vy7Qka6DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "unix-crypt-td-js": "^1.1.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/apache-md5": { + "version": "1.1.8", + "resolved": "https://registry.npmmirror.com/apache-md5/-/apache-md5-1.1.8.tgz", + "integrity": "sha512-FCAJojipPn0bXjuEpjOOOMN8FZDkxfWWp4JGN9mifU2IhxvKyXZYqpzPHdnTSUpmPDy+tsslB6Z1g+Vg6nVbYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/apiconnect-wsdl": { "version": "2.0.36", "resolved": "https://registry.npmjs.org/apiconnect-wsdl/-/apiconnect-wsdl-2.0.36.tgz", @@ -11906,6 +11941,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/array.prototype.findlast": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", @@ -12009,6 +12067,16 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "license": "MIT" }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmmirror.com/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, "node_modules/assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", @@ -12229,6 +12297,13 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmmirror.com/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", + "dev": true, + "license": "MIT" + }, "node_modules/bignumber.js": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", @@ -13680,6 +13755,16 @@ "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", "license": "MIT" }, + "node_modules/crypto-random-string": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz", + "integrity": "sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/css-in-js-utils": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz", @@ -13791,6 +13876,16 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/daemonize-process": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/daemonize-process/-/daemonize-process-1.0.9.tgz", + "integrity": "sha512-YoB+AmcgHIBDVeyfVWSCV90FNk799zX8Uvn7RJTDCD8Y0EMNbSfIKLG961VgchJme2GHmqpXUuV8Rxe2j2L+bw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4" + } + }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", @@ -14496,6 +14591,19 @@ "node": "*" } }, + "node_modules/dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/dmg-builder": { "version": "26.8.1", "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.8.1.tgz", @@ -16924,6 +17032,31 @@ "micromatch": "^4.0.2" } }, + "node_modules/fixturez": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/fixturez/-/fixturez-1.1.0.tgz", + "integrity": "sha512-c4q9eZsAmCzj9gkrEO/YwIRlrHWt/TXQiX9jR9WeLFOqeeV6EyzdiiV28CpSzF6Ip+gyYrSv5UeOHqyzfcNTVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fs-extra": "^5.0.0", + "globby": "^7.1.1", + "signal-exit": "^3.0.2", + "tempy": "^0.2.1" + } + }, + "node_modules/fixturez/node_modules/fs-extra": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-5.0.0.tgz", + "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, "node_modules/flat": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", @@ -17363,6 +17496,108 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/git-http-mock-server": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/git-http-mock-server/-/git-http-mock-server-2.0.0.tgz", + "integrity": "sha512-LOCls7jjuzwfKmUbcFsqj2yIEqExBzv0rA1tL7j1ULhRLAax4U1Bd/rbU9ebtri1ldzgcPD1VAyuhS1pvDC2pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-auth": "^2.0.0", + "buffer-equal-constant-time": "^1.0.1", + "chalk": "^2.4.1", + "daemonize-process": "^1.0.9", + "fixturez": "^1.1.0", + "htpasswd-js": "^1.0.2", + "micro-cors": "^0.1.1", + "minimisted": "^2.0.0", + "ssh-keygen": "^0.4.2", + "ssh2": "^0.6.1", + "tree-kill": "^1.2.0" + }, + "bin": { + "git-http-mock-server": "http-daemon.js", + "git-ssh-mock-server": "ssh-daemon.js" + } + }, + "node_modules/git-http-mock-server/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/git-http-mock-server/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/git-http-mock-server/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/git-http-mock-server/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/git-http-mock-server/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/git-http-mock-server/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/git-http-mock-server/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -17473,6 +17708,51 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/globby": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/globby/-/globby-7.1.1.tgz", + "integrity": "sha512-yANWAN2DUcBtuus5Cpd+SKROzXHs2iVXFZt/Ykrfz6SAXqacLX25NZpltE+39ceMexYF4TtEadjuSTw8+3wX4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true, + "license": "MIT" + }, + "node_modules/globby/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/globby/node_modules/slash": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/slash/-/slash-1.0.0.tgz", + "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/gonzales-pe": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz", @@ -18021,6 +18301,32 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/htpasswd-js": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/htpasswd-js/-/htpasswd-js-1.0.2.tgz", + "integrity": "sha512-KON5L4YKYXk647tmVclKgmHHG5nApjy9K+WiRoScnoWhS63lMoTca1ommUW2XQ3FDW8TtNDIQA7J0WYXICbMAA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "apache-crypt": "^1.2.1", + "apache-md5": "^1.1.2", + "bcryptjs": "^2.4.3", + "fs-extra": "^4.0.2", + "xerror": "^1.1.2" + } + }, + "node_modules/htpasswd-js/node_modules/fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, "node_modules/http-assert": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz", @@ -20947,6 +21253,16 @@ "node": ">= 0.6" } }, + "node_modules/micro-cors": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/micro-cors/-/micro-cors-0.1.1.tgz", + "integrity": "sha512-6WqIahA5sbQR1Gjexp1VuWGFDKbZZleJb/gy1khNGk18a6iN1FdTcr3Q8twaxkV5H94RjxIBjirYbWCehpMBFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -23342,6 +23658,29 @@ "dev": true, "license": "MIT" }, + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-type/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -25967,6 +26306,60 @@ "license": "BSD-3-Clause", "optional": true }, + "node_modules/ssh-keygen": { + "version": "0.4.2", + "resolved": "https://registry.npmmirror.com/ssh-keygen/-/ssh-keygen-0.4.2.tgz", + "integrity": "sha512-SlEWW3cCtz87jwtCTfxo+tR+SQd4jJXWaBI/D9JVd74b2/N9ZvrWcd9lMFwFv0iMYb4aVAeMderH4AK5ZyW+Nw==", + "dev": true, + "dependencies": { + "underscore": "1.4.x" + }, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/ssh-keygen/node_modules/underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmmirror.com/underscore/-/underscore-1.4.4.tgz", + "integrity": "sha512-ZqGrAgaqqZM7LGRzNjLnw5elevWb5M8LEoDMadxIW3OWbcv72wMMgKdwOKpd5Fqxe8choLD8HN3iSj3TUh/giQ==", + "dev": true + }, + "node_modules/ssh2": { + "version": "0.6.2", + "resolved": "https://registry.npmmirror.com/ssh2/-/ssh2-0.6.2.tgz", + "integrity": "sha512-DJ+dOhXEEsmNpcQTI0x69FS++JH6qqL/ltEHf01pI1SSLMAcmD+hL4jRwvHjPwynPsmSUbHJ/WIZYzROfqZWjA==", + "dev": true, + "dependencies": { + "ssh2-streams": "~0.2.0" + }, + "engines": { + "node": ">=4.5.0" + } + }, + "node_modules/ssh2-streams": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/ssh2-streams/-/ssh2-streams-0.2.1.tgz", + "integrity": "sha512-3zCOsmunh1JWgPshfhKmBCL3lUtHPoh+a/cyQ49Ft0Q0aF7xgN06b76L+oKtFi0fgO57FLjFztb1GlJcEZ4a3Q==", + "dev": true, + "dependencies": { + "asn1": "~0.2.0", + "semver": "^5.1.0", + "streamsearch": "~0.1.2" + }, + "engines": { + "node": ">=4.5.0" + } + }, + "node_modules/ssh2-streams/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/ssri": { "version": "13.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.1.tgz", @@ -26216,6 +26609,15 @@ "any-promise": "^1.1.0" } }, + "node_modules/streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -26823,6 +27225,16 @@ "node": ">=6.0.0" } }, + "node_modules/temp-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/temp-dir/-/temp-dir-1.0.0.tgz", + "integrity": "sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/temp-file": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", @@ -26886,6 +27298,20 @@ "rimraf": "bin.js" } }, + "node_modules/tempy": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/tempy/-/tempy-0.2.1.tgz", + "integrity": "sha512-LB83o9bfZGrntdqPuRdanIVCPReam9SOZKW0fOy5I9X3A854GGWi0tjCqoXEk84XIEYBc/x9Hq3EFop/H5wJaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "temp-dir": "^1.0.0", + "unique-string": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/throttle-debounce": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz", @@ -27109,6 +27535,16 @@ "node": ">=18" } }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, "node_modules/truncate-utf8-bytes": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", @@ -27515,6 +27951,19 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/unique-string": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/unique-string/-/unique-string-1.0.0.tgz", + "integrity": "sha512-ODgiYu03y5g76A1I9Gt0/chLCzQjvzDy7DsZGsLOE/1MrF6wriEskSncj1+/C58Xk/kPZDppSctDybCwOSaGAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "crypto-random-string": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -27525,6 +27974,13 @@ "node": ">= 4.0.0" } }, + "node_modules/unix-crypt-td-js": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/unix-crypt-td-js/-/unix-crypt-td-js-1.1.4.tgz", + "integrity": "sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -28840,6 +29296,13 @@ } } }, + "node_modules/xerror": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/xerror/-/xerror-1.1.3.tgz", + "integrity": "sha512-2l5hmDymDUIuKT53v/nYxofTMUDQuu5P/Y3qHOjQiih6QUHBCgWpbpL3I8BoE5TVfUVTMmUQ0jdUAimTGc9UIg==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/xml-name-validator": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", @@ -29154,7 +29617,7 @@ } }, "packages/insomnia": { - "version": "12.5.1-alpha.0", + "version": "12.6.0-beta.0", "license": "Apache-2.0", "dependencies": { "@apideck/better-ajv-errors": "^0.3.6", @@ -29189,6 +29652,8 @@ "@tailwindcss/typography": "^0.5.16", "@tanstack/react-virtual": "3.13.12", "@xmldom/xmldom": "^0.9.8", + "acorn": "^8.16.0", + "acorn-walk": "^8.3.5", "ajv": "^8.17.1", "apiconnect-wsdl": "2.0.36", "aws4": "^1.13.2", @@ -29332,14 +29797,14 @@ } }, "packages/insomnia-api": { - "version": "12.5.1-alpha.0", + "version": "12.6.0-beta.0", "license": "Apache-2.0", "dependencies": { "@getinsomnia/insomnia-v3-fetch": "^1.0.1" } }, "packages/insomnia-inso": { - "version": "12.5.1-alpha.0", + "version": "12.6.0-beta.0", "license": "Apache-2.0", "dependencies": { "@seald-io/nedb": "^4.1.1", @@ -29388,7 +29853,7 @@ "license": "MIT" }, "packages/insomnia-scripting-environment": { - "version": "12.5.1-alpha.0", + "version": "12.6.0-beta.0", "license": "Apache-2.0", "dependencies": { "@types/deep-equal": "^1.0.4", @@ -29408,7 +29873,7 @@ } }, "packages/insomnia-smoke-test": { - "version": "12.5.1-alpha.0", + "version": "12.6.0-beta.0", "license": "Apache-2.0", "devDependencies": { "@playwright/test": "1.55.1", @@ -29421,6 +29886,7 @@ "esbuild-runner": "2.2.2", "express": "^4.21.2", "express-basic-auth": "^1.2.1", + "git-http-mock-server": "^2.0.0", "graphql": "^16.10.0", "graphql-http": "^1.22.4", "http-errors": "^2.0.0", @@ -29739,7 +30205,7 @@ } }, "packages/insomnia-testing": { - "version": "12.5.1-alpha.0", + "version": "12.6.0-beta.0", "license": "Apache-2.0", "dependencies": { "chai-json-schema": "1.5.1" diff --git a/packages/insomnia-api/package.json b/packages/insomnia-api/package.json index 1f082aaf72e7..ea04bb53102b 100644 --- a/packages/insomnia-api/package.json +++ b/packages/insomnia-api/package.json @@ -2,7 +2,7 @@ "private": true, "name": "insomnia-api", "license": "Apache-2.0", - "version": "12.5.1-alpha.0", + "version": "12.6.0-beta.0", "author": "Kong ", "description": "Insomnia API functionalities", "repository": { diff --git a/packages/insomnia-inso/package.json b/packages/insomnia-inso/package.json index 188e6cb5bcd3..542074579cee 100644 --- a/packages/insomnia-inso/package.json +++ b/packages/insomnia-inso/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "insomnia-inso", - "version": "12.5.1-alpha.0", + "version": "12.6.0-beta.0", "homepage": "https://insomnia.rest", "description": "A CLI for Insomnia - The Collaborative API Design Tool", "author": "Kong ", diff --git a/packages/insomnia-scripting-environment/package.json b/packages/insomnia-scripting-environment/package.json index 0ad8d06759b9..f8c0fc3bbca9 100644 --- a/packages/insomnia-scripting-environment/package.json +++ b/packages/insomnia-scripting-environment/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "insomnia-scripting-environment", - "version": "12.5.1-alpha.0", + "version": "12.6.0-beta.0", "description": "", "main": "src/objects/index.ts", "types": "src/objects/index.ts", diff --git a/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/HEAD b/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/HEAD new file mode 100644 index 000000000000..cb089cd89a7d --- /dev/null +++ b/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/config b/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/config new file mode 100644 index 000000000000..64280b806c97 --- /dev/null +++ b/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/config @@ -0,0 +1,6 @@ +[core] + repositoryformatversion = 0 + filemode = false + bare = true + symlinks = false + ignorecase = true diff --git a/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/description b/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/description new file mode 100644 index 000000000000..498b267a8c78 --- /dev/null +++ b/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/info/exclude b/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/info/exclude new file mode 100644 index 000000000000..a5196d1be8fb --- /dev/null +++ b/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/objects/33/57649e085f0f653c60993fc34b0d380c4c2991 b/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/objects/33/57649e085f0f653c60993fc34b0d380c4c2991 new file mode 100644 index 000000000000..78c4d86d1a52 Binary files /dev/null and b/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/objects/33/57649e085f0f653c60993fc34b0d380c4c2991 differ diff --git a/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/objects/5a/d28e22767f979da2c198dc6c1003b25964e3da b/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/objects/5a/d28e22767f979da2c198dc6c1003b25964e3da new file mode 100644 index 000000000000..a49384ff604c Binary files /dev/null and b/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/objects/5a/d28e22767f979da2c198dc6c1003b25964e3da differ diff --git a/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/objects/73/a07b09e3e790de0277df5ac98e0ad62ecf83d4 b/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/objects/73/a07b09e3e790de0277df5ac98e0ad62ecf83d4 new file mode 100644 index 000000000000..b6f9704d044c --- /dev/null +++ b/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/objects/73/a07b09e3e790de0277df5ac98e0ad62ecf83d4 @@ -0,0 +1,3 @@ +x­ÎA +ƒ0@Ñ®sŠì™dâ˜Rz•8«P‰‘âíëª'èö->_ò²ÌÕz—ZT-b×S`…Ø0R‡BÀŒ£`à$ˆgv&íuÊÅ)t^‹Ø+E& +¾ùÑ}ß´lí;]_Gûœë´­äåf]ß“G‡Ä¶`N=?ªþ¯h’ù…³D, \ No newline at end of file diff --git a/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/refs/heads/master b/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/refs/heads/master new file mode 100644 index 000000000000..1c22a987384c --- /dev/null +++ b/packages/insomnia-smoke-test/fixtures/git-repo/git-server.git/refs/heads/master @@ -0,0 +1 @@ +73a07b09e3e790de0277df5ac98e0ad62ecf83d4 diff --git a/packages/insomnia-smoke-test/package.json b/packages/insomnia-smoke-test/package.json index 303c65151468..2628cd6ff130 100644 --- a/packages/insomnia-smoke-test/package.json +++ b/packages/insomnia-smoke-test/package.json @@ -11,7 +11,7 @@ "bugs": { "url": "https://github.com/kong/insomnia/issues" }, - "version": "12.5.1-alpha.0", + "version": "12.6.0-beta.0", "scripts": { "test:dev": "xvfb-maybe cross-env BUNDLE=dev playwright test", "test:build": "xvfb-maybe cross-env BUNDLE=build playwright test", @@ -30,6 +30,7 @@ "esbuild-runner": "2.2.2", "express": "^4.21.2", "express-basic-auth": "^1.2.1", + "git-http-mock-server": "^2.0.0", "graphql": "^16.10.0", "graphql-http": "^1.22.4", "http-errors": "^2.0.0", diff --git a/packages/insomnia-smoke-test/playwright.config.ts b/packages/insomnia-smoke-test/playwright.config.ts index 36f49be05fcf..907d01ba00cf 100644 --- a/packages/insomnia-smoke-test/playwright.config.ts +++ b/packages/insomnia-smoke-test/playwright.config.ts @@ -1,4 +1,7 @@ +import os from 'node:os'; + import type { PlaywrightTestConfig } from '@playwright/test'; +const isWindows = os.platform() === 'win32'; const config: PlaywrightTestConfig = { projects: [ { @@ -35,7 +38,7 @@ const config: PlaywrightTestConfig = { }, }, reporter: process.env.CI ? [['github'], ['line']] : [['list']], - timeout: process.env.CI ? 60 * 1000 : 20 * 1000, + timeout: process.env.CI || isWindows ? 60 * 1000 : 20 * 1000, forbidOnly: !!process.env.CI, outputDir: 'traces', testDir: 'tests', diff --git a/packages/insomnia-smoke-test/playwright/pages/preferences/credentials-tab.ts b/packages/insomnia-smoke-test/playwright/pages/preferences/credentials-tab.ts new file mode 100644 index 000000000000..17f20b6d034b --- /dev/null +++ b/packages/insomnia-smoke-test/playwright/pages/preferences/credentials-tab.ts @@ -0,0 +1,38 @@ +import type { ElectronApplication, Locator, Page } from '@playwright/test'; + +import { BasePage } from '../base-page'; + +/** + * Component for the **Credentials tab** within Insomnia Preferences. + * + * Handles credential management functionality: + * - Add, edit, and remove credentials + */ +export class PreferencesCredentialsTab extends BasePage { + constructor( + readonly page: Page, + readonly app: ElectronApplication, + ) { + super(page); + } + + get root(): Locator { + return this.page.getByTestId('credentials-settings-tab'); + } + + async addAccessTokenGitCredential() { + await this.page.getByRole('button', { name: 'Create Git Credential' }).click(); + await this.page.getByText('Access Token').click(); + await this.page.getByRole('textbox', { name: 'Author Email' }).click(); + await this.page.getByRole('textbox', { name: 'Author Email' }).fill('a@b.com'); + await this.page.getByRole('textbox', { name: 'Author Name' }).click(); + await this.page.getByRole('textbox', { name: 'Author Name' }).fill('author'); + await this.page.getByRole('textbox', { name: 'Username' }).click(); + await this.page.getByRole('textbox', { name: 'Username' }).fill('username'); + await this.page.getByRole('textbox', { name: 'Git Access Token' }).click(); + await this.page.getByRole('textbox', { name: 'Git Access Token' }).fill('accesstoken'); + await this.page.getByRole('textbox', { name: 'Repository base URL' }).click(); + await this.page.getByRole('textbox', { name: 'Repository base URL' }).fill('http://localhost:4010/git/'); + await this.page.getByRole('button', { name: 'Save Credential' }).click(); + } +} diff --git a/packages/insomnia-smoke-test/playwright/pages/preferences/index.ts b/packages/insomnia-smoke-test/playwright/pages/preferences/index.ts index 995f2f5cfa0d..d58096b7adcf 100644 --- a/packages/insomnia-smoke-test/playwright/pages/preferences/index.ts +++ b/packages/insomnia-smoke-test/playwright/pages/preferences/index.ts @@ -1,25 +1,30 @@ import type { ElectronApplication, Locator, Page } from '@playwright/test'; +import { PreferencesCredentialsTab } from './credentials-tab'; import { PreferencesDataTab } from './data-tab'; -type PreferencesTab = 'Data' | 'General' | 'Themes' | 'Plugins' | 'Other'; +type PreferencesTab = 'Data' | 'General' | 'Themes' | 'Credentials' | 'Plugins' | 'Other'; /** * Page Object for **Insomnia Preferences** modal. * * Composes preference tabs: * - Data tab (import/export) + * - Credentials tab (Git credentials management) * - Other tabs (themes, plugins, etc.) can be added as needed */ export class PreferencesPage { /** Data tab (import/export functionality). */ readonly dataTab: PreferencesDataTab; + /** Credentials tab (Git credentials management). */ + readonly credentialsTab: PreferencesCredentialsTab; constructor( readonly page: Page, readonly app: ElectronApplication, ) { this.dataTab = new PreferencesDataTab(page, app); + this.credentialsTab = new PreferencesCredentialsTab(page, app); } /** The root preferences dialog. */ @@ -43,7 +48,7 @@ export class PreferencesPage { * Closes the preferences modal. */ async closePreferences(): Promise { - await this.page.locator('.app').press('Escape'); + await this.page.getByRole('button', { name: 'Modal Close Button' }).click(); await this.root.waitFor({ state: 'hidden' }); } } diff --git a/packages/insomnia-smoke-test/playwright/pages/project/index.ts b/packages/insomnia-smoke-test/playwright/pages/project/index.ts index b174339a0e66..df1d6e81d55f 100644 --- a/packages/insomnia-smoke-test/playwright/pages/project/index.ts +++ b/packages/insomnia-smoke-test/playwright/pages/project/index.ts @@ -80,6 +80,33 @@ export class ProjectPage extends BasePage { await this.page.getByRole('button', { name: 'Create', exact: true }).click(); } + async createGitSyncProject(name = 'My Git Project'): Promise { + await this.page.getByRole('button', { name: 'Create new Project' }).click(); + await this.page.getByRole('textbox', { name: 'Project name' }).click(); + await this.page.getByRole('textbox', { name: 'Project name' }).press('ControlOrMeta+a'); + await this.page.getByRole('textbox', { name: 'Project name' }).fill(name); + await this.page.getByText('Git Sync').click(); + await this.page.getByRole('button', { name: 'Access Token author Git' }).click(); + await this.page.getByRole('option', { name: 'Custom Git Credential' }).click(); + await this.page.getByRole('textbox', { name: 'Repository URL' }).click(); + await this.page.getByRole('textbox', { name: 'Repository URL' }).fill('git-server.git'); + await this.page.getByRole('button', { name: 'Show suggestions Branch' }).click(); + await this.page.getByRole('option', { name: 'master' }).click(); + await this.page.getByRole('button', { name: 'Scan for files' }).click(); + await this.page.getByRole('button', { name: 'Create Blank Project' }).click(); + const projectModalCloseButton = this.page.locator('[data-test-id="project-modal-close-button"]'); + await projectModalCloseButton.waitFor({ state: 'visible', timeout: 5000 }).catch(() => {}); + if (await projectModalCloseButton.isVisible()) { + await projectModalCloseButton.click(); + } + await this.page.getByRole('button', { name: 'Personal workspace' }).click(); + await this.page.getByRole('option', { name: /Magic/ }).locator('span').click(); + await this.page.getByRole('button', { name: /Magic/ }).click(); + await this.page.getByRole('option', { name: 'Personal workspace' }).locator('span').click(); + await this.page.getByText('Git Project').waitFor({ state: 'visible', timeout: 10_000 }); + await this.page.getByText('Git Project').click(); + } + // =========================================================================== // Import Operations // =========================================================================== diff --git a/packages/insomnia-smoke-test/server/index.ts b/packages/insomnia-smoke-test/server/index.ts index 0f29bad42b31..649160830d1b 100644 --- a/packages/insomnia-smoke-test/server/index.ts +++ b/packages/insomnia-smoke-test/server/index.ts @@ -6,6 +6,7 @@ import nodePath from 'node:path'; import * as bodyParser from 'body-parser'; import cookieParser from 'cookie-parser'; import express from 'express'; +import gitMiddleware from 'git-http-mock-server/middleware'; import { createHandler } from 'graphql-http/lib/use/http'; import { basicAuthRouter } from './basic-auth'; @@ -152,6 +153,15 @@ app.get('/v1/oauth/azure/config', (_req, res) => { }); }); +app.use( + '/git', + gitMiddleware({ + root: nodePath.join(__dirname, '../fixtures/git-repo'), + glob: '*', + route: '/', + }), +); + startWebSocketServer( app.listen(port, '::', () => { console.log(`Listening at http://localhost:${port}`); diff --git a/packages/insomnia-smoke-test/tests/smoke/disable-git-sync.test.ts b/packages/insomnia-smoke-test/tests/smoke/disable-git-sync.test.ts new file mode 100644 index 000000000000..4f476cbe3196 --- /dev/null +++ b/packages/insomnia-smoke-test/tests/smoke/disable-git-sync.test.ts @@ -0,0 +1,101 @@ +import { expect } from '@playwright/test'; + +import { test } from '../../playwright/test'; + +const mockCredentials = { + email: 'insomnia-test@konghq.com', + gitUsername: 'insomnia-test', + username: 'insomnia', + token: '12345', + baseUrl: 'https://fakeurl.com/', +}; + +test.describe('Git Sync', () => { + test.describe('with git sync feature flag disabled', () => { + test.beforeEach(async ({ request }) => { + // Disable git sync feature flag for organization + await request.post('http://127.0.0.1:4010/v1/test-utils/organizations/features', { + data: { + features: { + gitSync: { + enabled: false, + }, + konnectSync: { + enabled: true, + }, + }, + }, + }); + }); + + test.afterEach(async ({ request }) => { + // Re-enable git sync feature flag for organization + await request.post('http://127.0.0.1:4010/v1/test-utils/organizations/features', { + data: { + features: { + gitSync: { + enabled: true, + }, + konnectSync: { + enabled: true, + }, + }, + }, + }); + }); + + test('should disable git sync usage', async ({ page }) => { + await page.getByTestId('settings-button').click(); + await page.getByRole('tab', { name: 'Credentials' }).click(); + await page.getByRole('button', { name: 'Create Git Credential' }).click(); + await page.getByText('Access Token').click(); + await page.getByRole('textbox', { name: 'Author Email' }).fill(mockCredentials.email); + await page.getByRole('textbox', { name: 'Author Name' }).fill(mockCredentials.gitUsername); + await page.getByRole('textbox', { name: 'Username', exact: true }).fill(mockCredentials.username); + await page.getByRole('textbox', { name: 'Git Access Token' }).fill(mockCredentials.token); + await page.getByRole('textbox', { name: 'Repository base URL' }).fill(mockCredentials.baseUrl); + await page.getByRole('button', { name: 'Save Credential' }).click(); + await page.getByRole('button', { name: 'Modal Close Button' }).click(); + await page.getByRole('button', { name: 'Create new Project' }).click(); + await page.getByLabel('Project Type Item: git').click(); + await expect.soft(page.getByLabel('Git Sync Feature Disabled Banner')).toBeVisible(); + + await expect.soft(page.getByLabel('Git Setup Form')).toBeHidden(); + await expect.soft(page.getByRole('button', { name: 'Scan for files' })).toBeDisabled(); + }); + }); + + test.describe('with git storage rule disabled', () => { + test.beforeEach(async ({ request }) => { + // Set storage rule to disable git sync + await request.post('http://127.0.0.1:4010/v1/test-utils/organizations/storage-rule', { + data: { + enableCloudSync: true, + enableGitSync: false, + enableLocalVault: true, + isOverridden: false, + }, + }); + }); + + test.afterEach(async ({ request }) => { + // reset the storage rule after test + await request.post('http://127.0.0.1:4010/v1/test-utils/organizations/storage-rule', { + data: { + enableCloudSync: true, + enableGitSync: true, + enableLocalVault: true, + isOverridden: false, + }, + }); + }); + + test('disable git sync selection', async ({ page }) => { + await page.getByRole('button', { name: 'Create new Project' }).click(); + const banner = page.getByLabel('Project Storage Restriction Banner'); + await expect.soft(banner).toBeVisible(); + await expect.soft(banner).not.toHaveText('Git Sync'); + await expect.soft(page.getByLabel('Project Type: git')).toBeDisabled(); + }); + }); +}); diff --git a/packages/insomnia-smoke-test/tests/smoke/export.test.ts b/packages/insomnia-smoke-test/tests/smoke/export.test.ts index fb0cd9f4d9c8..563801382ddb 100644 --- a/packages/insomnia-smoke-test/tests/smoke/export.test.ts +++ b/packages/insomnia-smoke-test/tests/smoke/export.test.ts @@ -33,9 +33,8 @@ test.describe('Export', () => { await insomnia.preferencesPage.switchToPreferenceTab('Data'); await insomnia.preferencesPage.dataTab.exportProjectData(tempDir, 'yaml'); await waitForExportFiles(tempDir, 2); - await insomnia.preferencesPage.closePreferences(); const exportedFiles = getExportedFiles(tempDir); - expect.soft(exportedFiles.length).toBe(2); + expect.soft(exportedFiles).toHaveLength(2); const fixtureMap: Record = { 'Collection-A': FIXTURE_FILES[0], 'Collection-B': FIXTURE_FILES[1], @@ -78,7 +77,7 @@ test.describe('Export', () => { await insomnia.preferencesPage.dataTab.exportAllData(tempDir); await insomnia.preferencesPage.closePreferences(); const exportedFiles = getExportedFiles(tempDir).filter((file: string) => !file.includes('scratchpad')); - expect.soft(exportedFiles.length).toBe(2); + expect.soft(exportedFiles).toHaveLength(2); const fixtureMap: Record = { 'Collection-A': FIXTURE_FILES[0], 'Collection-B': FIXTURE_FILES[1], @@ -123,8 +122,6 @@ test.describe('Export', () => { await insomnia.preferencesPage.dataTab.exportProjectData(exportFilePath, 'har'); await waitForExportFiles(tempDir, 1); - await insomnia.preferencesPage.closePreferences(); - const exportedContent = readExportedFile(exportFilePath); const har = JSON.parse(exportedContent); diff --git a/packages/insomnia-smoke-test/tests/smoke/git-sync.test.ts b/packages/insomnia-smoke-test/tests/smoke/git-sync.test.ts index 4f476cbe3196..c604c4ab8dbe 100644 --- a/packages/insomnia-smoke-test/tests/smoke/git-sync.test.ts +++ b/packages/insomnia-smoke-test/tests/smoke/git-sync.test.ts @@ -1,101 +1,96 @@ import { expect } from '@playwright/test'; +import type { InsomniaApp } from '../../playwright/pages'; import { test } from '../../playwright/test'; -const mockCredentials = { - email: 'insomnia-test@konghq.com', - gitUsername: 'insomnia-test', - username: 'insomnia', - token: '12345', - baseUrl: 'https://fakeurl.com/', -}; - test.describe('Git Sync', () => { - test.describe('with git sync feature flag disabled', () => { - test.beforeEach(async ({ request }) => { - // Disable git sync feature flag for organization - await request.post('http://127.0.0.1:4010/v1/test-utils/organizations/features', { - data: { - features: { - gitSync: { - enabled: false, - }, - konnectSync: { - enabled: true, - }, - }, - }, - }); - }); - - test.afterEach(async ({ request }) => { - // Re-enable git sync feature flag for organization - await request.post('http://127.0.0.1:4010/v1/test-utils/organizations/features', { - data: { - features: { - gitSync: { - enabled: true, - }, - konnectSync: { - enabled: true, - }, - }, - }, - }); - }); + test.slow(); - test('should disable git sync usage', async ({ page }) => { - await page.getByTestId('settings-button').click(); - await page.getByRole('tab', { name: 'Credentials' }).click(); - await page.getByRole('button', { name: 'Create Git Credential' }).click(); - await page.getByText('Access Token').click(); - await page.getByRole('textbox', { name: 'Author Email' }).fill(mockCredentials.email); - await page.getByRole('textbox', { name: 'Author Name' }).fill(mockCredentials.gitUsername); - await page.getByRole('textbox', { name: 'Username', exact: true }).fill(mockCredentials.username); - await page.getByRole('textbox', { name: 'Git Access Token' }).fill(mockCredentials.token); - await page.getByRole('textbox', { name: 'Repository base URL' }).fill(mockCredentials.baseUrl); - await page.getByRole('button', { name: 'Save Credential' }).click(); - await page.getByRole('button', { name: 'Modal Close Button' }).click(); - await page.getByRole('button', { name: 'Create new Project' }).click(); - await page.getByLabel('Project Type Item: git').click(); - await expect.soft(page.getByLabel('Git Sync Feature Disabled Banner')).toBeVisible(); + test('Create new branch and switch to it', async ({ insomnia, page }) => { + await addAccessTokenGitCredential(insomnia); + await insomnia.projectPage.createGitSyncProject(); - await expect.soft(page.getByLabel('Git Setup Form')).toBeHidden(); - await expect.soft(page.getByRole('button', { name: 'Scan for files' })).toBeDisabled(); - }); + await page.getByTestId('git-dropdown').click(); + await page.getByRole('menuitemradio', { name: 'Branches' }).click(); + await page.getByRole('textbox', { name: 'New branch name:' }).click(); + await page.getByRole('textbox', { name: 'New branch name:' }).fill('branch1'); + await page.getByRole('button', { name: 'Create', exact: true }).click(); + await expect.soft(page.getByText('branch1 *')).toBeVisible(); }); - test.describe('with git storage rule disabled', () => { - test.beforeEach(async ({ request }) => { - // Set storage rule to disable git sync - await request.post('http://127.0.0.1:4010/v1/test-utils/organizations/storage-rule', { - data: { - enableCloudSync: true, - enableGitSync: false, - enableLocalVault: true, - isOverridden: false, - }, - }); - }); + test('Commit and check history', async ({ insomnia, page }) => { + await addAccessTokenGitCredential(insomnia); + await insomnia.projectPage.createGitSyncProject(); + + await page.getByRole('button', { name: 'New request collection' }).click(); + await page.getByRole('textbox', { name: 'Name', exact: true }).click(); + await page.getByRole('textbox', { name: 'Name', exact: true }).press('ControlOrMeta+a'); + await page.getByRole('textbox', { name: 'Name', exact: true }).fill('Collection 1'); + await page.getByRole('textbox', { name: 'File name my_collection' }).click(); + await page.getByRole('textbox', { name: 'File name my_collection' }).press('ControlOrMeta+a'); + await page.getByRole('textbox', { name: 'File name my_collection' }).fill('collection_1'); + await page.getByRole('button', { name: 'Create', exact: true }).click(); + await page.getByTestId('git-dropdown').click(); + await expect.soft(page.getByRole('menuitemradio', { name: 'Commit' })).toBeVisible(); + await page.getByRole('menuitemradio', { name: 'Commit' }).click(); + await expect.soft(page.getByLabel('Unstaged changes').locator('span')).toContainText('collection_1.yaml'); - test.afterEach(async ({ request }) => { - // reset the storage rule after test - await request.post('http://127.0.0.1:4010/v1/test-utils/organizations/storage-rule', { - data: { - enableCloudSync: true, - enableGitSync: true, - enableLocalVault: true, - isOverridden: false, - }, - }); - }); + await page.locator('button[name="Stage all changes"]').click(); + await page.getByRole('textbox', { name: 'Message' }).click(); + await page.getByRole('textbox', { name: 'Message' }).fill('1'); + await page.getByRole('button', { name: 'Commit', exact: true }).click(); + await page.getByTestId('git-dropdown').click(); + await page.getByText('History').click(); + await expect.soft(page.getByLabel('1', { exact: true }).getByRole('rowheader')).toContainText('1'); + }); + + test('Merge branch and verify changes on the other branch has been merged into current branch', async ({ + insomnia, + page, + }) => { + await addAccessTokenGitCredential(insomnia); + await insomnia.projectPage.createGitSyncProject(); - test('disable git sync selection', async ({ page }) => { - await page.getByRole('button', { name: 'Create new Project' }).click(); - const banner = page.getByLabel('Project Storage Restriction Banner'); - await expect.soft(banner).toBeVisible(); - await expect.soft(banner).not.toHaveText('Git Sync'); - await expect.soft(page.getByLabel('Project Type: git')).toBeDisabled(); - }); + await page.getByTestId('git-dropdown').click(); + await page.getByRole('menuitemradio', { name: 'Branches' }).click(); + await page.getByRole('textbox', { name: 'New branch name:' }).click(); + await page.getByRole('textbox', { name: 'New branch name:' }).fill('branch1'); + await page.getByRole('button', { name: 'Create', exact: true }).click(); + await expect.soft(page.getByText('branch1 *')).toBeVisible(); + await page.getByTestId('close-git-project-branches-modal').click(); + await page.getByTestId('git-project-branches-modal-overlay').waitFor({ state: 'hidden' }); + await page.getByRole('button', { name: 'New request collection' }).click(); + await page.getByRole('textbox', { name: 'Name', exact: true }).click(); + await page.getByRole('textbox', { name: 'Name', exact: true }).press('ControlOrMeta+a'); + await page.getByRole('textbox', { name: 'Name', exact: true }).fill('collection 1'); + await page.getByRole('textbox', { name: 'File name my_collection' }).click(); + await page.getByRole('textbox', { name: 'File name my_collection' }).press('ControlOrMeta+a'); + await page.getByRole('textbox', { name: 'File name my_collection' }).fill('collection_1'); + await page.getByRole('button', { name: 'Create', exact: true }).click(); + await page.getByTestId('project').click(); + await page.getByTestId('git-dropdown').click(); + await page.getByRole('menuitemradio', { name: 'Commit' }).click(); + await page.locator('button[name="Stage all changes"]').click(); + await page.getByRole('textbox', { name: 'Message' }).click(); + await page.getByRole('textbox', { name: 'Message' }).fill('commit 1'); + await page.getByRole('button', { name: 'Commit', exact: true }).click(); + await page.getByTestId('git-dropdown').click(); + await page.getByRole('menuitemradio', { name: 'master' }).click(); + await page.locator('html').click(); + await page.getByTestId('git-dropdown').click(); + await page.getByRole('menuitemradio', { name: 'Branches' }).click(); + await page.getByLabel('branch1').getByRole('button', { name: 'Merge' }).click(); + await page.getByRole('button', { name: 'ïª Confirm' }).click(); + await page.getByTestId('close-git-project-branches-modal').click(); + await page.getByTestId('git-project-branches-modal-overlay').waitFor({ state: 'hidden' }); + await expect.soft(page.getByText('collection 1')).toBeVisible(); }); }); + +async function addAccessTokenGitCredential(insomnia: InsomniaApp) { + await insomnia.statusbar.openPreferences(); + await insomnia.preferencesPage.switchToPreferenceTab('Credentials'); + await insomnia.preferencesPage.credentialsTab.addAccessTokenGitCredential(); + await expect.soft(insomnia.page.getByRole('row', { name: 'Custom Git Credential' })).toBeVisible(); + await insomnia.preferencesPage.closePreferences(); +} diff --git a/packages/insomnia-smoke-test/tests/smoke/pre-request-script-features.test.ts b/packages/insomnia-smoke-test/tests/smoke/pre-request-script-features.test.ts index 2f7a958f6972..582ae61c69d0 100644 --- a/packages/insomnia-smoke-test/tests/smoke/pre-request-script-features.test.ts +++ b/packages/insomnia-smoke-test/tests/smoke/pre-request-script-features.test.ts @@ -208,6 +208,7 @@ test.describe('pre-request features tests', () => { }), }; }); + test('run test cases', async ({ page }) => { for (const tc of testCases) { console.log(`Running test case: ${tc.name}`); @@ -230,6 +231,7 @@ test.describe('pre-request features tests', () => { tc.customVerify(bodyJson); } }); + test('send request with content type', async ({ page }) => { await page.getByTestId('settings-button').click(); await page.getByTestId('dataFolders').click(); @@ -660,14 +662,17 @@ test.describe('unhappy paths', () => { await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click(); // verify - await expect.soft(page.getByTestId('response-pane')).toContainText('my custom error'); + await expect + .soft(page.getByTestId('response-pane')) + .toContainText(`my custom error`); await page.getByRole('tab', { name: 'Scripts' }).click(); await page.getByTestId('CodeEditor').getByRole('textbox').press('ControlOrMeta+a'); await page.keyboard.press('Backspace'); await editor.fill(`insomnia.INVALID_FIELD.set('', '')`); - await page.getByRole('tab', { name: 'Body' }).click(); + // CodeMirror debounces onChange by DEBOUNCE_MILLIS (100ms). + await page.evaluate(() => new Promise(resolve => setTimeout(resolve, 150))); // send await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click(); @@ -678,3 +683,174 @@ test.describe('unhappy paths', () => { .toContainText(`Cannot read properties of undefined (reading 'set')`); }); }); + +test.describe('sandbox features', () => { + test.slow(process.platform === 'darwin' || process.platform === 'win32', 'Slow app start on these platforms'); + + test.beforeEach(async ({ app, page }) => { + const text = await loadFixture('pre-request-collection.yaml'); + await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text); + + await page.getByLabel('Import').click(); + await page.locator('[data-test-id="import-from-clipboard"]').click(); + await page.getByRole('button', { name: 'Scan' }).click(); + await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click(); + }); + + // Blocked Roots / Scopes group: 'this' is blocked. + test('blocked roots / scopes group', async ({ page }) => { + await page.getByLabel('Request Collection').getByTestId('echo pre-request script result').press('Enter'); + + + await page.getByRole('tab', { name: 'Scripts' }).click(); + const editor = page.getByTestId('CodeEditor').getByRole('textbox'); + + // enter script that accesses a property on 'this'. + await editor.fill(`insomnia.environment.set('result', String(this?.process));`); + + // CodeMirror debounces onChange by DEBOUNCE_MILLIS (100ms). + await page.evaluate(() => new Promise(resolve => setTimeout(resolve, 150))); + + await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click(); + + // verify blocked-root error + await expect + .soft(page.getByTestId('response-pane')) + .toContainText("The script was blocked because it used 'this'."); + + // navigate to Settings → Scripting, disable the "Scopes" blocked roots group + await page.getByTestId('settings-button').click(); + await page.locator('text=Insomnia Preferences').first().click(); + await page.getByRole('tab', { name: 'Scripting' }).click(); + const scopesSwitch = page.locator('div:has(> h4:has-text("Scopes")) label[data-react-aria-pressable]'); + await scopesSwitch.scrollIntoViewIfNeeded(); + await scopesSwitch.click(); + + await page.locator('.app').press('Escape'); + + // re-send — no sandbox error; this === undefined. + await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click(); + await expect + .soft(page.getByTestId('response-pane')) + .not.toContainText("The script was blocked because it used 'this'."); + await expect + .soft(page.locator('[data-testid="response-status-tag"]:visible')) + .toContainText('200 OK'); + }); + + // Blocked Properties / Prototype Mutation group: 'prototype' is blocked. + test('blocked properties / prototype mutation group', async ({ page }) => { + await page.getByLabel('Request Collection').getByTestId('echo pre-request script result').press('Enter'); + + // enter script that accesses Object.prototype. + await page.getByRole('tab', { name: 'Scripts' }).click(); + const editor = page.getByTestId('CodeEditor').getByRole('textbox'); + await editor.fill(`insomnia.environment.set('result', typeof Object.prototype.toString);`); + + // CodeMirror debounces onChange by DEBOUNCE_MILLIS (100ms). + await page.evaluate(() => new Promise(resolve => setTimeout(resolve, 150))); + + await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click(); + + // verify blocked-property error + await expect + .soft(page.getByTestId('response-pane')) + .toContainText("The script was blocked because it used the property 'prototype'."); + + // navigate to Settings → Scripting, disable the "Prototype Mutation" blocked properties group + await page.getByTestId('settings-button').click(); + await page.locator('text=Insomnia Preferences').first().click(); + await page.getByRole('tab', { name: 'Scripting' }).click(); + const protoMutationSwitch = page.locator('div:has(> h4:has-text("Prototype Mutation")) label[data-react-aria-pressable]'); + await protoMutationSwitch.scrollIntoViewIfNeeded(); + await protoMutationSwitch.click(); + await expect.soft(protoMutationSwitch).not.toHaveAttribute('data-selected'); + await page.locator('.app').press('Escape'); + + // re-send — prototype access now allowed; Object.prototype.toString is a function + await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click(); + await expect + .soft(page.getByTestId('response-pane')) + .not.toContainText("The script was blocked because it used the property 'prototype'."); + await expect + .soft(page.locator('[data-testid="response-status-tag"]:visible')) + .toContainText('200 OK'); + }); + + // Mask Rules / Runtime APIs group: 'Function' is masked to undefined at runtime. + test('Mask Rules / Runtime APIs group.', async ({ page }) => { + await page.getByLabel('Request Collection').getByTestId('echo pre-request script result').press('Enter'); + + // enter script that uses the Function constructor, only masked at runtime. + await page.getByRole('tab', { name: 'Scripts' }).click(); + const editor = page.getByTestId('CodeEditor').getByRole('textbox'); + await editor.fill(`const f = new Function('return 42'); insomnia.environment.set('result', f());`); + + // CodeMirror debounces onChange by DEBOUNCE_MILLIS (100ms). + await page.evaluate(() => new Promise(resolve => setTimeout(resolve, 150))); + + // send — Function masked to undefined → V8 uses the identifier name: "Function is not a constructor" + await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click(); + + await expect + .soft(page.getByTestId('response-pane')) + .toContainText('Function is not a constructor'); + + // navigate to Settings → Scripting, disable the "Runtime APIs" mask group + await page.getByTestId('settings-button').click(); + await page.locator('text=Insomnia Preferences').first().click(); + await page.getByRole('tab', { name: 'Scripting' }).click(); + const runtimeApisSwitch = page.locator('div:has(> h4:has-text("Runtime APIs")) label[data-react-aria-pressable]'); + await runtimeApisSwitch.scrollIntoViewIfNeeded(); + await runtimeApisSwitch.click(); + await expect.soft(runtimeApisSwitch).not.toHaveAttribute('data-selected'); + await page.locator('.app').press('Escape'); + + // re-send — Function is now the real constructor; script returns 42 + await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click(); + await expect + .soft(page.getByTestId('response-pane')) + .not.toContainText('Function is not a constructor'); + await expect + .soft(page.locator('[data-testid="response-status-tag"]:visible')) + .toContainText('200 OK'); + }); + + test('Layered security / unblocked properties resolve undefined', async ({ page }) => { + await page.getByLabel('Request Collection').getByTestId('echo pre-request script result').press('Enter'); + + // enter script that accesses a property on 'process'. + await page.getByRole('tab', { name: 'Scripts' }).click(); + const editor = page.getByTestId('CodeEditor').getByRole('textbox'); + await editor.fill(`insomnia.environment.set('result', String(process?.version));`); + + // CodeMirror debounces onChange by DEBOUNCE_MILLIS (100ms). + await page.evaluate(() => new Promise(resolve => setTimeout(resolve, 150))); + + await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click(); + + // verify blocked-root error + await expect + .soft(page.getByTestId('response-pane')) + .toContainText("The script was blocked because it used 'process'."); + + // navigate to Settings → Scripting, disable only the "Node.js Internals" BLOCKED ROOTS group. + await page.getByTestId('settings-button').click(); + await page.locator('text=Insomnia Preferences').first().click(); + await page.getByRole('tab', { name: 'Scripting' }).click(); + const nodeInternalsSwitch = page.locator('xpath=//h4[normalize-space(text())="Node.js Internals"]/following-sibling::div[1]//label[@data-react-aria-pressable]'); + await nodeInternalsSwitch.scrollIntoViewIfNeeded(); + await nodeInternalsSwitch.click(); + await expect.soft(nodeInternalsSwitch).not.toHaveAttribute('data-selected'); + await page.locator('.app').press('Escape'); + + // process?.version === undefined. + await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click(); + await expect + .soft(page.getByTestId('response-pane')) + .not.toContainText("The script was blocked because it used 'process'."); + await expect + .soft(page.locator('[data-testid="response-status-tag"]:visible')) + .toContainText('200 OK'); + }); +}); diff --git a/packages/insomnia-testing/package.json b/packages/insomnia-testing/package.json index d767e046ef59..36461aa0f1c2 100644 --- a/packages/insomnia-testing/package.json +++ b/packages/insomnia-testing/package.json @@ -2,7 +2,7 @@ "private": true, "name": "insomnia-testing", "license": "Apache-2.0", - "version": "12.5.1-alpha.0", + "version": "12.6.0-beta.0", "author": "Kong ", "repository": { "type": "git", diff --git a/packages/insomnia/config/renderer-node-import-baseline.json b/packages/insomnia/config/renderer-node-import-baseline.json index 935a32e7648a..5dfb9700f07e 100644 --- a/packages/insomnia/config/renderer-node-import-baseline.json +++ b/packages/insomnia/config/renderer-node-import-baseline.json @@ -92,6 +92,18 @@ "importer": "src/script-executor.ts", "builtin": "fs/promises" }, + { + "importer": "src/scripting/require-interceptor.ts", + "builtin": "buffer" + }, + { + "importer": "src/scripting/require-interceptor.ts", + "builtin": "timers" + }, + { + "importer": "src/scripting/require-interceptor.ts", + "builtin": "util" + }, { "importer": "src/templating/base-extension.ts", "builtin": "crypto" diff --git a/packages/insomnia/package.json b/packages/insomnia/package.json index 269a765447d1..9ecebaf9e9b2 100644 --- a/packages/insomnia/package.json +++ b/packages/insomnia/package.json @@ -1,6 +1,6 @@ { "name": "insomnia", - "version": "12.5.1-alpha.0", + "version": "12.6.0-beta.0", "productName": "Insomnia", "private": true, "description": "The Collaborative API Design Tool", @@ -73,6 +73,8 @@ "@tailwindcss/typography": "^0.5.16", "@tanstack/react-virtual": "3.13.12", "@xmldom/xmldom": "^0.9.8", + "acorn": "^8.16.0", + "acorn-walk": "^8.3.5", "ajv": "^8.17.1", "apiconnect-wsdl": "2.0.36", "aws4": "^1.13.2", diff --git a/packages/insomnia/src/account/session.ts b/packages/insomnia/src/account/session.ts index 838ccf5c5294..3b9a7f9c585b 100644 --- a/packages/insomnia/src/account/session.ts +++ b/packages/insomnia/src/account/session.ts @@ -225,7 +225,8 @@ async function _removeAllCredentials() { * */ async function _removeGitRepository(repo: GitRepository) { - const projects = await database.find(models.project.type, { gitRepositoryId: repo._id }); + const queryIds = models.project.getQueryableGitRepositoryIds(repo._id); + const projects = await database.find(models.project.type, { gitRepositoryId: { $in: queryIds } }); for (const p of projects) { await services.project.update(p, { gitRepositoryId: models.project.EMPTY_GIT_PROJECT_ID }); } diff --git a/packages/insomnia/src/common/settings.ts b/packages/insomnia/src/common/settings.ts index 0f8c716c471b..6ded268f7016 100644 --- a/packages/insomnia/src/common/settings.ts +++ b/packages/insomnia/src/common/settings.ts @@ -163,6 +163,16 @@ export interface Settings { saveVaultKeyToOSSecretManager: boolean; vaultSecretCacheDuration: number; dataFolders: string[]; + // AST and shadowing check. + scriptSandboxEnabled: boolean; + // Wraps the user script in 'use strict', preventing accidental globals and making `this` undefined. + scriptStrictModeEnabled: boolean; + // Names of security rules that have been individually disabled. + disabledSecurityRules: string[]; + // AST blocked-property names that have been individually disabled. + disabledBlockedProperties: string[]; + // AST blocked-root names that have been individually disabled. + disabledBlockedRoots: string[]; /** Custom npm registry URL for plugin installation (e.g., corporate mirror). Empty string uses the default https://registry.npmjs.org/. */ npmRegistryUrl: string; } diff --git a/packages/insomnia/src/entry.hidden-window-preload.ts b/packages/insomnia/src/entry.hidden-window-preload.ts index 49aa39f3cbc0..b0ff3265673a 100644 --- a/packages/insomnia/src/entry.hidden-window-preload.ts +++ b/packages/insomnia/src/entry.hidden-window-preload.ts @@ -13,7 +13,7 @@ import { stopMonitorAsyncTasks, } from '../../insomnia-scripting-environment/src/objects'; // this will also import lots of node_modules into the preload script, consider moving this file insomnia-scripting-environment -import { requireInterceptor } from './require-interceptor'; +import { requireInterceptor } from './scripting/require-interceptor'; export interface HiddenBrowserWindowToMainBridgeAPI { requireInterceptor: (module: string) => any; diff --git a/packages/insomnia/src/entry.hidden-window.ts b/packages/insomnia/src/entry.hidden-window.ts index 08521a8e3fad..64a305301edb 100644 --- a/packages/insomnia/src/entry.hidden-window.ts +++ b/packages/insomnia/src/entry.hidden-window.ts @@ -1,23 +1,12 @@ import * as Sentry from '@sentry/electron/renderer'; -import * as _ from 'es-toolkit/compat'; import { SENTRY_OPTIONS } from 'insomnia/src/common/sentry'; -import { - initInsomniaObject, - InsomniaObject, - waitForAllTestsDone, -} from '../../insomnia-scripting-environment/src/objects'; -import { - getNewConsole, - mergeClientCertificates, - mergeCookieJar, - mergeRequests, - mergeSettings, - type RequestContext, -} from '../../insomnia-scripting-environment/src/objects'; +import type { RequestContext } from '../../insomnia-scripting-environment/src/objects'; +import { runScript } from './scripting/run-script'; +import { type ScriptSecurityPolicy } from './scripting/sandbox'; export interface HiddenBrowserWindowBridgeAPI { - runScript: (options: { script: string; context: RequestContext }) => Promise; + runScript: (options: { script: string; context: RequestContext; securityPolicy?: ScriptSecurityPolicy }) => Promise; } Sentry.init({ @@ -38,14 +27,17 @@ window.bridge.onmessage( const result = await window.bridge.Promise.race([timeoutPromise, runScript(data)]); callback(result); } catch (err) { - const errMessage = err.message - ? `Error from Pre-request or after-response script: - -${err.message}` - : err; - const fullErrMessage = `${errMessage} - -${err.stack ? `Stack: ${err.stack}` : ''}`; + const error = err instanceof Error ? err : new Error(String(err)); + if ((error as NodeJS.ErrnoException).code === 'SECURITY_POLICY_VIOLATION') { + console.log('[hidden-window] security policy violation:', error.message); + callback({ error: error.message }); + return; + } + const errMessage = error.message + ? `Error from Pre-request or after-response script:\n${error.message}` + : String(error); + const fullErrMessage = `${errMessage}\n\n${error.stack ? `Stack: ${error.stack}` : ''}`; + console.log('[hidden-window] script error:', errMessage); Sentry.captureException(errMessage, { tags: { source: 'hidden-window', @@ -57,112 +49,3 @@ ${err.stack ? `Stack: ${err.stack}` : ''}`; } }, ); - -// This function is duplicated in scriptExecutor.ts to run in nodejs -// TODO: consider removing this implementation and using only nodejs scripting -const runScript = async ({ script, context }: { script: string; context: RequestContext }): Promise => { - const scriptConsole = getNewConsole(); - - const executionContext = await initInsomniaObject(context, scriptConsole.log); - - const AsyncFunction = (async () => {}).constructor; - const executeScript = AsyncFunction( - 'insomnia', - 'require', - 'console', - '_', - 'setTimeout', - // disable these as they are not supported in web or existing implementation - 'setImmediate', - 'queueMicrotask', - 'process', - 'waitForAllTestsDone', - ` - const $ = insomnia; - window.bridge.resetAsyncTasks(); // exclude unnecessary ones - ${script}; - await waitForAllTestsDone(); - window.bridge.stopMonitorAsyncTasks(); // the next one should not be monitored - await window.bridge.asyncTasksAllSettled(); - return insomnia;`, - ); - - const mutatedInsomniaObject = await executeScript( - executionContext, - window.bridge.requireInterceptor, - scriptConsole, - _, - proxiedSetTimeout, - undefined, - undefined, - undefined, - waitForAllTestsDone, - ); - if (mutatedInsomniaObject == null || !(mutatedInsomniaObject instanceof InsomniaObject)) { - throw new Error('insomnia object is invalid or script returns earlier than expected.'); - } - const mutatedContextObject = mutatedInsomniaObject.toObject(); - const updatedRequest = mergeRequests(context.request, mutatedContextObject.request); - const updatedSettings = mergeSettings(context.settings, mutatedContextObject.request); - const updatedCertificates = mergeClientCertificates( - mutatedContextObject.clientCertificates, - mutatedContextObject.request, - ); - const updatedCookieJar = mergeCookieJar(context.cookieJar, mutatedContextObject.cookieJar); - - return { - ...context, - environment: { - id: context.environment.id, - name: context.environment.name, - data: mutatedContextObject.environment, - }, - baseEnvironment: { - id: context.baseEnvironment.id, - name: context.baseEnvironment.name, - data: mutatedContextObject.baseEnvironment, - }, - iterationData: context.iterationData - ? { - name: context.iterationData.name, - data: mutatedContextObject.iterationData, - } - : undefined, - transientVariables: { - name: context.transientVariables?.name || 'transientVariables', - data: mutatedContextObject.variables, - }, - request: updatedRequest, - execution: mutatedContextObject.execution, - settings: updatedSettings, - clientCertificates: updatedCertificates, - cookieJar: updatedCookieJar, - globals: context.globals && { - id: context.globals.id, - name: context.globals.name, - data: mutatedContextObject.globals, - }, - baseGlobals: context.baseGlobals && { - id: context.baseGlobals.id, - name: context.baseGlobals.name, - data: mutatedContextObject.baseGlobals, - }, - requestTestResults: mutatedContextObject.requestTestResults, - logs: scriptConsole.dumpLogsAsArray(), - parentFolders: mutatedContextObject.parentFolders, - }; -}; - -// proxiedSetTimeout has to be here as callback could be an async task -function proxiedSetTimeout(callback: () => void, ms?: number | undefined) { - let resolveHdl: (value: unknown) => void; - - new Promise(resolve => { - resolveHdl = resolve; - }); - - return setTimeout(() => { - callback(); - resolveHdl(null); - }, ms); -} diff --git a/packages/insomnia/src/entry.preload.ts b/packages/insomnia/src/entry.preload.ts index af3298e0a260..a4319e6c75e0 100644 --- a/packages/insomnia/src/entry.preload.ts +++ b/packages/insomnia/src/entry.preload.ts @@ -196,7 +196,7 @@ const git: GitServiceAPI = { pullFromGitRemote: options => invokeWithNormalizedError('git.pullFromGitRemote', options), continueMerge: options => invokeWithNormalizedError('git.continueMerge', options), discardChanges: options => invokeWithNormalizedError('git.discardChanges', options), - abortMerge: () => invokeWithNormalizedError('git.abortMerge'), + abortMerge: options => invokeWithNormalizedError('git.abortMerge', options), gitStatus: options => invokeWithNormalizedError('git.gitStatus', options), diff: () => invokeWithNormalizedError('git.diff'), multipleCommitToGitRepo: options => invokeWithNormalizedError('git.multipleCommitToGitRepo', options), @@ -265,7 +265,8 @@ const main: Window['main'] = { requestId: string, authentication: AuthTypeOAuth2, forceRefresh?: boolean, - ): Promise => invokeWithNormalizedError('getOAuth2Token', requestId, authentication, forceRefresh), + ): Promise => + invokeWithNormalizedError('getOAuth2Token', requestId, authentication, forceRefresh), insecureReadFile: options => invokeWithNormalizedError('insecureReadFile', options), insecureReadFileWithEncoding: options => invokeWithNormalizedError('insecureReadFileWithEncoding', options), secureReadFile: options => invokeWithNormalizedError('secureReadFile', options), diff --git a/packages/insomnia/src/insomnia-data/node-src/services/project.ts b/packages/insomnia/src/insomnia-data/node-src/services/project.ts index 8deb40117570..41d57097ceb2 100644 --- a/packages/insomnia/src/insomnia-data/node-src/services/project.ts +++ b/packages/insomnia/src/insomnia-data/node-src/services/project.ts @@ -16,8 +16,9 @@ export function getByRemoteId(remoteId: string) { } export function getAllByGitRepositoryIds(gitRepositoryIds: string[]) { + const queryIds = gitRepositoryIds.flatMap(id => models.project.getQueryableGitRepositoryIds(id)); return db.find(type, { - gitRepositoryId: { $in: gitRepositoryIds }, + gitRepositoryId: { $in: queryIds }, }); } diff --git a/packages/insomnia/src/insomnia-data/src/models/project.ts b/packages/insomnia/src/insomnia-data/src/models/project.ts index b5692ca73f0f..3ca8ada262d8 100644 --- a/packages/insomnia/src/insomnia-data/src/models/project.ts +++ b/packages/insomnia/src/insomnia-data/src/models/project.ts @@ -15,10 +15,69 @@ export const SCRATCHPAD_PROJECT_ID = `${prefix}_scratchpad`; // This is used to identify Git Projects that are not connected to a remote yet export const EMPTY_GIT_PROJECT_ID = 'empty'; +// Prefix used when encoding a GitRepository._id into gitRepositoryId for downgrade protection. +// The real GitRepository doc has _id = 'git_xxx'. We store 'gr_xxx' on the project so that the +// old app's getById('gr_xxx') returns null — preventing it from touching the git folder. +// The new app swaps the prefix back to recover the real ID. +export const PROTECTED_GIT_REPO_PREFIX = 'gr_'; +const REAL_GIT_REPO_PREFIX = 'git_'; + +/** + * Decode a raw gitRepositoryId string to the real GitRepository._id. + * Handles both the protected ('gr_xxx') and legacy ('git_xxx') formats. + */ +export function decodeRepoId(id: string): string { + if (id.startsWith(PROTECTED_GIT_REPO_PREFIX)) { + return REAL_GIT_REPO_PREFIX + id.slice(PROTECTED_GIT_REPO_PREFIX.length); + } + return id; +} + +/** + * Given a connected GitProject, return the real GitRepository._id. + * Returns null when the project is not connected (gitRepositoryId is 'empty'). + * + * Handles two formats: + * - 'gr_xxx' → protected (new format) → returns 'git_xxx' + * - 'git_xxx' → legacy (pre-migration) → returns 'git_xxx' as-is + */ +export function getEffectiveRepoId(project: GitProject): string | null { + const id = project.gitRepositoryId; + if (id === EMPTY_GIT_PROJECT_ID) return null; + return decodeRepoId(id); +} + +/** + * Encode a real GitRepository._id ('git_xxx') into the protected format ('gr_xxx') + * that is stored on the project's gitRepositoryId field. + */ +export function toProtectedRepoId(gitRepositoryId: string): string { + if (gitRepositoryId.startsWith(REAL_GIT_REPO_PREFIX)) { + return PROTECTED_GIT_REPO_PREFIX + gitRepositoryId.slice(REAL_GIT_REPO_PREFIX.length); + } + return gitRepositoryId; // already protected or unexpected format — pass through +} + +/** + * Return all values that may be stored in Project.gitRepositoryId for a given real + * GitRepository._id, covering both legacy ('git_xxx') and protected ('gr_xxx') forms. + * Use this when building DB queries that must match projects regardless of which + * storage format they were written with. + */ +export function getQueryableGitRepositoryIds(gitRepositoryId: string): string[] { + const realId = decodeRepoId(gitRepositoryId); + const protectedId = toProtectedRepoId(realId); + return Array.from(new Set([realId, protectedId])); +} + export function isEmptyGitProject(project: Project) { return project.gitRepositoryId === EMPTY_GIT_PROJECT_ID; } +export function isConnectedGitProject(project: Project): project is GitProject { + return isGitProject(project) && getEffectiveRepoId(project) !== null; +} + export const isScratchpadProject = (project: Pick) => project._id === SCRATCHPAD_PROJECT_ID; export const isLocalProject = (project: Pick): project is LocalProject => project.remoteId === null; diff --git a/packages/insomnia/src/insomnia-data/src/models/settings.ts b/packages/insomnia/src/insomnia-data/src/models/settings.ts index 7aa65edf1e92..393136f9d77f 100644 --- a/packages/insomnia/src/insomnia-data/src/models/settings.ts +++ b/packages/insomnia/src/insomnia-data/src/models/settings.ts @@ -76,6 +76,11 @@ export function init(): BaseSettings { // The duration in mins for which the external vault secret is cached vaultSecretCacheDuration: 30, dataFolders: [], + scriptSandboxEnabled: true, + scriptStrictModeEnabled: true, + disabledSecurityRules: [], + disabledBlockedProperties: [], + disabledBlockedRoots: [], npmRegistryUrl: '', }; } diff --git a/packages/insomnia/src/main/git-service.ts b/packages/insomnia/src/main/git-service.ts index 00934e90893c..732d630f184c 100644 --- a/packages/insomnia/src/main/git-service.ts +++ b/packages/insomnia/src/main/git-service.ts @@ -61,7 +61,7 @@ import GitVCS, { import { MemClient } from '../sync/git/mem-client'; import { NeDBClient } from '../sync/git/ne-db-client'; import { projectRoutableFSClient } from '../sync/git/project-routable-fs-client'; -import { repoFileWatcherRegistry } from '../sync/git/repo-file-watcher'; +import { createElectronNotifier, RepoFileWatcherRegistry, type WatcherNotifier } from '../sync/git/repo-file-watcher'; import { routableFSClient } from '../sync/git/routable-fs-client'; import { shallowClone } from '../sync/git/shallow-clone'; import type { AutoResolvedConflict, MergeConflict } from '../sync/types'; @@ -72,6 +72,35 @@ import { ipcMainHandle } from './ipc/electron'; // Initialize Git Remote Providers on module load initializeGitRemoteProviders(); +/** + * Set of repo IDs for which conflict problems should be suppressed in the + * file-problems-changed IPC broadcast. Active while the user is resolving + * conflicts via SyncMergeModal so the generic "CLI conflict" blocking modal + * does not appear on top of the interactive resolver. + */ +const suppressedConflictRepos = new Set(); + +const _electronNotifier = createElectronNotifier(); +const conflictFilteringNotifier: WatcherNotifier = { + onDbSynced: () => _electronNotifier.onDbSynced(), + onProblemsChanged: payload => { + _electronNotifier.onProblemsChanged({ + ...payload, + conflictsSuppressed: suppressedConflictRepos.has(payload.repoId), + }); + }, +}; + +const repoFileWatcherRegistry = new RepoFileWatcherRegistry(conflictFilteringNotifier); + +function suppressConflictProblems(repoId: string): void { + suppressedConflictRepos.add(repoId); +} + +function clearConflictSuppression(repoId: string): void { + suppressedConflictRepos.delete(repoId); +} + type PushPull = 'push' | 'pull'; type VCSAction = | PushPull @@ -189,12 +218,10 @@ async function getGitRepository({ projectId, workspaceId }: { projectId: string; invariant(projectId, 'Project ID is required'); const project = await services.project.getById(projectId); invariant(project, 'Project not found'); - invariant(project.gitRepositoryId, 'Project is not linked to a git repository'); - invariant( - project.gitRepositoryId && !models.project.isEmptyGitProject(project), - 'Project is not linked to a git repository', - ); - const gitRepository = await services.gitRepository.getById(project.gitRepositoryId); + invariant(models.project.isConnectedGitProject(project), 'Project is not linked to a git repository'); + const repoId = models.project.getEffectiveRepoId(project); + invariant(repoId, 'Project is not linked to a git repository'); + const gitRepository = await services.gitRepository.getById(repoId); invariant(gitRepository, 'Git Repository not found'); return gitRepository; } @@ -264,22 +291,18 @@ export async function getProjectGitFileIssues({ gitRepositoryId, }: GetProjectGitFileIssuesOptions): Promise { const project = await services.project.getById(projectId); - if ( - !project || - !models.project.isGitProject(project) || - !project.gitRepositoryId || - models.project.isEmptyGitProject(project) - ) { + if (!project || !models.project.isConnectedGitProject(project)) { return []; } - if (gitRepositoryId && gitRepositoryId !== project.gitRepositoryId) { + const effectiveRepoId = models.project.getEffectiveRepoId(project); + if (gitRepositoryId && gitRepositoryId !== effectiveRepoId) { return []; } return mapWorkspaceFileIssues({ - issues: repoFileWatcherRegistry.getProblems(project.gitRepositoryId), - repoId: project.gitRepositoryId, + issues: repoFileWatcherRegistry.getProblems(effectiveRepoId!), + repoId: effectiveRepoId!, metas: await getProjectWorkspacesWithMeta(projectId), workspaceId, }); @@ -1139,7 +1162,7 @@ export const cloneGitRepoAction = async ({ await services.project.update(project, { remoteId: null, - gitRepositoryId: gitRepository._id, + gitRepositoryId: models.project.toProtectedRepoId(gitRepository._id), }); return project; @@ -1148,7 +1171,7 @@ export const cloneGitRepoAction = async ({ const project = await services.project.create({ name: name || gitRepository.uri.split('/').pop() || 'New Git Project', parentId: organizationId, - gitRepositoryId: gitRepository._id, + gitRepositoryId: models.project.toProtectedRepoId(gitRepository._id), }); return project; @@ -1454,7 +1477,8 @@ export const updateGitRepoAction = async ({ let gitRepository: GitRepository | undefined; if (gitRepositoryId && gitRepositoryId !== models.project.EMPTY_GIT_PROJECT_ID) { - gitRepository = await services.gitRepository.getById(gitRepositoryId); + const effectiveId = models.project.decodeRepoId(gitRepositoryId); + gitRepository = await services.gitRepository.getById(effectiveId); invariant(gitRepository, 'GitRepository not found'); } else { const newRepo: Partial = { @@ -1476,7 +1500,7 @@ export const updateGitRepoAction = async ({ const project = await services.project.getById(projectId); invariant(project, 'Project not found'); await services.project.update(project, { - gitRepositoryId: gitRepository._id, + gitRepositoryId: models.project.toProtectedRepoId(gitRepository._id), }); } @@ -1543,6 +1567,7 @@ export const resetGitRepoAction = async ({ projectId, workspaceId }: { projectId await services.gitRepository.remove(repo); // Stop the file watcher for this repository (project-scoped flow only). repoFileWatcherRegistry.stopWatcher(repo._id); + clearConflictSuppression(repo._id); await database.flushChanges(flushId); @@ -1982,6 +2007,7 @@ export const mergeGitBranch = async ({ const bufferId = await database.bufferChanges(); try { + suppressConflictProblems(gitRepository._id); await GitVCS.merge({ theirsBranch, allowUncommittedChangesBeforeMerge, @@ -1993,6 +2019,7 @@ export const mergeGitBranch = async ({ // Import all YAML files from disk into the DB after merge + checkout const gitRepoId = gitRepository._id; await repoFileWatcherRegistry.importAllFiles(gitRepoId); + clearConflictSuppression(gitRepository._id); trackSegmentEvent(SegmentEvent.vcsAction, { ...vcsSegmentEventProperties('git', 'merge_branch'), @@ -2013,8 +2040,10 @@ export const mergeGitBranch = async ({ return {}; } catch (err) { if (err instanceof MergeConflictError) { + // Keep suppression active — user will resolve via SyncMergeModal. return err.data; } + clearConflictSuppression(gitRepository._id); let errorMessage = getErrorMessage(err); if (err instanceof Errors.HttpError) { @@ -2231,9 +2260,12 @@ export async function fetchGitRemoteBranches({ } export async function pullFromGitRemote({ projectId, workspaceId }: { projectId: string; workspaceId?: string }) { + let repoId: string | null = null; try { await assertBranchOnOrigin('pull'); const gitRepository = await getGitRepository({ projectId, workspaceId }); + repoId = gitRepository._id; + suppressConflictProblems(repoId); invariant(gitRepository.credentialsId, 'Git Credentials ID is required'); const credentials = await services.gitCredentials.getById(gitRepository.credentialsId); invariant(credentials, 'Git Credentials not found'); @@ -2243,6 +2275,7 @@ export async function pullFromGitRemote({ projectId, workspaceId }: { projectId: // Import all YAML files from disk into the DB after pull await repoFileWatcherRegistry.importAllFiles(gitRepository._id); + clearConflictSuppression(repoId); trackSegmentEvent(SegmentEvent.vcsAction, { ...vcsSegmentEventProperties('git', 'pull'), @@ -2266,9 +2299,13 @@ export async function pullFromGitRemote({ projectId, workspaceId }: { projectId: }; } catch (err: unknown) { if (err instanceof MergeConflictError) { + // Keep suppression active — user will resolve via SyncMergeModal. + // clearConflictSuppression is called by continueMerge or abortMergeAction. return err.data; } + if (repoId) clearConflictSuppression(repoId); + if ( err instanceof Errors.UserCanceledError || (err instanceof Errors.HttpError && (err.data.statusCode === 401 || err.data.statusCode === 403)) @@ -2334,6 +2371,9 @@ export const continueMerge = async ({ // Import all YAML files from disk into the DB after merge resolution await repoFileWatcherRegistry.importAllFiles(gitRepository._id); + // Files are clean now — lift the conflict suppression so any remaining + // issues (parse errors etc.) are reported normally. + clearConflictSuppression(gitRepository._id); const log = (await GitVCS.log({ depth: 1 })) || []; @@ -2414,7 +2454,9 @@ export const discardChangesAction = async ({ } }; -export const abortMergeAction = async () => { +export const abortMergeAction = async ({ projectId, workspaceId }: { projectId: string; workspaceId?: string }) => { + const gitRepository = await getGitRepository({ projectId, workspaceId }); + clearConflictSuppression(gitRepository._id); return GitVCS.abortMerge(); }; @@ -2851,14 +2893,12 @@ export async function runAllGitRepoMigrations(): Promise { const failedProjects: { id: string; name: string }[] = []; const allProjects = await services.project.all(); - const gitProjects = allProjects.filter( - (p): p is GitProject => models.project.isGitProject(p) && !models.project.isEmptyGitProject(p), - ); + const gitProjects = allProjects.filter((p): p is GitProject => models.project.isConnectedGitProject(p)); if (gitProjects.length === 0) return { logs, failedProjects }; // Batch-fetch all git repositories in one query instead of N individual lookups. - const repoIds = gitProjects.map(p => p.gitRepositoryId); + const repoIds = gitProjects.map(p => models.project.getEffectiveRepoId(p)).filter(Boolean) as string[]; const gitRepositories = await database.find(models.gitRepository.type, { _id: { $in: repoIds }, }); @@ -2869,11 +2909,13 @@ export async function runAllGitRepoMigrations(): Promise { const ts = () => new Date().toISOString(); const projectList = gitProjects.map(p => `"${p.name}"`).join(', '); - logs.push(`${ts()} [INFO] Starting migration v${CURRENT_MIGRATION_VERSION} for ${gitProjects.length} repo(s): ${projectList}`); + logs.push( + `${ts()} [INFO] Starting migration v${CURRENT_MIGRATION_VERSION} for ${gitProjects.length} repo(s): ${projectList}`, + ); await Promise.all( gitProjects.map(async project => { - const gitRepository = repoById.get(project.gitRepositoryId); + const gitRepository = repoById.get(models.project.getEffectiveRepoId(project)!); if (!gitRepository) return; const repoId = gitRepository._id; @@ -2901,15 +2943,16 @@ export async function runAllGitRepoMigrations(): Promise { logs.push(`${ts()} [INFO] ["${name}"] Converting to local project`); try { const project = await services.project.getById(id); - if (!project || !project.gitRepositoryId) { + if (!project || !models.project.isConnectedGitProject(project)) { logs.push(`${ts()} [WARN] ["${name}"] Project not found or already local — skipping`); return; } - const gitRepository = await services.gitRepository.getById(project.gitRepositoryId); + const effectiveRepoId = models.project.getEffectiveRepoId(project as GitProject); + const gitRepository = effectiveRepoId ? await services.gitRepository.getById(effectiveRepoId) : null; if (gitRepository) { await services.gitRepository.remove(gitRepository); - logs.push(`${ts()} [INFO] ["${name}"] Removed git repository ${project.gitRepositoryId}`); + logs.push(`${ts()} [INFO] ["${name}"] Removed git repository ${effectiveRepoId}`); } await services.project.update(project, { name, gitRepositoryId: null }); @@ -3036,7 +3079,7 @@ export const registerGitServiceAPI = () => { ipcMainHandle('git.discardChanges', (_, options: Parameters[0]) => discardChangesAction(options), ); - ipcMainHandle('git.abortMerge', _ => abortMergeAction()); + ipcMainHandle('git.abortMerge', (_, options: Parameters[0]) => abortMergeAction(options)); ipcMainHandle('git.gitStatus', (_, options: Parameters[0]) => gitStatusAction(options)); ipcMainHandle('git.diff', () => diff()); ipcMainHandle('git.stageChanges', (_, options: Parameters[0]) => diff --git a/packages/insomnia/src/routes/git-migration.$.tsx b/packages/insomnia/src/routes/git-migration.$.tsx index 742f82bd4b33..93700a5d482d 100644 --- a/packages/insomnia/src/routes/git-migration.$.tsx +++ b/packages/insomnia/src/routes/git-migration.$.tsx @@ -3,9 +3,10 @@ import { Link } from 'react-router'; import { Button } from '~/basic-components/button'; import { CopyButton } from '~/ui/components/base/copy-button'; +import { Link as ExternalLink } from '~/ui/components/base/link'; import { InsomniaLogo } from '~/ui/components/insomnia-icon'; import { TrailLinesContainer } from '~/ui/components/trail-lines-container'; -import git_for_all from '~/ui/images/onboarding/git_for_all.png'; +import git_migration from '~/ui/images/git-migration/git.png'; type MigrationStatus = 'default' | 'running' | 'completed' | 'partiallyCompleted' | 'error'; @@ -73,15 +74,10 @@ const MigrationView = () => { <>

We hit an unexpected error while updating your file system. Please try again.

- If the issue persists, please{' '} - - raise a support ticket. - {' '} - You may also re-install the previous version by following the steps{' '} - - here - - . + Having trouble and need to contact us, or back up to an old version? See our{' '} + + docs. +

) : ( @@ -90,6 +86,13 @@ const MigrationView = () => { In order to continue with this update, we need to adjust your local file system. This is required to enable managing Insomnia changes using git on the CLI.

+

+ Note: This change is backwards compatible, but we strongly recommend{' '} + + following these best practices + {' '} + when returning to an earlier version of Insomnia. +

{isUpdateRunning ? 'Note: Your data is safe and the update only takes seconds.' @@ -101,7 +104,7 @@ const MigrationView = () => {

{isUpdateCompletedSuccessfully ? ( Open Insomnia @@ -117,7 +120,7 @@ const MigrationView = () => { Copy Error Logs Open Insomnia @@ -134,7 +137,7 @@ const MigrationView = () => { Copy Error Logs
) : ( diff --git a/packages/insomnia/src/ui/components/modals/git-project-staging-modal.tsx b/packages/insomnia/src/ui/components/modals/git-project-staging-modal.tsx index fed6d35181f2..e437fb140994 100644 --- a/packages/insomnia/src/ui/components/modals/git-project-staging-modal.tsx +++ b/packages/insomnia/src/ui/components/modals/git-project-staging-modal.tsx @@ -813,8 +813,8 @@ const ManualCommitForm: FC = ({ ) : null} -
-
+
+
Staged changes @@ -840,7 +840,7 @@ const ManualCommitForm: FC = ({ {changes.staged.length} -
+
= ({
-
+
Unstaged changes
@@ -959,7 +959,7 @@ const ManualCommitForm: FC = ({
-
+
= ({

You can now browse Git Sync project files on your local file system and manage changes using your normal Git workflows.{' '} - + Learn more ↗

@@ -1371,7 +1371,7 @@ const OriginalGitProjectStagingModal: FC<
)}
-
+
{isGenerateCommitMessagesWithAIEnabled && (

diff --git a/packages/insomnia/src/ui/components/modals/project-modal.tsx b/packages/insomnia/src/ui/components/modals/project-modal.tsx index c00f1238d421..93adddbbe1a3 100644 --- a/packages/insomnia/src/ui/components/modals/project-modal.tsx +++ b/packages/insomnia/src/ui/components/modals/project-modal.tsx @@ -64,6 +64,7 @@ export const ProjectModal = ({