mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-02-16 12:47:46 +01:00
Compare commits
16 Commits
v2025.5.5
...
v2025.6.0-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6666b9623 | ||
|
|
fa98351e30 | ||
|
|
3c8be3f5b9 | ||
|
|
eb3d1c409b | ||
|
|
46b049c72b | ||
|
|
fec64b5c02 | ||
|
|
8c3ed60579 | ||
|
|
907e09a417 | ||
|
|
28c6af8f94 | ||
|
|
f8b0510d08 | ||
|
|
5f99b7df05 | ||
|
|
158877b355 | ||
|
|
8b84545b67 | ||
|
|
0e28079965 | ||
|
|
5d5f9cc943 | ||
|
|
b71bc2cc92 |
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -62,7 +62,7 @@ jobs:
|
||||
|
||||
- name: install dependencies (windows only)
|
||||
if: matrix.platform == 'windows-latest'
|
||||
run: cargo install --force trusted-signing-cli --version 0.5.0
|
||||
run: cargo install --force trusted-signing-cli
|
||||
|
||||
- name: Install NPM Dependencies
|
||||
run: npm ci
|
||||
|
||||
337
package-lock.json
generated
337
package-lock.json
generated
@@ -58,7 +58,7 @@
|
||||
"@eslint/compat": "^1.3.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.29.0",
|
||||
"@tauri-apps/cli": "2.4.1",
|
||||
"@tauri-apps/cli": "^2.8.4",
|
||||
"@typescript-eslint/eslint-plugin": "^8.27.0",
|
||||
"@typescript-eslint/parser": "^8.27.0",
|
||||
"@yaakapp/cli": "^0.2.7",
|
||||
@@ -3081,9 +3081,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/api": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.6.0.tgz",
|
||||
"integrity": "sha512-hRNcdercfgpzgFrMXWwNDBN0B7vNzOzRepy6ZAmhxi5mDLVPNrTpo9MGg2tN/F7JRugj4d2aF7E1rtPXAHaetg==",
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.8.0.tgz",
|
||||
"integrity": "sha512-ga7zdhbS2GXOMTIZRT0mYjKJtR9fivsXzsyq5U3vjDL0s6DTMwYRm0UHNjzTY5dh4+LSC68Sm/7WEiimbQNYlw==",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@@ -3091,9 +3091,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.4.1.tgz",
|
||||
"integrity": "sha512-9Ta81jx9+57FhtU/mPIckDcOBtPTUdKM75t4+aA0X84b8Sclb0jy1xA8NplmcRzp2fsfIHNngU2NiRxsW5+yOQ==",
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.8.4.tgz",
|
||||
"integrity": "sha512-ejUZBzuQRcjFV+v/gdj/DcbyX/6T4unZQjMSBZwLzP/CymEjKcc2+Fc8xTORThebHDUvqoXMdsCZt8r+hyN15g==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"bin": {
|
||||
@@ -3107,23 +3107,23 @@
|
||||
"url": "https://opencollective.com/tauri"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tauri-apps/cli-darwin-arm64": "2.4.1",
|
||||
"@tauri-apps/cli-darwin-x64": "2.4.1",
|
||||
"@tauri-apps/cli-linux-arm-gnueabihf": "2.4.1",
|
||||
"@tauri-apps/cli-linux-arm64-gnu": "2.4.1",
|
||||
"@tauri-apps/cli-linux-arm64-musl": "2.4.1",
|
||||
"@tauri-apps/cli-linux-riscv64-gnu": "2.4.1",
|
||||
"@tauri-apps/cli-linux-x64-gnu": "2.4.1",
|
||||
"@tauri-apps/cli-linux-x64-musl": "2.4.1",
|
||||
"@tauri-apps/cli-win32-arm64-msvc": "2.4.1",
|
||||
"@tauri-apps/cli-win32-ia32-msvc": "2.4.1",
|
||||
"@tauri-apps/cli-win32-x64-msvc": "2.4.1"
|
||||
"@tauri-apps/cli-darwin-arm64": "2.8.4",
|
||||
"@tauri-apps/cli-darwin-x64": "2.8.4",
|
||||
"@tauri-apps/cli-linux-arm-gnueabihf": "2.8.4",
|
||||
"@tauri-apps/cli-linux-arm64-gnu": "2.8.4",
|
||||
"@tauri-apps/cli-linux-arm64-musl": "2.8.4",
|
||||
"@tauri-apps/cli-linux-riscv64-gnu": "2.8.4",
|
||||
"@tauri-apps/cli-linux-x64-gnu": "2.8.4",
|
||||
"@tauri-apps/cli-linux-x64-musl": "2.8.4",
|
||||
"@tauri-apps/cli-win32-arm64-msvc": "2.8.4",
|
||||
"@tauri-apps/cli-win32-ia32-msvc": "2.8.4",
|
||||
"@tauri-apps/cli-win32-x64-msvc": "2.8.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-darwin-arm64": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.4.1.tgz",
|
||||
"integrity": "sha512-QME7s8XQwy3LWClTVlIlwXVSLKkeJ/z88pr917Mtn9spYOjnBfsgHAgGdmpWD3NfJxjg7CtLbhH49DxoFL+hLg==",
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.8.4.tgz",
|
||||
"integrity": "sha512-BKu8HRkYV01SMTa7r4fLx+wjgtRK8Vep7lmBdHDioP6b8XH3q2KgsAyPWfEZaZIkZ2LY4SqqGARaE9oilNe0oA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -3138,9 +3138,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-darwin-x64": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.4.1.tgz",
|
||||
"integrity": "sha512-/r89IcW6Ya1sEsFUEH7wLNruDTj7WmDWKGpPy7gATFtQr5JEY4heernqE82isjTUimnHZD8SCr0jA3NceI4ybw==",
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.8.4.tgz",
|
||||
"integrity": "sha512-imb9PfSd/7G6VAO7v1bQ2A3ZH4NOCbhGJFLchxzepGcXf9NKkfun157JH9mko29K6sqAwuJ88qtzbKCbWJTH9g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3155,9 +3155,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-arm-gnueabihf": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.4.1.tgz",
|
||||
"integrity": "sha512-9tDijkRB+CchAGjXxYdY9l/XzFpLp1yihUtGXJz9eh+3qIoRI043n3e+6xmU8ZURr7XPnu+R4sCmXs6HD+NCEQ==",
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.8.4.tgz",
|
||||
"integrity": "sha512-Ml215UnDdl7/fpOrF1CNovym/KjtUbCuPgrcZ4IhqUCnhZdXuphud/JT3E8X97Y03TZ40Sjz8raXYI2ET0exzw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -3172,9 +3172,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-arm64-gnu": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.4.1.tgz",
|
||||
"integrity": "sha512-pnFGDEXBAzS4iDYAVxTRhAzNu3K2XPGflYyBc0czfHDBXopqRgMyj5Q9Wj7HAwv6cM8BqzXINxnb2ZJFGmbSgA==",
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.8.4.tgz",
|
||||
"integrity": "sha512-pbcgBpMyI90C83CxE5REZ9ODyIlmmAPkkJXtV398X3SgZEIYy5TACYqlyyv2z5yKgD8F8WH4/2fek7+jH+ZXAw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -3189,9 +3189,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-arm64-musl": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.1.tgz",
|
||||
"integrity": "sha512-Hp0zXgeZNKmT+eoJSCxSBUm2QndNuRxR55tmIeNm3vbyUMJN/49uW7nurZ5fBPsacN4Pzwlx1dIMK+Gnr9A69w==",
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.8.4.tgz",
|
||||
"integrity": "sha512-zumFeaU1Ws5Ay872FTyIm7z8kfzEHu8NcIn8M6TxbJs0a7GRV21KBdpW1zNj2qy7HynnpQCqjAYXTUUmm9JAOw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -3206,9 +3206,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-riscv64-gnu": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.4.1.tgz",
|
||||
"integrity": "sha512-3T3bo2E4fdYRvzcXheWUeQOVB+LunEEi92iPRgOyuSVexVE4cmHYl+MPJF+EUV28Et0hIVTsHibmDO0/04lAFg==",
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.8.4.tgz",
|
||||
"integrity": "sha512-qiqbB3Zz6IyO201f+1ojxLj65WYj8mixL5cOMo63nlg8CIzsP23cPYUrx1YaDPsCLszKZo7tVs14pc7BWf+/aQ==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -3223,9 +3223,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-x64-gnu": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.4.1.tgz",
|
||||
"integrity": "sha512-kLN0FdNONO+2i+OpU9+mm6oTGufRC00e197TtwjpC0N6K2K8130w7Q3FeODIM2CMyg0ov3tH+QWqKW7GNhHFzg==",
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.8.4.tgz",
|
||||
"integrity": "sha512-TaqaDd9Oy6k45Hotx3pOf+pkbsxLaApv4rGd9mLuRM1k6YS/aw81YrsMryYPThrxrScEIUcmNIHaHsLiU4GMkw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3240,9 +3240,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-x64-musl": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.1.tgz",
|
||||
"integrity": "sha512-a8exvA5Ub9eg66a6hsMQKJIkf63QAf9OdiuFKOsEnKZkNN2x0NLgfvEcqdw88VY0UMs9dBoZ1AGbWMeYnLrLwQ==",
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.8.4.tgz",
|
||||
"integrity": "sha512-ot9STAwyezN8w+bBHZ+bqSQIJ0qPZFlz/AyscpGqB/JnJQVDFQcRDmUPFEaAtt2UUHSWzN3GoTJ5ypqLBp2WQA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3257,9 +3257,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-win32-arm64-msvc": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.4.1.tgz",
|
||||
"integrity": "sha512-4JFrslsMCJQG1c573T9uqQSAbF3j/tMKkMWzsIssv8jvPiP++OG61A2/F+y9te9/Q/O95cKhDK63kaiO5xQaeg==",
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.8.4.tgz",
|
||||
"integrity": "sha512-+2aJ/g90dhLiOLFSD1PbElXX3SoMdpO7HFPAZB+xot3CWlAZD1tReUFy7xe0L5GAR16ZmrxpIDM9v9gn5xRy/w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -3274,9 +3274,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-win32-ia32-msvc": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.4.1.tgz",
|
||||
"integrity": "sha512-9eXfFORehYSCRwxg2KodfmX/mhr50CI7wyBYGbPLePCjr5z0jK/9IyW6r0tC+ZVjwpX48dkk7hKiUgI25jHjzA==",
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.8.4.tgz",
|
||||
"integrity": "sha512-yj7WDxkL1t9Uzr2gufQ1Hl7hrHuFKTNEOyascbc109EoiAqCp0tgZ2IykQqOZmZOHU884UAWI1pVMqBhS/BfhA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -3291,9 +3291,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-win32-x64-msvc": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.4.1.tgz",
|
||||
"integrity": "sha512-60a4Ov7Jrwqz2hzDltlS7301dhSAmM9dxo+IRBD3xz7yobKrgaHXYpWvnRomYItHcDd51VaKc9292H8/eE/gsw==",
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.8.4.tgz",
|
||||
"integrity": "sha512-XuvGB4ehBdd7QhMZ9qbj/8icGEatDuBNxyYHbLKsTYh90ggUlPa/AtaqcC1Fo69lGkTmq9BOKrs1aWSi7xDonA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3317,57 +3317,57 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/plugin-dialog": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.3.0.tgz",
|
||||
"integrity": "sha512-ylSBvYYShpGlKKh732ZuaHyJ5Ie1JR71QCXewCtsRLqGdc8Is4xWdz6t43rzXyvkItM9syNPMvFVcvjgEy+/GA==",
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.4.0.tgz",
|
||||
"integrity": "sha512-OvXkrEBfWwtd8tzVCEXIvRfNEX87qs2jv6SqmVPiHcJjBhSF/GUvjqUNIDmKByb5N8nvDqVUM7+g1sXwdC/S9w==",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.6.0"
|
||||
"@tauri-apps/api": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/plugin-fs": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-fs/-/plugin-fs-2.4.0.tgz",
|
||||
"integrity": "sha512-Sp8AdDcbyXyk6LD6Pmdx44SH3LPeNAvxR2TFfq/8CwqzfO1yOyV+RzT8fov0NNN7d9nvW7O7MtMAptJ42YXA5g==",
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-fs/-/plugin-fs-2.4.2.tgz",
|
||||
"integrity": "sha512-YGhmYuTgXGsi6AjoV+5mh2NvicgWBfVJHHheuck6oHD+HC9bVWPaHvCP0/Aw4pHDejwrvT8hE3+zZAaWf+hrig==",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.6.0"
|
||||
"@tauri-apps/api": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/plugin-log": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-log/-/plugin-log-2.6.0.tgz",
|
||||
"integrity": "sha512-gVp3l31akA1Jk2bZsTA0hMFD5/gLe49Nw1btu5lViau0QqgC2XyT79LSwvy7a44ewtQbSexchqIg7oTJKMIbXQ==",
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-log/-/plugin-log-2.7.0.tgz",
|
||||
"integrity": "sha512-81XQ2f93x4vmIB5OY0XlYAxy60cHdYLs0Ki8Qp38tNATRiuBit+Orh3frpY3qfYQnqEvYVyRub7YRJWlmW2RRA==",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.6.0"
|
||||
"@tauri-apps/api": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/plugin-opener": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.4.0.tgz",
|
||||
"integrity": "sha512-43VyN8JJtvKWJY72WI/KNZszTpDpzHULFxQs0CJBIYUdCRowQ6Q1feWTDb979N7nldqSuDOaBupZ6wz2nvuWwQ==",
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.5.0.tgz",
|
||||
"integrity": "sha512-B0LShOYae4CZjN8leiNDbnfjSrTwoZakqKaWpfoH6nXiJwt6Rgj6RnVIffG3DoJiKsffRhMkjmBV9VeilSb4TA==",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.6.0"
|
||||
"@tauri-apps/api": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/plugin-os": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-os/-/plugin-os-2.3.0.tgz",
|
||||
"integrity": "sha512-dm3bDsMuUngpIQdJ1jaMkMfyQpHyDcaTIKTFaAMHoKeUd+Is3UHO2uzhElr6ZZkfytIIyQtSVnCWdW2Kc58f3g==",
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-os/-/plugin-os-2.3.1.tgz",
|
||||
"integrity": "sha512-ty5V8XDUIFbSnrk3zsFoP3kzN+vAufYzalJSlmrVhQTImIZa1aL1a03bOaP2vuBvfR+WDRC6NgV2xBl8G07d+w==",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.6.0"
|
||||
"@tauri-apps/api": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/plugin-shell": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.3.0.tgz",
|
||||
"integrity": "sha512-6GIRxO2z64uxPX4CCTuhQzefvCC0ew7HjdBhMALiGw74vFBDY95VWueAHOHgNOMV4UOUAFupyidN9YulTe5xlA==",
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.3.1.tgz",
|
||||
"integrity": "sha512-jjs2WGDO/9z2pjNlydY/F5yYhNsscv99K5lCmU5uKjsVvQ3dRlDhhtVYoa4OLDmktLtQvgvbQjCFibMl6tgGfw==",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.6.0"
|
||||
"@tauri-apps/api": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
@@ -17864,21 +17864,24 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "6.2.7",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.7.tgz",
|
||||
"integrity": "sha512-qg3LkeuinTrZoJHHF94coSaTfIPyBYoywp+ys4qu20oSJFbKMYoIJo0FWJT9q6Vp49l6z9IsJRbHdcGtiKbGoQ==",
|
||||
"version": "7.0.7",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.0.7.tgz",
|
||||
"integrity": "sha512-hc6LujN/EkJHmxeiDJMs0qBontZ1cdBvvoCbWhVjzUFTU329VRyOC46gHNSA8NcOC5yzCeXpwI40tieI3DEZqg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"postcss": "^8.5.3",
|
||||
"rollup": "^4.30.1"
|
||||
"fdir": "^6.4.6",
|
||||
"picomatch": "^4.0.3",
|
||||
"postcss": "^8.5.6",
|
||||
"rollup": "^4.40.0",
|
||||
"tinyglobby": "^0.2.14"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/vitejs/vite?sponsor=1"
|
||||
@@ -17887,14 +17890,14 @@
|
||||
"fsevents": "~2.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
||||
"@types/node": "^20.19.0 || >=22.12.0",
|
||||
"jiti": ">=1.21.0",
|
||||
"less": "*",
|
||||
"less": "^4.0.0",
|
||||
"lightningcss": "^1.21.0",
|
||||
"sass": "*",
|
||||
"sass-embedded": "*",
|
||||
"stylus": "*",
|
||||
"sugarss": "*",
|
||||
"sass": "^1.70.0",
|
||||
"sass-embedded": "^1.70.0",
|
||||
"stylus": ">=0.54.8",
|
||||
"sugarss": "^5.0.0",
|
||||
"terser": "^5.16.0",
|
||||
"tsx": "^4.8.1",
|
||||
"yaml": "^2.4.2"
|
||||
@@ -17959,9 +17962,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite-plugin-static-copy": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-3.1.1.tgz",
|
||||
"integrity": "sha512-oR53SkL5cX4KT1t18E/xU50vJDo0N8oaHza4EMk0Fm+2/u6nQivxavOfrDk3udWj+dizRizB/QnBvJOOQrTTAQ==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-3.1.2.tgz",
|
||||
"integrity": "sha512-aVmYOzptLVOI2b1jL+cmkF7O6uhRv1u5fvOkQgbohWZp2CbR22kn9ZqkCUIt9umKF7UhdbsEpshn1rf4720QFg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -18018,6 +18021,37 @@
|
||||
"vite": "^2 || ^3 || ^4 || ^5 || ^6 || ^7"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/fdir": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"picomatch": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz",
|
||||
@@ -19018,14 +19052,14 @@
|
||||
"@tanstack/react-query": "^5.76.1",
|
||||
"@tanstack/react-router": "^1.120.3",
|
||||
"@tanstack/react-virtual": "^3.13.8",
|
||||
"@tauri-apps/api": "^2.5.0",
|
||||
"@tauri-apps/plugin-clipboard-manager": "^2.2.2",
|
||||
"@tauri-apps/plugin-dialog": "^2.2.2",
|
||||
"@tauri-apps/plugin-fs": "^2.3.0",
|
||||
"@tauri-apps/plugin-log": "^2.4.0",
|
||||
"@tauri-apps/plugin-opener": "^2.2.7",
|
||||
"@tauri-apps/plugin-os": "^2.2.1",
|
||||
"@tauri-apps/plugin-shell": "^2.2.1",
|
||||
"@tauri-apps/api": "^2.8.0",
|
||||
"@tauri-apps/plugin-clipboard-manager": "^2.3.0",
|
||||
"@tauri-apps/plugin-dialog": "^2.4.0",
|
||||
"@tauri-apps/plugin-fs": "^2.4.2",
|
||||
"@tauri-apps/plugin-log": "^2.7.0",
|
||||
"@tauri-apps/plugin-opener": "^2.5.0",
|
||||
"@tauri-apps/plugin-os": "^2.3.1",
|
||||
"@tauri-apps/plugin-shell": "^2.3.1",
|
||||
"buffer": "^6.0.3",
|
||||
"classnames": "^2.5.1",
|
||||
"cm6-graphql": "^0.2.1",
|
||||
@@ -19083,28 +19117,13 @@
|
||||
"postcss": "^8.5.6",
|
||||
"postcss-nesting": "^13.0.2",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"vite": "^7.0.4",
|
||||
"vite-plugin-static-copy": "^3.1.1",
|
||||
"vite": "^7.0.7",
|
||||
"vite-plugin-static-copy": "^3.1.2",
|
||||
"vite-plugin-svgr": "^4.3.0",
|
||||
"vite-plugin-top-level-await": "^1.5.0",
|
||||
"vite-plugin-wasm": "^3.5.0"
|
||||
}
|
||||
},
|
||||
"src-web/node_modules/fdir": {
|
||||
"version": "6.4.6",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
|
||||
"integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"picomatch": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"src-web/node_modules/jiti": {
|
||||
"version": "1.21.7",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
|
||||
@@ -19115,19 +19134,6 @@
|
||||
"jiti": "bin/jiti.js"
|
||||
}
|
||||
},
|
||||
"src-web/node_modules/picomatch": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"src-web/node_modules/postcss-nested": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
|
||||
@@ -19231,81 +19237,6 @@
|
||||
"bin": {
|
||||
"uuid": "dist/esm/bin/uuid"
|
||||
}
|
||||
},
|
||||
"src-web/node_modules/vite": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz",
|
||||
"integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.4.6",
|
||||
"picomatch": "^4.0.2",
|
||||
"postcss": "^8.5.6",
|
||||
"rollup": "^4.40.0",
|
||||
"tinyglobby": "^0.2.14"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/vitejs/vite?sponsor=1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": "^20.19.0 || >=22.12.0",
|
||||
"jiti": ">=1.21.0",
|
||||
"less": "^4.0.0",
|
||||
"lightningcss": "^1.21.0",
|
||||
"sass": "^1.70.0",
|
||||
"sass-embedded": "^1.70.0",
|
||||
"stylus": ">=0.54.8",
|
||||
"sugarss": "^5.0.0",
|
||||
"terser": "^5.16.0",
|
||||
"tsx": "^4.8.1",
|
||||
"yaml": "^2.4.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
},
|
||||
"jiti": {
|
||||
"optional": true
|
||||
},
|
||||
"less": {
|
||||
"optional": true
|
||||
},
|
||||
"lightningcss": {
|
||||
"optional": true
|
||||
},
|
||||
"sass": {
|
||||
"optional": true
|
||||
},
|
||||
"sass-embedded": {
|
||||
"optional": true
|
||||
},
|
||||
"stylus": {
|
||||
"optional": true
|
||||
},
|
||||
"sugarss": {
|
||||
"optional": true
|
||||
},
|
||||
"terser": {
|
||||
"optional": true
|
||||
},
|
||||
"tsx": {
|
||||
"optional": true
|
||||
},
|
||||
"yaml": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
"@eslint/compat": "^1.3.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.29.0",
|
||||
"@tauri-apps/cli": "2.4.1",
|
||||
"@tauri-apps/cli": "^2.8.4",
|
||||
"@typescript-eslint/eslint-plugin": "^8.27.0",
|
||||
"@typescript-eslint/parser": "^8.27.0",
|
||||
"@yaakapp/cli": "^0.2.7",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, base: boolean, variables: Array<EnvironmentVariable>, color: string | null, };
|
||||
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, variables: Array<EnvironmentVariable>, color: string | null, parentModel: string, parentId: string | null, };
|
||||
|
||||
export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||
|
||||
|
||||
@@ -34,18 +34,18 @@ export async function convertToCurl(request: Partial<HttpRequest>) {
|
||||
let finalUrl = request.url || '';
|
||||
const urlParams = (request.urlParameters ?? []).filter(onlyEnabled);
|
||||
if (urlParams.length > 0) {
|
||||
// Build url
|
||||
// Build url
|
||||
const [base, hash] = finalUrl.split('#');
|
||||
const separator = base!.includes('?') ? '&' : '?';
|
||||
const queryString = urlParams
|
||||
.map(p => `${encodeURIComponent(p.name)}=${encodeURIComponent(p.value)}`)
|
||||
.map((p) => `${encodeURIComponent(p.name)}=${encodeURIComponent(p.value)}`)
|
||||
.join('&');
|
||||
finalUrl = base + separator + queryString + (hash ? `#${hash}` : '');
|
||||
}
|
||||
|
||||
|
||||
xs.push(quote(finalUrl));
|
||||
xs.push(NEWLINE);
|
||||
|
||||
|
||||
// Add headers
|
||||
for (const h of (request.headers ?? []).filter(onlyEnabled)) {
|
||||
xs.push('--header', quote(`${h.name}: ${h.value}`));
|
||||
@@ -53,7 +53,11 @@ export async function convertToCurl(request: Partial<HttpRequest>) {
|
||||
}
|
||||
|
||||
// Add form params
|
||||
if (Array.isArray(request.body?.form)) {
|
||||
const type = request.bodyType ?? 'none';
|
||||
if (
|
||||
(type === 'multipart/form-data' || type === 'application/x-www-form-urlencoded') &&
|
||||
Array.isArray(request.body?.form)
|
||||
) {
|
||||
const flag = request.bodyType === 'multipart/form-data' ? '--form' : '--data';
|
||||
for (const p of (request.body?.form ?? []).filter(onlyEnabled)) {
|
||||
if (p.file) {
|
||||
@@ -65,14 +69,14 @@ export async function convertToCurl(request: Partial<HttpRequest>) {
|
||||
}
|
||||
xs.push(NEWLINE);
|
||||
}
|
||||
} else if (typeof request.body?.query === 'string') {
|
||||
} else if (type === 'graphql' && typeof request.body?.query === 'string') {
|
||||
const body = {
|
||||
query: request.body.query || '',
|
||||
variables: maybeParseJSON(request.body.variables, undefined),
|
||||
};
|
||||
xs.push('--data', quote(JSON.stringify(body)));
|
||||
xs.push(NEWLINE);
|
||||
} else if (typeof request.body?.text === 'string') {
|
||||
} else if (type !== 'none' && typeof request.body?.text === 'string') {
|
||||
xs.push('--data', quote(request.body.text));
|
||||
xs.push(NEWLINE);
|
||||
}
|
||||
@@ -89,7 +93,9 @@ export async function convertToCurl(request: Partial<HttpRequest>) {
|
||||
|
||||
// Add bearer authentication
|
||||
if (request.authenticationType === 'bearer') {
|
||||
xs.push('--header', quote(`Authorization: Bearer ${request.authentication?.token ?? ''}`));
|
||||
const value =
|
||||
`${request.authentication?.prefix ?? 'Bearer'} ${request.authentication?.token ?? ''}`.trim();
|
||||
xs.push('--header', quote(`Authorization: ${value}`));
|
||||
xs.push(NEWLINE);
|
||||
}
|
||||
|
||||
@@ -116,4 +122,4 @@ function maybeParseJSON<T>(v: string, fallback: T) {
|
||||
} catch {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,7 @@ describe('exporter-curl', () => {
|
||||
{ name: 'c', value: 'ccc', enabled: false },
|
||||
],
|
||||
}),
|
||||
).toEqual(
|
||||
[`curl 'https://yaak.app/?a=aaa&b=bbb'`].join(` \\n `),
|
||||
);
|
||||
).toEqual([`curl 'https://yaak.app?a=aaa&b=bbb'`].join(` \\n `));
|
||||
});
|
||||
|
||||
test('Exports GET with params and hash', async () => {
|
||||
@@ -27,9 +25,7 @@ describe('exporter-curl', () => {
|
||||
{ name: 'c', value: 'ccc', enabled: false },
|
||||
],
|
||||
}),
|
||||
).toEqual(
|
||||
[`curl 'https://yaak.app/path?a=aaa&b=bbb#section'`].join(` \\n `),
|
||||
);
|
||||
).toEqual([`curl 'https://yaak.app/path?a=aaa&b=bbb#section'`].join(` \\n `));
|
||||
});
|
||||
test('Exports POST with url form data', async () => {
|
||||
expect(
|
||||
@@ -62,7 +58,10 @@ describe('exporter-curl', () => {
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
[`curl -X POST 'https://yaak.app'`, `--data '{"query":"{foo,bar}","variables":{"a":"aaa","b":"bbb"}}'`].join(` \\\n `),
|
||||
[
|
||||
`curl -X POST 'https://yaak.app'`,
|
||||
`--data '{"query":"{foo,bar}","variables":{"a":"aaa","b":"bbb"}}'`,
|
||||
].join(` \\\n `),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -206,6 +205,34 @@ describe('exporter-curl', () => {
|
||||
).toEqual([`curl 'https://yaak.app'`, `--header 'Authorization: Bearer tok'`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('Bearer auth with custom prefix', async () => {
|
||||
expect(
|
||||
await convertToCurl({
|
||||
url: 'https://yaak.app',
|
||||
authenticationType: 'bearer',
|
||||
authentication: {
|
||||
token: 'abc123',
|
||||
prefix: 'Token',
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
[`curl 'https://yaak.app'`, `--header 'Authorization: Token abc123'`].join(` \\\n `),
|
||||
);
|
||||
});
|
||||
|
||||
test('Bearer auth with empty prefix', async () => {
|
||||
expect(
|
||||
await convertToCurl({
|
||||
url: 'https://yaak.app',
|
||||
authenticationType: 'bearer',
|
||||
authentication: {
|
||||
token: 'xyz789',
|
||||
prefix: '',
|
||||
},
|
||||
}),
|
||||
).toEqual([`curl 'https://yaak.app'`, `--header 'Authorization: xyz789'`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('Broken bearer auth', async () => {
|
||||
expect(
|
||||
await convertToCurl({
|
||||
@@ -216,6 +243,18 @@ describe('exporter-curl', () => {
|
||||
password: 'pass',
|
||||
},
|
||||
}),
|
||||
).toEqual([`curl 'https://yaak.app'`, `--header 'Authorization: Bearer '`].join(` \\\n `));
|
||||
).toEqual([`curl 'https://yaak.app'`, `--header 'Authorization: Bearer'`].join(` \\\n `));
|
||||
});
|
||||
});
|
||||
|
||||
test('Stale body data', async () => {
|
||||
expect(
|
||||
await convertToCurl({
|
||||
url: 'https://yaak.app',
|
||||
bodyType: 'none',
|
||||
body: {
|
||||
text: 'ignore me',
|
||||
},
|
||||
}),
|
||||
).toEqual([`curl 'https://yaak.app'`].join(` \\\n `));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsonwebtoken": "^9.0.2"
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsonpath-plus": "^10.3.0"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xmldom/xmldom": "^0.9.8",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"shell-quote": "^1.8.1"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"yaml": "^2.4.2"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"openapi-to-postmanv2": "^5.0.0",
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsonpath-plus": "^10.3.0"
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsonpath-plus": "^10.3.0",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"date-fns": "^4.1.0"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": "^11.1.0"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xmldom/xmldom": "^0.9.8",
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "eslint . --ext .ts,.tsx"
|
||||
"lint":"eslint . --ext .ts,.tsx"
|
||||
}
|
||||
}
|
||||
|
||||
218
src-tauri/Cargo.lock
generated
218
src-tauri/Cargo.lock
generated
@@ -736,7 +736,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02260d489095346e5cafd04dea8e8cb54d1d74fcd759022a9b72986ebe9a1257"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"toml",
|
||||
"toml 0.8.23",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -822,6 +822,16 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "charset"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1f927b07c74ba84c7e5fe4db2baeb3e996ab2688992e39ac68ce3220a677c7e"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"encoding_rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.41"
|
||||
@@ -1350,9 +1360,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "dlopen2"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6"
|
||||
checksum = "b54f373ccf864bf587a89e880fb7610f8d73f3045f13580948ccbcaff26febff"
|
||||
dependencies = [
|
||||
"dlopen2_derive",
|
||||
"libc",
|
||||
@@ -1446,7 +1456,7 @@ dependencies = [
|
||||
"cc",
|
||||
"memchr",
|
||||
"rustc_version",
|
||||
"toml",
|
||||
"toml 0.8.23",
|
||||
"vswhom",
|
||||
"winreg",
|
||||
]
|
||||
@@ -3619,6 +3629,16 @@ dependencies = [
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-javascript-core"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9052cb1bb50a4c161d934befcf879526fb87ae9a68858f241e693ca46225cf5a"
|
||||
dependencies = [
|
||||
"objc2 0.6.1",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-metal"
|
||||
version = "0.2.2"
|
||||
@@ -3667,6 +3687,17 @@ dependencies = [
|
||||
"objc2-foundation 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-security"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1f8e0ef3ab66b08c42644dcb34dba6ec0a574bbd8adbb8bdbdc7a2779731a44"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"objc2 0.6.1",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-ui-kit"
|
||||
version = "0.3.1"
|
||||
@@ -3691,6 +3722,8 @@ dependencies = [
|
||||
"objc2-app-kit",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2-javascript-core",
|
||||
"objc2-security",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5223,6 +5256,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_urlencoded"
|
||||
version = "0.7.1"
|
||||
@@ -5280,9 +5322,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serialize-to-javascript"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9823f2d3b6a81d98228151fdeaf848206a7855a7a042bbf9bf870449a66cafb"
|
||||
checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -5291,13 +5333,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serialize-to-javascript-impl"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763"
|
||||
checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5616,17 +5658,18 @@ dependencies = [
|
||||
"cfg-expr",
|
||||
"heck 0.5.0",
|
||||
"pkg-config",
|
||||
"toml",
|
||||
"toml 0.8.23",
|
||||
"version-compare",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tao"
|
||||
version = "0.34.0"
|
||||
version = "0.34.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49c380ca75a231b87b6c9dd86948f035012e7171d1a7c40a9c2890489a7ffd8a"
|
||||
checksum = "959469667dbcea91e5485fc48ba7dd6023face91bb0f1a14681a70f99847c3f7"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"block2 0.6.1",
|
||||
"core-foundation 0.10.1",
|
||||
"core-graphics 0.24.0",
|
||||
"crossbeam-channel",
|
||||
@@ -5695,12 +5738,13 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||
|
||||
[[package]]
|
||||
name = "tauri"
|
||||
version = "2.6.2"
|
||||
version = "2.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "124e129c9c0faa6bec792c5948c89e86c90094133b0b9044df0ce5f0a8efaa0d"
|
||||
checksum = "d4d1d3b3dc4c101ac989fd7db77e045cc6d91a25349cd410455cb5c57d510c1c"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"cookie",
|
||||
"dirs",
|
||||
"dunce",
|
||||
"embed_plist",
|
||||
@@ -5719,6 +5763,7 @@ dependencies = [
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2-ui-kit",
|
||||
"objc2-web-kit",
|
||||
"percent-encoding",
|
||||
"plist",
|
||||
"raw-window-handle",
|
||||
@@ -5746,9 +5791,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-build"
|
||||
version = "2.3.0"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12f025c389d3adb83114bec704da973142e82fc6ec799c7c750c5e21cefaec83"
|
||||
checksum = "9c432ccc9ff661803dab74c6cd78de11026a578a9307610bbc39d3c55be7943f"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_toml",
|
||||
@@ -5762,15 +5807,15 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri-utils",
|
||||
"tauri-winres",
|
||||
"toml",
|
||||
"toml 0.9.5",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-codegen"
|
||||
version = "2.3.0"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5df493a1075a241065bc865ed5ef8d0fbc1e76c7afdc0bf0eccfaa7d4f0e406"
|
||||
checksum = "1ab3a62cf2e6253936a8b267c2e95839674e7439f104fa96ad0025e149d54d8a"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"brotli",
|
||||
@@ -5795,9 +5840,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-macros"
|
||||
version = "2.3.1"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f237fbea5866fa5f2a60a21bea807a2d6e0379db070d89c3a10ac0f2d4649bbc"
|
||||
checksum = "4368ea8094e7045217edb690f493b55b30caf9f3e61f79b4c24b6db91f07995e"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
@@ -5809,9 +5854,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin"
|
||||
version = "2.3.0"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d9a0bd00bf1930ad1a604d08b0eb6b2a9c1822686d65d7f4731a7723b8901d3"
|
||||
checksum = "9946a3cede302eac0c6eb6c6070ac47b1768e326092d32efbb91f21ed58d978f"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"glob",
|
||||
@@ -5820,7 +5865,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri-utils",
|
||||
"toml",
|
||||
"toml 0.9.5",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
@@ -5841,11 +5886,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-deep-link"
|
||||
version = "2.4.0"
|
||||
version = "2.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab261eb006db10ab478e3fbb5a4e2692df3f7eb3e28300ee2b64428979167ed0"
|
||||
checksum = "cd67112fb1131834c2a7398ffcba520dbbf62c17de3b10329acd1a3554b1a9bb"
|
||||
dependencies = [
|
||||
"dunce",
|
||||
"plist",
|
||||
"rust-ini",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -5861,9 +5907,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-dialog"
|
||||
version = "2.3.0"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aefb14219b492afb30b12647b5b1247cadd2c0603467310c36e0f7ae1698c28"
|
||||
checksum = "0beee42a4002bc695550599b011728d9dfabf82f767f134754ed6655e434824e"
|
||||
dependencies = [
|
||||
"log",
|
||||
"raw-window-handle",
|
||||
@@ -5879,9 +5925,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-fs"
|
||||
version = "2.4.0"
|
||||
version = "2.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c341290d31991dbca38b31d412c73dfbdb070bb11536784f19dd2211d13b778f"
|
||||
checksum = "315784ec4be45e90a987687bae7235e6be3d6e9e350d2b75c16b8a4bf22c1db7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dunce",
|
||||
@@ -5895,15 +5941,15 @@ dependencies = [
|
||||
"tauri-plugin",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.12",
|
||||
"toml",
|
||||
"toml 0.9.5",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-log"
|
||||
version = "2.6.0"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a59139183e0907cec1499dddee4e085f5a801dc659efa0848ee224f461371426"
|
||||
checksum = "61c1438bc7662acd16d508c919b3c087efd63669a4c75625dff829b1c75975ec"
|
||||
dependencies = [
|
||||
"android_logger",
|
||||
"byte-unit",
|
||||
@@ -5923,9 +5969,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-opener"
|
||||
version = "2.4.0"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecee219f11cdac713ab32959db5d0cceec4810ba4f4458da992292ecf9660321"
|
||||
checksum = "786156aa8e89e03d271fbd3fe642207da8e65f3c961baa9e2930f332bf80a1f5"
|
||||
dependencies = [
|
||||
"dunce",
|
||||
"glob",
|
||||
@@ -5945,9 +5991,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-os"
|
||||
version = "2.3.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05bccb4c6de4299beec5a9b070878a01bce9e2c945aa7a75bcea38bcba4c675d"
|
||||
checksum = "77a1c77ebf6f20417ab2a74e8c310820ba52151406d0c80fbcea7df232e3f6ba"
|
||||
dependencies = [
|
||||
"gethostname 1.0.2",
|
||||
"log",
|
||||
@@ -5963,9 +6009,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-shell"
|
||||
version = "2.3.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b9ffadec5c3523f11e8273465cacb3d86ea7652a28e6e2a2e9b5c182f791d25"
|
||||
checksum = "54777d0c0d8add34eea3ced84378619ef5b97996bd967d3038c668feefd21071"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
"log",
|
||||
@@ -5984,9 +6030,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-single-instance"
|
||||
version = "2.3.0"
|
||||
version = "2.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b441b6d5d1a194e9fee0b358fe0d602ded845d0f580e1f8c8ef78ebc3c8b225d"
|
||||
checksum = "fb9cac815bf11c4a80fb498666bcdad66d65b89e3ae24669e47806febb76389c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -6032,9 +6078,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-window-state"
|
||||
version = "2.3.0"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a3d22b21b9cec73601b512a868f7c74f93c044d44fd6ca1c84e9d6afb6b1559"
|
||||
checksum = "2d5f6fe3291bfa609c7e0b0ee3bedac294d94c7018934086ce782c1d0f2a468e"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"log",
|
||||
@@ -6047,9 +6093,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime"
|
||||
version = "2.7.0"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e7bb73d1bceac06c20b3f755b2c8a2cb13b20b50083084a8cf3700daf397ba4"
|
||||
checksum = "d4cfc9ad45b487d3fded5a4731a567872a4812e9552e3964161b08edabf93846"
|
||||
dependencies = [
|
||||
"cookie",
|
||||
"dpi",
|
||||
@@ -6058,20 +6104,23 @@ dependencies = [
|
||||
"jni",
|
||||
"objc2 0.6.1",
|
||||
"objc2-ui-kit",
|
||||
"objc2-web-kit",
|
||||
"raw-window-handle",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.12",
|
||||
"url",
|
||||
"webkit2gtk",
|
||||
"webview2-com",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime-wry"
|
||||
version = "2.7.1"
|
||||
version = "2.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "902b5aa9035e16f342eb64f8bf06ccdc2808e411a2525ed1d07672fa4e780bad"
|
||||
checksum = "c1fe9d48bd122ff002064e88cfcd7027090d789c4302714e68fcccba0f4b7807"
|
||||
dependencies = [
|
||||
"gtk",
|
||||
"http",
|
||||
@@ -6096,9 +6145,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-utils"
|
||||
version = "2.5.0"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41743bbbeb96c3a100d234e5a0b60a46d5aa068f266160862c7afdbf828ca02e"
|
||||
checksum = "41a3852fdf9a4f8fbeaa63dc3e9a85284dd6ef7200751f0bd66ceee30c93f212"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"brotli",
|
||||
@@ -6125,7 +6174,7 @@ dependencies = [
|
||||
"serde_with",
|
||||
"swift-rs",
|
||||
"thiserror 2.0.12",
|
||||
"toml",
|
||||
"toml 0.9.5",
|
||||
"url",
|
||||
"urlpattern",
|
||||
"uuid",
|
||||
@@ -6140,7 +6189,7 @@ checksum = "e8d321dbc6f998d825ab3f0d62673e810c861aac2d0de2cc2c395328f1d113b4"
|
||||
dependencies = [
|
||||
"embed-resource",
|
||||
"indexmap 2.9.0",
|
||||
"toml",
|
||||
"toml 0.8.23",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6390,11 +6439,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"serde_spanned 0.6.9",
|
||||
"toml_datetime 0.6.11",
|
||||
"toml_edit 0.22.27",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8"
|
||||
dependencies = [
|
||||
"indexmap 2.9.0",
|
||||
"serde",
|
||||
"serde_spanned 1.0.0",
|
||||
"toml_datetime 0.7.0",
|
||||
"toml_parser",
|
||||
"toml_writer",
|
||||
"winnow 0.7.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.11"
|
||||
@@ -6404,6 +6468,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.19.15"
|
||||
@@ -6411,7 +6484,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
|
||||
dependencies = [
|
||||
"indexmap 2.9.0",
|
||||
"toml_datetime",
|
||||
"toml_datetime 0.6.11",
|
||||
"winnow 0.5.40",
|
||||
]
|
||||
|
||||
@@ -6422,7 +6495,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81"
|
||||
dependencies = [
|
||||
"indexmap 2.9.0",
|
||||
"toml_datetime",
|
||||
"toml_datetime 0.6.11",
|
||||
"winnow 0.5.40",
|
||||
]
|
||||
|
||||
@@ -6434,18 +6507,33 @@ checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
||||
dependencies = [
|
||||
"indexmap 2.9.0",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"serde_spanned 0.6.9",
|
||||
"toml_datetime 0.6.11",
|
||||
"toml_write",
|
||||
"winnow 0.7.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10"
|
||||
dependencies = [
|
||||
"winnow 0.7.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_write"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d163a63c116ce562a22cda521fcc4d79152e7aba014456fb5eb442f6d6a10109"
|
||||
|
||||
[[package]]
|
||||
name = "tonic"
|
||||
version = "0.12.3"
|
||||
@@ -7723,14 +7811,15 @@ checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
|
||||
|
||||
[[package]]
|
||||
name = "wry"
|
||||
version = "0.52.1"
|
||||
version = "0.53.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12a714d9ba7075aae04a6e50229d6109e3d584774b99a6a8c60de1698ca111b9"
|
||||
checksum = "31f0e9642a0d061f6236c54ccae64c2722a7879ad4ec7dff59bd376d446d8e90"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"block2 0.6.1",
|
||||
"cookie",
|
||||
"crossbeam-channel",
|
||||
"dirs",
|
||||
"dpi",
|
||||
"dunce",
|
||||
"gdkx11",
|
||||
@@ -7826,6 +7915,7 @@ dependencies = [
|
||||
name = "yaak-app"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"charset",
|
||||
"chrono",
|
||||
"cookie",
|
||||
"encoding_rs",
|
||||
@@ -8143,9 +8233,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zbus"
|
||||
version = "5.7.1"
|
||||
version = "5.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3a7c7cee313d044fca3f48fa782cb750c79e4ca76ba7bc7718cd4024cdf6f68"
|
||||
checksum = "2d07e46d035fb8e375b2ce63ba4e4ff90a7f73cf2ffb0138b29e1158d2eaadf7"
|
||||
dependencies = [
|
||||
"async-broadcast",
|
||||
"async-executor",
|
||||
@@ -8168,7 +8258,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tracing",
|
||||
"uds_windows",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
"winnow 0.7.10",
|
||||
"zbus_macros",
|
||||
"zbus_names",
|
||||
@@ -8177,9 +8267,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zbus_macros"
|
||||
version = "5.7.1"
|
||||
version = "5.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a17e7e5eec1550f747e71a058df81a9a83813ba0f6a95f39c4e218bdc7ba366a"
|
||||
checksum = "57e797a9c847ed3ccc5b6254e8bcce056494b375b511b3d6edcec0aeb4defaca"
|
||||
dependencies = [
|
||||
"proc-macro-crate 3.3.0",
|
||||
"proc-macro2",
|
||||
|
||||
@@ -34,7 +34,7 @@ strip = true # Automatically strip symbols from the binary.
|
||||
cargo-clippy = []
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.2.0", features = [] }
|
||||
tauri-build = { version = "2.4.1", features = [] }
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
openssl-sys = { version = "0.9.105", features = ["vendored"] } # For Ubuntu installation to work
|
||||
@@ -56,15 +56,15 @@ serde_json = { workspace = true, features = ["raw_value"] }
|
||||
tauri = { workspace = true, features = ["devtools", "protocol-asset"] }
|
||||
tauri-plugin-clipboard-manager = "2.3.0"
|
||||
tauri-plugin-dialog = { workspace = true }
|
||||
tauri-plugin-fs = "2.4.0"
|
||||
tauri-plugin-log = { version = "2.6.0", features = ["colored"] }
|
||||
tauri-plugin-opener = "2.4.0"
|
||||
tauri-plugin-os = "2.3.0"
|
||||
tauri-plugin-fs = "2.4.2"
|
||||
tauri-plugin-log = { version = "2.7.0", features = ["colored"] }
|
||||
tauri-plugin-opener = "2.5.0"
|
||||
tauri-plugin-os = "2.3.1"
|
||||
tauri-plugin-shell = { workspace = true }
|
||||
tauri-plugin-deep-link = "2.4.0"
|
||||
tauri-plugin-single-instance = { version = "2.3.0", features = ["deep-link"] }
|
||||
tauri-plugin-deep-link = "2.4.3"
|
||||
tauri-plugin-single-instance = { version = "2.3.4", features = ["deep-link"] }
|
||||
tauri-plugin-updater = "2.9.0"
|
||||
tauri-plugin-window-state = "2.3.0"
|
||||
tauri-plugin-window-state = "2.4.0"
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true, features = ["sync"] }
|
||||
tokio-stream = "0.1.17"
|
||||
@@ -83,6 +83,7 @@ yaak-sse = { workspace = true }
|
||||
yaak-sync = { workspace = true }
|
||||
yaak-templates = { workspace = true }
|
||||
yaak-ws = { path = "yaak-ws" }
|
||||
charset = "0.1.5"
|
||||
|
||||
[workspace.dependencies]
|
||||
chrono = "0.4.41"
|
||||
@@ -90,10 +91,10 @@ hex = "0.4.3"
|
||||
reqwest = "0.12.20"
|
||||
serde = "1.0.219"
|
||||
serde_json = "1.0.140"
|
||||
tauri = "2.6.2"
|
||||
tauri-plugin = "2.3.0"
|
||||
tauri-plugin-dialog = "2.3.0"
|
||||
tauri-plugin-shell = "2.3.0"
|
||||
tauri = "2.8.5"
|
||||
tauri-plugin = "2.4.0"
|
||||
tauri-plugin-dialog = "2.4.0"
|
||||
tauri-plugin-shell = "2.3.1"
|
||||
tokio = "1.45.1"
|
||||
thiserror = "2.0.12"
|
||||
ts-rs = "11.0.1"
|
||||
|
||||
@@ -1,16 +1,27 @@
|
||||
use encoding_rs::SHIFT_JIS;
|
||||
use log::debug;
|
||||
use mime_guess::{Mime, mime};
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use tokio::fs;
|
||||
use yaak_models::models::HttpResponse;
|
||||
|
||||
pub async fn read_response_body<'a>(
|
||||
response: HttpResponse,
|
||||
) -> Option<String> {
|
||||
let body_path = match response.body_path {
|
||||
None => return None,
|
||||
Some(p) => p,
|
||||
};
|
||||
pub async fn read_response_body(body_path: impl AsRef<Path>, content_type: &str) -> Option<String> {
|
||||
let body = fs::read(body_path).await.ok()?;
|
||||
let body_charset = parse_charset(content_type).unwrap_or("utf-8".to_string());
|
||||
debug!("body_charset: {}", body_charset);
|
||||
if let Some(decoder) = charset::Charset::for_label(body_charset.as_bytes()) {
|
||||
debug!("Using decoder for charset: {}", body_charset);
|
||||
let (cow, real_encoding, exist_replace) = decoder.decode(&body);
|
||||
debug!(
|
||||
"Decoded body with charset: {}, real_encoding: {:?}, exist_replace: {}",
|
||||
body_charset, real_encoding, exist_replace
|
||||
);
|
||||
return cow.into_owned().into();
|
||||
}
|
||||
|
||||
let body = fs::read(body_path).await.unwrap();
|
||||
let (s, _, _) = SHIFT_JIS.decode(body.as_slice());
|
||||
Some(s.to_string())
|
||||
Some(String::from_utf8_lossy(&body).to_string())
|
||||
}
|
||||
|
||||
fn parse_charset(content_type: &str) -> Option<String> {
|
||||
let mime: Mime = Mime::from_str(content_type).ok()?;
|
||||
mime.get_param(mime::CHARSET).map(|v| v.to_string())
|
||||
}
|
||||
|
||||
@@ -43,14 +43,14 @@ pub async fn send_http_request<R: Runtime>(
|
||||
) -> Result<HttpResponse> {
|
||||
let app_handle = window.app_handle().clone();
|
||||
let plugin_manager = app_handle.state::<PluginManager>();
|
||||
let (settings, workspace) = {
|
||||
let db = window.db();
|
||||
let settings = db.get_settings();
|
||||
let workspace = db.get_workspace(&unrendered_request.workspace_id)?;
|
||||
(settings, workspace)
|
||||
};
|
||||
let base_environment =
|
||||
app_handle.db().get_base_environment(&unrendered_request.workspace_id)?;
|
||||
let settings = window.db().get_settings();
|
||||
let workspace = window.db().get_workspace(&unrendered_request.workspace_id)?;
|
||||
let environment_id = environment.map(|e| e.id);
|
||||
let environment_chain = window.db().resolve_environments(
|
||||
&unrendered_request.workspace_id,
|
||||
unrendered_request.folder_id.as_deref(),
|
||||
environment_id.as_deref(),
|
||||
)?;
|
||||
|
||||
let response_id = og_response.id.clone();
|
||||
let response = Arc::new(Mutex::new(og_response.clone()));
|
||||
@@ -76,20 +76,17 @@ pub async fn send_http_request<R: Runtime>(
|
||||
RenderPurpose::Send,
|
||||
);
|
||||
|
||||
let request =
|
||||
match render_http_request(&resolved_request, &base_environment, environment.as_ref(), &cb)
|
||||
.await
|
||||
{
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
return Ok(response_err(
|
||||
&app_handle,
|
||||
&*response.lock().await,
|
||||
e.to_string(),
|
||||
&update_source,
|
||||
));
|
||||
}
|
||||
};
|
||||
let request = match render_http_request(&resolved_request, environment_chain, &cb).await {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
return Ok(response_err(
|
||||
&app_handle,
|
||||
&*response.lock().await,
|
||||
e.to_string(),
|
||||
&update_source,
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let mut url_string = request.url.clone();
|
||||
|
||||
@@ -111,7 +108,7 @@ pub async fn send_http_request<R: Runtime>(
|
||||
.referer(false)
|
||||
.tls_info(true);
|
||||
|
||||
let tls_config = yaak_http::tls::get_config(workspace.setting_validate_certificates);
|
||||
let tls_config = yaak_http::tls::get_config(workspace.setting_validate_certificates, true);
|
||||
client_builder = client_builder.use_preconfigured_tls(tls_config);
|
||||
|
||||
match settings.proxy {
|
||||
|
||||
@@ -30,8 +30,9 @@ use yaak_common::window::WorkspaceWindowTrait;
|
||||
use yaak_grpc::manager::{DynamicMessage, GrpcHandle};
|
||||
use yaak_grpc::{Code, ServiceDefinition, deserialize_message, serialize_message};
|
||||
use yaak_models::models::{
|
||||
CookieJar, Environment, GrpcConnection, GrpcConnectionState, GrpcEvent, GrpcEventType,
|
||||
GrpcRequest, HttpRequest, HttpResponse, HttpResponseState, Plugin, Workspace, WorkspaceMeta,
|
||||
AnyModel, CookieJar, Environment, GrpcConnection, GrpcConnectionState, GrpcEvent,
|
||||
GrpcEventType, GrpcRequest, HttpRequest, HttpResponse, HttpResponseState, Plugin, Workspace,
|
||||
WorkspaceMeta,
|
||||
};
|
||||
use yaak_models::query_manager::QueryManagerExt;
|
||||
use yaak_models::util::{BatchUpsertResult, UpdateSource, get_workspace_export_resources};
|
||||
@@ -110,15 +111,11 @@ async fn cmd_render_template<R: Runtime>(
|
||||
workspace_id: &str,
|
||||
environment_id: Option<&str>,
|
||||
) -> YaakResult<String> {
|
||||
let environment = match environment_id {
|
||||
Some(id) => app_handle.db().get_environment(id).ok(),
|
||||
None => None,
|
||||
};
|
||||
let base_environment = app_handle.db().get_base_environment(&workspace_id)?;
|
||||
let environment_chain =
|
||||
app_handle.db().resolve_environments(workspace_id, None, environment_id)?;
|
||||
let result = render_template(
|
||||
template,
|
||||
&base_environment,
|
||||
environment.as_ref(),
|
||||
environment_chain,
|
||||
&PluginTemplateCallback::new(
|
||||
&app_handle,
|
||||
&PluginWindowContext::new(&window),
|
||||
@@ -147,21 +144,19 @@ async fn cmd_grpc_reflect<R: Runtime>(
|
||||
app_handle: AppHandle<R>,
|
||||
grpc_handle: State<'_, Mutex<GrpcHandle>>,
|
||||
) -> YaakResult<Vec<ServiceDefinition>> {
|
||||
let environment = match environment_id {
|
||||
Some(id) => app_handle.db().get_environment(id).ok(),
|
||||
None => None,
|
||||
};
|
||||
let unrendered_request = app_handle.db().get_grpc_request(request_id)?;
|
||||
let (resolved_request, auth_context_id) = resolve_grpc_request(&window, &unrendered_request)?;
|
||||
|
||||
let base_environment =
|
||||
app_handle.db().get_base_environment(&unrendered_request.workspace_id)?;
|
||||
let environment_chain = app_handle.db().resolve_environments(
|
||||
&unrendered_request.workspace_id,
|
||||
unrendered_request.folder_id.as_deref(),
|
||||
environment_id,
|
||||
)?;
|
||||
let workspace = app_handle.db().get_workspace(&unrendered_request.workspace_id)?;
|
||||
|
||||
let req = render_grpc_request(
|
||||
&resolved_request,
|
||||
&base_environment,
|
||||
environment.as_ref(),
|
||||
environment_chain,
|
||||
&PluginTemplateCallback::new(
|
||||
&app_handle,
|
||||
&PluginWindowContext::new(&window),
|
||||
@@ -196,20 +191,18 @@ async fn cmd_grpc_go<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
grpc_handle: State<'_, Mutex<GrpcHandle>>,
|
||||
) -> YaakResult<String> {
|
||||
let environment = match environment_id {
|
||||
Some(id) => app_handle.db().get_environment(id).ok(),
|
||||
None => None,
|
||||
};
|
||||
let unrendered_request = app_handle.db().get_grpc_request(request_id)?;
|
||||
let (resolved_request, auth_context_id) = resolve_grpc_request(&window, &unrendered_request)?;
|
||||
let base_environment =
|
||||
app_handle.db().get_base_environment(&unrendered_request.workspace_id)?;
|
||||
let environment_chain = app_handle.db().resolve_environments(
|
||||
&unrendered_request.workspace_id,
|
||||
unrendered_request.folder_id.as_deref(),
|
||||
environment_id,
|
||||
)?;
|
||||
let workspace = app_handle.db().get_workspace(&unrendered_request.workspace_id)?;
|
||||
|
||||
let request = render_grpc_request(
|
||||
&resolved_request,
|
||||
&base_environment,
|
||||
environment.as_ref(),
|
||||
environment_chain.clone(),
|
||||
&PluginTemplateCallback::new(
|
||||
&app_handle,
|
||||
&PluginWindowContext::new(&window),
|
||||
@@ -300,9 +293,8 @@ async fn cmd_grpc_go<R: Runtime>(
|
||||
let cb = {
|
||||
let cancelled_rx = cancelled_rx.clone();
|
||||
let app_handle = app_handle.clone();
|
||||
let environment_chain = environment_chain.clone();
|
||||
let window = window.clone();
|
||||
let base_environment = base_environment.clone();
|
||||
let environment = environment.clone();
|
||||
let base_msg = base_msg.clone();
|
||||
let method_desc = method_desc.clone();
|
||||
|
||||
@@ -327,12 +319,12 @@ async fn cmd_grpc_go<R: Runtime>(
|
||||
let app_handle = app_handle.clone();
|
||||
let base_msg = base_msg.clone();
|
||||
let method_desc = method_desc.clone();
|
||||
let environment_chain = environment_chain.clone();
|
||||
let msg = block_in_place(|| {
|
||||
tauri::async_runtime::block_on(async {
|
||||
render_template(
|
||||
msg.as_str(),
|
||||
&base_environment,
|
||||
environment.as_ref(),
|
||||
environment_chain,
|
||||
&PluginTemplateCallback::new(
|
||||
&app_handle,
|
||||
&PluginWindowContext::new(&window),
|
||||
@@ -396,12 +388,12 @@ async fn cmd_grpc_go<R: Runtime>(
|
||||
let window = window.clone();
|
||||
let app_handle = app_handle.clone();
|
||||
let base_event = base_msg.clone();
|
||||
let environment_chain = environment_chain.clone();
|
||||
let req = request.clone();
|
||||
let msg = if req.message.is_empty() { "{}".to_string() } else { req.message };
|
||||
let msg = render_template(
|
||||
msg.as_str(),
|
||||
&base_environment.clone(),
|
||||
environment.as_ref(),
|
||||
environment_chain,
|
||||
&PluginTemplateCallback::new(
|
||||
&app_handle,
|
||||
&PluginWindowContext::new(&window),
|
||||
@@ -726,33 +718,43 @@ async fn cmd_format_json(text: &str) -> YaakResult<String> {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_filter_response<R: Runtime>(
|
||||
async fn cmd_http_response_body<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
app_handle: AppHandle<R>,
|
||||
response_id: &str,
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
filter: &str,
|
||||
response_id: &str,
|
||||
filter: Option<&str>,
|
||||
) -> YaakResult<FilterResponse> {
|
||||
let response = app_handle.db().get_http_response(response_id)?;
|
||||
|
||||
if let None = response.body_path {
|
||||
return Err(GenericError("Response body path not set".to_string()));
|
||||
}
|
||||
|
||||
let mut content_type = "".to_string();
|
||||
for header in response.headers.iter() {
|
||||
if header.name.to_lowercase() == "content-type" {
|
||||
content_type = header.value.to_string().to_lowercase();
|
||||
break;
|
||||
let body_path = match response.body_path {
|
||||
None => {
|
||||
return Err(GenericError("Response body path not set".to_string()));
|
||||
}
|
||||
}
|
||||
Some(p) => p,
|
||||
};
|
||||
|
||||
let body = read_response_body(response)
|
||||
let content_type = response
|
||||
.headers
|
||||
.iter()
|
||||
.find_map(|h| {
|
||||
if h.name.eq_ignore_ascii_case("content-type") { Some(h.value.as_str()) } else { None }
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let body = read_response_body(&body_path, content_type)
|
||||
.await
|
||||
.ok_or(GenericError("Failed to find response body".to_string()))?;
|
||||
|
||||
// TODO: Have plugins register their own content type (regex?)
|
||||
Ok(plugin_manager.filter_data(&window, filter, &body, &content_type).await?)
|
||||
match filter {
|
||||
Some(filter) if !filter.is_empty() => {
|
||||
Ok(plugin_manager.filter_data(&window, filter, &body, content_type).await?)
|
||||
}
|
||||
_ => Ok(FilterResponse {
|
||||
content: body,
|
||||
error: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -823,30 +825,25 @@ async fn cmd_get_http_authentication_config<R: Runtime>(
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
auth_name: &str,
|
||||
values: HashMap<String, JsonPrimitive>,
|
||||
request_id: &str,
|
||||
request: AnyModel,
|
||||
environment_id: Option<&str>,
|
||||
workspace_id: &str,
|
||||
) -> YaakResult<GetHttpAuthenticationConfigResponse> {
|
||||
let base_environment = window.db().get_base_environment(&workspace_id)?;
|
||||
let environment = match environment_id {
|
||||
Some(id) => match window.db().get_environment(id) {
|
||||
Ok(env) => Some(env),
|
||||
Err(e) => {
|
||||
warn!("Failed to find environment by id {id} {}", e);
|
||||
None
|
||||
}
|
||||
let (workspace_id, folder_id) = match request.clone() {
|
||||
AnyModel::HttpRequest(m) => (m.workspace_id, m.folder_id),
|
||||
AnyModel::GrpcRequest(m) => (m.workspace_id, m.folder_id),
|
||||
AnyModel::WebsocketRequest(m) => (m.workspace_id, m.folder_id),
|
||||
AnyModel::Folder(m) => (m.workspace_id, m.folder_id),
|
||||
AnyModel::Workspace(m) => (m.id, None),
|
||||
m => {
|
||||
return Err(GenericError(format!("Unsupported model to call auth config {m:?}")));
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
let environment_chain =
|
||||
window.db().resolve_environments(&workspace_id, folder_id.as_deref(), environment_id)?;
|
||||
|
||||
Ok(plugin_manager
|
||||
.get_http_authentication_config(
|
||||
&window,
|
||||
&base_environment,
|
||||
environment.as_ref(),
|
||||
auth_name,
|
||||
values,
|
||||
request_id,
|
||||
)
|
||||
.get_http_authentication_config(&window, environment_chain, auth_name, values, request.id())
|
||||
.await?)
|
||||
}
|
||||
|
||||
@@ -897,30 +894,29 @@ async fn cmd_call_http_authentication_action<R: Runtime>(
|
||||
auth_name: &str,
|
||||
action_index: i32,
|
||||
values: HashMap<String, JsonPrimitive>,
|
||||
model_id: &str,
|
||||
workspace_id: &str,
|
||||
model: AnyModel,
|
||||
environment_id: Option<&str>,
|
||||
) -> YaakResult<()> {
|
||||
let base_environment = window.db().get_base_environment(&workspace_id)?;
|
||||
let environment = match environment_id {
|
||||
Some(id) => match window.db().get_environment(id) {
|
||||
Ok(env) => Some(env),
|
||||
Err(e) => {
|
||||
warn!("Failed to find environment by id {id} {}", e);
|
||||
None
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
let (workspace_id, folder_id) = match model.clone() {
|
||||
AnyModel::HttpRequest(m) => (m.workspace_id, m.folder_id),
|
||||
AnyModel::GrpcRequest(m) => (m.workspace_id, m.folder_id),
|
||||
AnyModel::WebsocketRequest(m) => (m.workspace_id, m.folder_id),
|
||||
AnyModel::Folder(m) => (m.workspace_id, m.folder_id),
|
||||
AnyModel::Workspace(m) => (m.id, None),
|
||||
m => {
|
||||
return Err(GenericError(format!("Unsupported model to call auth {m:?}")));
|
||||
}
|
||||
};
|
||||
let environment_chain =
|
||||
window.db().resolve_environments(&workspace_id, folder_id.as_deref(), environment_id)?;
|
||||
Ok(plugin_manager
|
||||
.call_http_authentication_action(
|
||||
&window,
|
||||
&base_environment,
|
||||
environment.as_ref(),
|
||||
environment_chain,
|
||||
auth_name,
|
||||
action_index,
|
||||
values,
|
||||
model_id,
|
||||
&model.id(),
|
||||
)
|
||||
.await?)
|
||||
}
|
||||
@@ -1330,7 +1326,7 @@ pub fn run() {
|
||||
cmd_delete_send_history,
|
||||
cmd_dismiss_notification,
|
||||
cmd_export_data,
|
||||
cmd_filter_response,
|
||||
cmd_http_response_body,
|
||||
cmd_format_json,
|
||||
cmd_get_http_authentication_summaries,
|
||||
cmd_get_http_authentication_config,
|
||||
@@ -1389,12 +1385,15 @@ pub fn run() {
|
||||
} => {
|
||||
let w = app_handle.get_webview_window(&label).unwrap();
|
||||
let h = app_handle.clone();
|
||||
|
||||
// Run update check whenever the window is focused
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let val: State<'_, Mutex<YaakUpdater>> = h.state();
|
||||
let update_mode = get_update_mode(&w).await.unwrap();
|
||||
if let Err(e) = val.lock().await.maybe_check(&w, update_mode).await {
|
||||
warn!("Failed to check for updates {e:?}");
|
||||
if w.db().get_settings().autoupdate {
|
||||
let val: State<'_, Mutex<YaakUpdater>> = h.state();
|
||||
let update_mode = get_update_mode(&w).await.unwrap();
|
||||
if let Err(e) = val.lock().await.maybe_check(&w, update_mode).await {
|
||||
warn!("Failed to check for updates {e:?}");
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -74,20 +74,15 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
|
||||
let workspace =
|
||||
workspace_from_window(&window).expect("Failed to get workspace_id from window URL");
|
||||
let environment = environment_from_window(&window);
|
||||
let base_environment = app_handle
|
||||
let environment_id = environment_from_window(&window).map(|e| e.id);
|
||||
let environment_chain = window
|
||||
.db()
|
||||
.get_base_environment(&workspace.id)
|
||||
.expect("Failed to get base environment");
|
||||
.resolve_environments(&workspace.id, None, environment_id.as_deref())
|
||||
.expect("Failed to resolve environments");
|
||||
let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose);
|
||||
let grpc_request = render_grpc_request(
|
||||
&req.grpc_request,
|
||||
&base_environment,
|
||||
environment.as_ref(),
|
||||
&cb,
|
||||
)
|
||||
.await
|
||||
.expect("Failed to render grpc request");
|
||||
let grpc_request = render_grpc_request(&req.grpc_request, environment_chain, &cb)
|
||||
.await
|
||||
.expect("Failed to render grpc request");
|
||||
Some(InternalEventPayload::RenderGrpcRequestResponse(RenderGrpcRequestResponse {
|
||||
grpc_request,
|
||||
}))
|
||||
@@ -98,20 +93,15 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
|
||||
let workspace =
|
||||
workspace_from_window(&window).expect("Failed to get workspace_id from window URL");
|
||||
let environment = environment_from_window(&window);
|
||||
let base_environment = app_handle
|
||||
let environment_id = environment_from_window(&window).map(|e| e.id);
|
||||
let environment_chain = window
|
||||
.db()
|
||||
.get_base_environment(&workspace.id)
|
||||
.expect("Failed to get base environment");
|
||||
.resolve_environments(&workspace.id, None, environment_id.as_deref())
|
||||
.expect("Failed to resolve environments");
|
||||
let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose);
|
||||
let http_request = render_http_request(
|
||||
&req.http_request,
|
||||
&base_environment,
|
||||
environment.as_ref(),
|
||||
&cb,
|
||||
)
|
||||
.await
|
||||
.expect("Failed to render http request");
|
||||
let http_request = render_http_request(&req.http_request, environment_chain, &cb)
|
||||
.await
|
||||
.expect("Failed to render http request");
|
||||
Some(InternalEventPayload::RenderHttpRequestResponse(RenderHttpRequestResponse {
|
||||
http_request,
|
||||
}))
|
||||
@@ -122,13 +112,13 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
|
||||
let workspace =
|
||||
workspace_from_window(&window).expect("Failed to get workspace_id from window URL");
|
||||
let environment = environment_from_window(&window);
|
||||
let base_environment = app_handle
|
||||
let environment_id = environment_from_window(&window).map(|e| e.id);
|
||||
let environment_chain = window
|
||||
.db()
|
||||
.get_base_environment(&workspace.id)
|
||||
.expect("Failed to get base environment");
|
||||
.resolve_environments(&workspace.id, None, environment_id.as_deref())
|
||||
.expect("Failed to resolve environments");
|
||||
let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose);
|
||||
let data = render_json_value(req.data, &base_environment, environment.as_ref(), &cb)
|
||||
let data = render_json_value(req.data, environment_chain, &cb)
|
||||
.await
|
||||
.expect("Failed to render template");
|
||||
Some(InternalEventPayload::TemplateRenderResponse(TemplateRenderResponse { data }))
|
||||
|
||||
@@ -5,35 +5,32 @@ use yaak_models::models::{
|
||||
Environment, GrpcRequest, HttpRequest, HttpRequestHeader, HttpUrlParameter,
|
||||
};
|
||||
use yaak_models::render::make_vars_hashmap;
|
||||
use yaak_templates::{parse_and_render, render_json_value_raw, TemplateCallback};
|
||||
use yaak_templates::{TemplateCallback, parse_and_render, render_json_value_raw};
|
||||
|
||||
pub async fn render_template<T: TemplateCallback>(
|
||||
template: &str,
|
||||
base_environment: &Environment,
|
||||
environment: Option<&Environment>,
|
||||
environment_chain: Vec<Environment>,
|
||||
cb: &T,
|
||||
) -> yaak_templates::error::Result<String> {
|
||||
let vars = &make_vars_hashmap(base_environment, environment);
|
||||
let vars = &make_vars_hashmap(environment_chain);
|
||||
render(template, vars, cb).await
|
||||
}
|
||||
|
||||
pub async fn render_json_value<T: TemplateCallback>(
|
||||
value: Value,
|
||||
base_environment: &Environment,
|
||||
environment: Option<&Environment>,
|
||||
environment_chain: Vec<Environment>,
|
||||
cb: &T,
|
||||
) -> yaak_templates::error::Result<Value> {
|
||||
let vars = &make_vars_hashmap(base_environment, environment);
|
||||
let vars = &make_vars_hashmap(environment_chain);
|
||||
render_json_value_raw(value, vars, cb).await
|
||||
}
|
||||
|
||||
pub async fn render_grpc_request<T: TemplateCallback>(
|
||||
r: &GrpcRequest,
|
||||
base_environment: &Environment,
|
||||
environment: Option<&Environment>,
|
||||
environment_chain: Vec<Environment>,
|
||||
cb: &T,
|
||||
) -> yaak_templates::error::Result<GrpcRequest> {
|
||||
let vars = &make_vars_hashmap(base_environment, environment);
|
||||
let vars = &make_vars_hashmap(environment_chain);
|
||||
|
||||
let mut metadata = Vec::new();
|
||||
for p in r.metadata.clone() {
|
||||
@@ -62,11 +59,10 @@ pub async fn render_grpc_request<T: TemplateCallback>(
|
||||
|
||||
pub async fn render_http_request<T: TemplateCallback>(
|
||||
r: &HttpRequest,
|
||||
base_environment: &Environment,
|
||||
environment: Option<&Environment>,
|
||||
environment_chain: Vec<Environment>,
|
||||
cb: &T,
|
||||
) -> yaak_templates::error::Result<HttpRequest> {
|
||||
let vars = &make_vars_hashmap(base_environment, environment);
|
||||
let vars = &make_vars_hashmap(environment_chain);
|
||||
|
||||
let mut url_parameters = Vec::new();
|
||||
for p in r.url_parameters.clone() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, base: boolean, variables: Array<EnvironmentVariable>, color: string | null, };
|
||||
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, variables: Array<EnvironmentVariable>, color: string | null, parentModel: string, parentId: string | null, };
|
||||
|
||||
export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ pub(crate) fn find_ssh_key() -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn get_current_branch(repo: &Repository) -> Result<Option<Branch>> {
|
||||
pub(crate) fn get_current_branch(repo: &Repository) -> Result<Option<Branch<'_>>> {
|
||||
for b in repo.branches(None)? {
|
||||
let branch = b?.0;
|
||||
if branch.is_head() {
|
||||
@@ -101,7 +101,7 @@ pub(crate) fn get_default_remote_in_repo(repo: &Repository) -> Result<String> {
|
||||
return Ok(DEFAULT_REMOTE_NAME.into());
|
||||
}
|
||||
|
||||
// if only one remote exists pick that
|
||||
// if only one remote exists, pick that
|
||||
if remotes.len() == 1 {
|
||||
let first_remote = remotes
|
||||
.iter()
|
||||
|
||||
@@ -4,8 +4,11 @@ use hyper_util::client::legacy::Client;
|
||||
use hyper_util::rt::TokioExecutor;
|
||||
use tonic::body::BoxBody;
|
||||
|
||||
// I think ALPN breaks this because we're specifying http2_only
|
||||
const WITH_ALPN: bool = false;
|
||||
|
||||
pub(crate) fn get_transport(validate_certificates: bool) -> Client<HttpsConnector<HttpConnector>, BoxBody> {
|
||||
let tls_config = yaak_http::tls::get_config(validate_certificates);
|
||||
let tls_config = yaak_http::tls::get_config(validate_certificates, WITH_ALPN);
|
||||
|
||||
let mut http = HttpConnector::new();
|
||||
http.enforce_http(false);
|
||||
|
||||
@@ -5,7 +5,7 @@ use rustls::{ClientConfig, DigitallySignedStruct, SignatureScheme};
|
||||
use rustls_platform_verifier::BuilderVerifierExt;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub fn get_config(validate_certificates: bool) -> ClientConfig {
|
||||
pub fn get_config(validate_certificates: bool, with_alpn: bool) -> ClientConfig {
|
||||
let arc_crypto_provider = Arc::new(ring::default_provider());
|
||||
let config_builder = ClientConfig::builder_with_provider(arc_crypto_provider)
|
||||
.with_safe_default_protocol_versions()
|
||||
@@ -19,8 +19,11 @@ pub fn get_config(validate_certificates: bool) -> ClientConfig {
|
||||
.with_custom_certificate_verifier(Arc::new(NoVerifier))
|
||||
.with_no_client_auth()
|
||||
};
|
||||
// Required for http/2 support
|
||||
client.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
||||
|
||||
if with_alpn {
|
||||
client.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
||||
}
|
||||
|
||||
client
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ export type EditorKeymap = "default" | "vim" | "vscode" | "emacs";
|
||||
|
||||
export type EncryptedKey = { encryptedKey: string, };
|
||||
|
||||
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, base: boolean, variables: Array<EnvironmentVariable>, color: string | null, };
|
||||
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, variables: Array<EnvironmentVariable>, color: string | null, parentModel: string, parentId: string | null, };
|
||||
|
||||
export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||
|
||||
@@ -62,7 +62,7 @@ export type ProxySetting = { "type": "enabled", http: string, https: string, aut
|
||||
|
||||
export type ProxySettingAuth = { user: string, password: string, };
|
||||
|
||||
export type Settings = { model: "settings", id: string, createdAt: string, updatedAt: string, appearance: string, coloredMethods: boolean, editorFont: string | null, editorFontSize: number, editorKeymap: EditorKeymap, editorSoftWrap: boolean, hideWindowControls: boolean, interfaceFont: string | null, interfaceFontSize: number, interfaceScale: number, openWorkspaceNewWindow: boolean | null, proxy: ProxySetting | null, themeDark: string, themeLight: string, updateChannel: string, };
|
||||
export type Settings = { model: "settings", id: string, createdAt: string, updatedAt: string, appearance: string, coloredMethods: boolean, editorFont: string | null, editorFontSize: number, editorKeymap: EditorKeymap, editorSoftWrap: boolean, hideWindowControls: boolean, interfaceFont: string | null, interfaceFontSize: number, interfaceScale: number, openWorkspaceNewWindow: boolean | null, proxy: ProxySetting | null, themeDark: string, themeLight: string, updateChannel: string, autoupdate: boolean, };
|
||||
|
||||
export type SyncState = { model: "sync_state", id: string, workspaceId: string, createdAt: string, updatedAt: string, flushedAt: string, modelId: string, checksum: string, relPath: string, syncDir: string, };
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE settings ADD COLUMN autoupdate BOOLEAN DEFAULT true NOT NULL;
|
||||
@@ -0,0 +1,62 @@
|
||||
-- Create temporary table for migration
|
||||
CREATE TABLE environments__new
|
||||
(
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
deleted_at DATETIME,
|
||||
workspace_id TEXT NOT NULL
|
||||
REFERENCES workspaces ON DELETE CASCADE,
|
||||
|
||||
name TEXT NOT NULL,
|
||||
variables TEXT DEFAULT '[]' NOT NULL,
|
||||
model TEXT DEFAULT 'environment',
|
||||
public BOOLEAN DEFAULT FALSE,
|
||||
color TEXT,
|
||||
|
||||
-- NEW
|
||||
parent_model TEXT DEFAULT 'workspace' NOT NULL,
|
||||
parent_id TEXT
|
||||
);
|
||||
|
||||
-- Backfill the data from the old table
|
||||
-- - base=1 -> (workspace, NULL)
|
||||
-- - base=0 -> (environment, id_of_workspace_base) (fallback to workspace,NULL if none)
|
||||
INSERT INTO environments__new
|
||||
(id, created_at, updated_at, deleted_at, workspace_id, name, variables, model, public, color, parent_model, parent_id)
|
||||
SELECT
|
||||
e.id,
|
||||
e.created_at,
|
||||
e.updated_at,
|
||||
e.deleted_at,
|
||||
e.workspace_id,
|
||||
e.name,
|
||||
e.variables,
|
||||
e.model,
|
||||
e.public,
|
||||
e.color,
|
||||
CASE
|
||||
WHEN e.base = 1 THEN 'workspace'
|
||||
WHEN (
|
||||
SELECT COUNT(1)
|
||||
FROM environments b
|
||||
WHERE b.workspace_id = e.workspace_id AND b.base = 1
|
||||
) > 0 THEN 'environment'
|
||||
ELSE 'workspace'
|
||||
END AS parent_model,
|
||||
CASE
|
||||
WHEN e.base = 1 THEN NULL
|
||||
ELSE (
|
||||
SELECT b.id
|
||||
FROM environments b
|
||||
WHERE b.workspace_id = e.workspace_id AND b.base = 1
|
||||
ORDER BY b.created_at ASC, b.id ASC
|
||||
LIMIT 1
|
||||
)
|
||||
END AS parent_id
|
||||
FROM environments e;
|
||||
|
||||
-- Move everything to the new table
|
||||
DROP TABLE environments;
|
||||
ALTER TABLE environments__new
|
||||
RENAME TO environments;
|
||||
@@ -89,7 +89,7 @@ impl<'a> DbContext<'a> {
|
||||
col: impl IntoColumnRef,
|
||||
value: impl Into<SimpleExpr>,
|
||||
limit: Option<u64>,
|
||||
) -> crate::error::Result<Vec<M>>
|
||||
) -> Result<Vec<M>>
|
||||
where
|
||||
M: Into<AnyModel> + Clone + UpsertModelInfo,
|
||||
{
|
||||
|
||||
@@ -29,6 +29,9 @@ pub enum Error {
|
||||
|
||||
#[error("Multiple base environments for {0}. Delete duplicates before continuing.")]
|
||||
MultipleBaseEnvironments(String),
|
||||
|
||||
#[error("Multiple folder environments for {0}. Delete duplicates before continuing.")]
|
||||
MultipleFolderEnvironments(String),
|
||||
|
||||
#[error("unknown error")]
|
||||
Unknown,
|
||||
|
||||
@@ -120,6 +120,7 @@ pub struct Settings {
|
||||
pub theme_dark: String,
|
||||
pub theme_light: String,
|
||||
pub update_channel: String,
|
||||
pub autoupdate: bool,
|
||||
}
|
||||
|
||||
impl UpsertModelInfo for Settings {
|
||||
@@ -168,6 +169,7 @@ impl UpsertModelInfo for Settings {
|
||||
(ThemeDark, self.theme_dark.as_str().into()),
|
||||
(ThemeLight, self.theme_light.as_str().into()),
|
||||
(UpdateChannel, self.update_channel.into()),
|
||||
(Autoupdate, self.autoupdate.into()),
|
||||
(ColoredMethods, self.colored_methods.into()),
|
||||
(Proxy, proxy.into()),
|
||||
])
|
||||
@@ -190,6 +192,7 @@ impl UpsertModelInfo for Settings {
|
||||
SettingsIden::ThemeDark,
|
||||
SettingsIden::ThemeLight,
|
||||
SettingsIden::UpdateChannel,
|
||||
SettingsIden::Autoupdate,
|
||||
SettingsIden::ColoredMethods,
|
||||
]
|
||||
}
|
||||
@@ -219,6 +222,7 @@ impl UpsertModelInfo for Settings {
|
||||
theme_light: row.get("theme_light")?,
|
||||
hide_window_controls: row.get("hide_window_controls")?,
|
||||
update_channel: row.get("update_channel")?,
|
||||
autoupdate: row.get("autoupdate")?,
|
||||
colored_methods: row.get("colored_methods")?,
|
||||
})
|
||||
}
|
||||
@@ -529,9 +533,10 @@ pub struct Environment {
|
||||
|
||||
pub name: String,
|
||||
pub public: bool,
|
||||
pub base: bool,
|
||||
pub variables: Vec<EnvironmentVariable>,
|
||||
pub color: Option<String>,
|
||||
pub parent_model: String,
|
||||
pub parent_id: Option<String>,
|
||||
}
|
||||
|
||||
impl UpsertModelInfo for Environment {
|
||||
@@ -564,7 +569,8 @@ impl UpsertModelInfo for Environment {
|
||||
(CreatedAt, upsert_date(source, self.created_at)),
|
||||
(UpdatedAt, upsert_date(source, self.updated_at)),
|
||||
(WorkspaceId, self.workspace_id.into()),
|
||||
(Base, self.base.into()),
|
||||
(ParentId, self.parent_id.into()),
|
||||
(ParentModel, self.parent_model.into()),
|
||||
(Color, self.color.into()),
|
||||
(Name, self.name.trim().into()),
|
||||
(Public, self.public.into()),
|
||||
@@ -575,7 +581,8 @@ impl UpsertModelInfo for Environment {
|
||||
fn update_columns() -> Vec<impl IntoIden> {
|
||||
vec![
|
||||
EnvironmentIden::UpdatedAt,
|
||||
EnvironmentIden::Base,
|
||||
EnvironmentIden::ParentId,
|
||||
EnvironmentIden::ParentModel,
|
||||
EnvironmentIden::Color,
|
||||
EnvironmentIden::Name,
|
||||
EnvironmentIden::Public,
|
||||
@@ -594,7 +601,8 @@ impl UpsertModelInfo for Environment {
|
||||
workspace_id: row.get("workspace_id")?,
|
||||
created_at: row.get("created_at")?,
|
||||
updated_at: row.get("updated_at")?,
|
||||
base: row.get("base")?,
|
||||
parent_id: row.get("parent_id")?,
|
||||
parent_model: row.get("parent_model")?,
|
||||
color: row.get("color")?,
|
||||
name: row.get("name")?,
|
||||
public: row.get("public")?,
|
||||
@@ -2068,6 +2076,17 @@ macro_rules! define_any_model {
|
||||
)*
|
||||
}
|
||||
|
||||
impl AnyModel {
|
||||
#[inline]
|
||||
pub fn id(&self) -> &str {
|
||||
match self {
|
||||
$(
|
||||
AnyModel::$type(inner) => &inner.id,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
impl From<$type> for AnyModel {
|
||||
fn from(value: $type) -> Self {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use crate::db_context::DbContext;
|
||||
use crate::error::Error::{MissingBaseEnvironment, MultipleBaseEnvironments};
|
||||
use crate::error::Error::{
|
||||
MissingBaseEnvironment, MultipleBaseEnvironments, MultipleFolderEnvironments,
|
||||
};
|
||||
use crate::error::Result;
|
||||
use crate::models::{Environment, EnvironmentIden, EnvironmentVariable};
|
||||
use crate::util::UpdateSource;
|
||||
@@ -10,21 +12,31 @@ impl<'a> DbContext<'a> {
|
||||
self.find_one(EnvironmentIden::Id, id)
|
||||
}
|
||||
|
||||
pub fn get_environment_by_folder_id(&self, folder_id: &str) -> Result<Option<Environment>> {
|
||||
let environments: Vec<Environment> =
|
||||
self.find_many(EnvironmentIden::ParentId, folder_id, None)?;
|
||||
if environments.len() > 1 {
|
||||
return Err(MultipleFolderEnvironments(folder_id.to_string()));
|
||||
}
|
||||
|
||||
Ok(environments.get(0).cloned())
|
||||
}
|
||||
|
||||
pub fn get_base_environment(&self, workspace_id: &str) -> Result<Environment> {
|
||||
let environments = self.list_environments_ensure_base(workspace_id)?;
|
||||
let base_environments =
|
||||
environments.into_iter().filter(|e| e.base).collect::<Vec<Environment>>();
|
||||
let base_environments = environments
|
||||
.into_iter()
|
||||
.filter(|e| e.parent_id.is_none())
|
||||
.collect::<Vec<Environment>>();
|
||||
|
||||
if base_environments.len() > 1 {
|
||||
return Err(MultipleBaseEnvironments(workspace_id.to_string()));
|
||||
}
|
||||
|
||||
let base_environment = base_environments.into_iter().find(|e| e.base).ok_or(
|
||||
Ok(base_environments.first().cloned().ok_or(
|
||||
// Should never happen because one should be created above if it does not exist
|
||||
MissingBaseEnvironment(workspace_id.to_string()),
|
||||
)?;
|
||||
|
||||
Ok(base_environment)
|
||||
)?)
|
||||
}
|
||||
|
||||
/// Lists environments and will create a base environment if one doesn't exist
|
||||
@@ -32,13 +44,12 @@ impl<'a> DbContext<'a> {
|
||||
let mut environments =
|
||||
self.find_many::<Environment>(EnvironmentIden::WorkspaceId, workspace_id, None)?;
|
||||
|
||||
let base_environment = environments.iter().find(|e| e.base);
|
||||
let base_environment = environments.iter().find(|e| e.parent_id.is_none());
|
||||
|
||||
if let None = base_environment {
|
||||
let e = self.upsert_environment(
|
||||
&Environment {
|
||||
workspace_id: workspace_id.to_string(),
|
||||
base: true,
|
||||
name: "Global Variables".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
@@ -98,4 +109,43 @@ impl<'a> DbContext<'a> {
|
||||
source,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn resolve_environments(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
folder_id: Option<&str>,
|
||||
active_environment_id: Option<&str>,
|
||||
) -> Result<Vec<Environment>> {
|
||||
let mut environments = Vec::new();
|
||||
|
||||
if let Some(folder_id) = folder_id {
|
||||
let folder = self.get_folder(folder_id)?;
|
||||
|
||||
// Add current folder's environment
|
||||
if let Some(e) = self.get_environment_by_folder_id(folder_id)? {
|
||||
environments.push(e);
|
||||
};
|
||||
|
||||
// Recurse up
|
||||
let ancestors = self.resolve_environments(
|
||||
workspace_id,
|
||||
folder.folder_id.as_deref(),
|
||||
active_environment_id,
|
||||
)?;
|
||||
environments.extend(ancestors);
|
||||
} else {
|
||||
// Add active and base environments
|
||||
if let Some(id) = active_environment_id {
|
||||
if let Ok(e) = self.get_environment(&id) {
|
||||
// Add active sub environment
|
||||
environments.push(e);
|
||||
};
|
||||
};
|
||||
|
||||
// Add the base environment
|
||||
environments.push(self.get_base_environment(workspace_id)?);
|
||||
}
|
||||
|
||||
Ok(environments)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
use crate::connection_or_tx::ConnectionOrTx;
|
||||
use crate::db_context::DbContext;
|
||||
use crate::error::Result;
|
||||
use crate::models::{
|
||||
Folder, FolderIden, GrpcRequest, GrpcRequestIden, HttpRequest, HttpRequestHeader,
|
||||
HttpRequestIden, WebsocketRequest, WebsocketRequestIden,
|
||||
};
|
||||
use crate::models::{Environment, EnvironmentIden, Folder, FolderIden, GrpcRequest, GrpcRequestIden, HttpRequest, HttpRequestHeader, HttpRequestIden, WebsocketRequest, WebsocketRequestIden};
|
||||
use crate::util::UpdateSource;
|
||||
use serde_json::Value;
|
||||
use std::collections::BTreeMap;
|
||||
@@ -37,6 +34,10 @@ impl<'a> DbContext<'a> {
|
||||
self.delete_websocket_request(&m, source)?;
|
||||
}
|
||||
|
||||
for e in self.find_many(EnvironmentIden::ParentId, fid, None)? {
|
||||
self.delete_environment(&e, source)?;
|
||||
}
|
||||
|
||||
// Recurse down into child folders
|
||||
for folder in self.find_many::<Folder>(FolderIden::FolderId, fid, None)? {
|
||||
self.delete_folder(&folder, source)?;
|
||||
@@ -99,6 +100,17 @@ impl<'a> DbContext<'a> {
|
||||
)?;
|
||||
}
|
||||
|
||||
for m in self.find_many::<Environment>(EnvironmentIden::ParentId, fid, None)? {
|
||||
self.upsert_environment(
|
||||
&Environment {
|
||||
id: "".into(),
|
||||
parent_id: Some(new_folder.id.clone()),
|
||||
..m
|
||||
},
|
||||
source,
|
||||
)?;
|
||||
}
|
||||
|
||||
for m in self.find_many::<Folder>(FolderIden::FolderId, fid, None)? {
|
||||
// Recurse down
|
||||
self.duplicate_folder(
|
||||
|
||||
@@ -31,6 +31,7 @@ impl<'a> DbContext<'a> {
|
||||
theme_dark: "yaak-dark".to_string(),
|
||||
theme_light: "yaak-light".to_string(),
|
||||
update_channel: "stable".to_string(),
|
||||
autoupdate: true,
|
||||
colored_methods: false,
|
||||
};
|
||||
self.upsert(&settings, &UpdateSource::Background).expect("Failed to upsert settings")
|
||||
|
||||
@@ -64,7 +64,7 @@ impl QueryManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect(&self) -> DbContext {
|
||||
pub fn connect(&self) -> DbContext<'_> {
|
||||
let conn = self
|
||||
.pool
|
||||
.lock()
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
use std::collections::HashMap;
|
||||
use crate::models::{Environment, EnvironmentVariable};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn make_vars_hashmap(
|
||||
base_environment: &Environment,
|
||||
environment: Option<&Environment>,
|
||||
) -> HashMap<String, String> {
|
||||
pub fn make_vars_hashmap(environment_chain: Vec<Environment>) -> HashMap<String, String> {
|
||||
let mut variables = HashMap::new();
|
||||
variables = add_variable_to_map(variables, &base_environment.variables);
|
||||
|
||||
if let Some(e) = environment {
|
||||
for e in environment_chain.iter().rev() {
|
||||
variables = add_variable_to_map(variables, &e.variables);
|
||||
}
|
||||
|
||||
@@ -31,4 +27,3 @@ fn add_variable_to_map(
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, base: boolean, variables: Array<EnvironmentVariable>, color: string | null, };
|
||||
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, variables: Array<EnvironmentVariable>, color: string | null, parentModel: string, parentId: string | null, };
|
||||
|
||||
export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||
|
||||
|
||||
@@ -584,8 +584,7 @@ impl PluginManager {
|
||||
pub async fn get_http_authentication_config<R: Runtime>(
|
||||
&self,
|
||||
window: &WebviewWindow<R>,
|
||||
base_environment: &Environment,
|
||||
environment: Option<&Environment>,
|
||||
environment_chain: Vec<Environment>,
|
||||
auth_name: &str,
|
||||
values: HashMap<String, JsonPrimitive>,
|
||||
request_id: &str,
|
||||
@@ -596,7 +595,7 @@ impl PluginManager {
|
||||
.find_map(|(p, r)| if r.name == auth_name { Some(p) } else { None })
|
||||
.ok_or(PluginNotFoundErr(auth_name.into()))?;
|
||||
|
||||
let vars = &make_vars_hashmap(&base_environment, environment);
|
||||
let vars = &make_vars_hashmap(environment_chain);
|
||||
let cb = PluginTemplateCallback::new(
|
||||
window.app_handle(),
|
||||
&PluginWindowContext::new(&window),
|
||||
@@ -629,14 +628,13 @@ impl PluginManager {
|
||||
pub async fn call_http_authentication_action<R: Runtime>(
|
||||
&self,
|
||||
window: &WebviewWindow<R>,
|
||||
base_environment: &Environment,
|
||||
environment: Option<&Environment>,
|
||||
environment_chain: Vec<Environment>,
|
||||
auth_name: &str,
|
||||
action_index: i32,
|
||||
values: HashMap<String, JsonPrimitive>,
|
||||
model_id: &str,
|
||||
) -> Result<()> {
|
||||
let vars = &make_vars_hashmap(&base_environment, environment);
|
||||
let vars = &make_vars_hashmap(environment_chain);
|
||||
let rendered_values = render_json_value_raw(
|
||||
json!(values),
|
||||
vars,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use crate::error::Result;
|
||||
use log::info;
|
||||
use serde;
|
||||
use serde::Deserialize;
|
||||
use log::{info, warn};
|
||||
use std::net::SocketAddr;
|
||||
use tauri::path::BaseDirectory;
|
||||
use tauri::{AppHandle, Manager, Runtime};
|
||||
@@ -9,12 +7,6 @@ use tauri_plugin_shell::process::CommandEvent;
|
||||
use tauri_plugin_shell::ShellExt;
|
||||
use tokio::sync::watch::Receiver;
|
||||
|
||||
#[derive(Deserialize, Default)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
struct PortFile {
|
||||
port: i32,
|
||||
}
|
||||
|
||||
pub async fn start_nodejs_plugin_runtime<R: Runtime>(
|
||||
app: &AppHandle<R>,
|
||||
addr: SocketAddr,
|
||||
@@ -44,10 +36,10 @@ pub async fn start_nodejs_plugin_runtime<R: Runtime>(
|
||||
while let Some(event) = child_rx.recv().await {
|
||||
match event {
|
||||
CommandEvent::Stderr(line) => {
|
||||
print!("{}", String::from_utf8(line).unwrap());
|
||||
warn!("{}", String::from_utf8_lossy(&line).trim_end_matches(&['\n', '\r'][..]));
|
||||
}
|
||||
CommandEvent::Stdout(line) => {
|
||||
print!("{}", String::from_utf8(line).unwrap());
|
||||
info!("{}", String::from_utf8_lossy(&line).trim_end_matches(&['\n', '\r'][..]));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, base: boolean, variables: Array<EnvironmentVariable>, color: string | null, };
|
||||
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, variables: Array<EnvironmentVariable>, color: string | null, parentModel: string, parentId: string | null, };
|
||||
|
||||
export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||
|
||||
|
||||
@@ -109,24 +109,18 @@ pub(crate) async fn send<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
ws_manager: State<'_, Mutex<WebsocketManager>>,
|
||||
) -> Result<WebsocketConnection> {
|
||||
let (connection, unrendered_request) = {
|
||||
let db = app_handle.db();
|
||||
let connection = db.get_websocket_connection(connection_id)?;
|
||||
let unrendered_request = db.get_websocket_request(&connection.request_id)?;
|
||||
(connection, unrendered_request)
|
||||
};
|
||||
let environment = match environment_id {
|
||||
Some(id) => Some(app_handle.db().get_environment(id)?),
|
||||
None => None,
|
||||
};
|
||||
let base_environment =
|
||||
app_handle.db().get_base_environment(&unrendered_request.workspace_id)?;
|
||||
let connection = app_handle.db().get_websocket_connection(connection_id)?;
|
||||
let unrendered_request = app_handle.db().get_websocket_request(&connection.request_id)?;
|
||||
let environment_chain = app_handle.db().resolve_environments(
|
||||
&unrendered_request.workspace_id,
|
||||
unrendered_request.folder_id.as_deref(),
|
||||
environment_id,
|
||||
)?;
|
||||
let (resolved_request, _auth_context_id) =
|
||||
resolve_websocket_request(&window, &unrendered_request)?;
|
||||
let request = render_websocket_request(
|
||||
&resolved_request,
|
||||
&base_environment,
|
||||
environment.as_ref(),
|
||||
environment_chain,
|
||||
&PluginTemplateCallback::new(
|
||||
&app_handle,
|
||||
&PluginWindowContext::new(&window),
|
||||
@@ -192,19 +186,17 @@ pub(crate) async fn connect<R: Runtime>(
|
||||
ws_manager: State<'_, Mutex<WebsocketManager>>,
|
||||
) -> Result<WebsocketConnection> {
|
||||
let unrendered_request = app_handle.db().get_websocket_request(request_id)?;
|
||||
let environment = match environment_id {
|
||||
Some(id) => Some(app_handle.db().get_environment(id)?),
|
||||
None => None,
|
||||
};
|
||||
let base_environment =
|
||||
app_handle.db().get_base_environment(&unrendered_request.workspace_id)?;
|
||||
let environment_chain = app_handle.db().resolve_environments(
|
||||
&unrendered_request.workspace_id,
|
||||
unrendered_request.folder_id.as_deref(),
|
||||
environment_id,
|
||||
)?;
|
||||
let workspace = app_handle.db().get_workspace(&unrendered_request.workspace_id)?;
|
||||
let (resolved_request, auth_context_id) =
|
||||
resolve_websocket_request(&window, &unrendered_request)?;
|
||||
let request = render_websocket_request(
|
||||
&resolved_request,
|
||||
&base_environment,
|
||||
environment.as_ref(),
|
||||
environment_chain,
|
||||
&PluginTemplateCallback::new(
|
||||
&app_handle,
|
||||
&PluginWindowContext::new(&window),
|
||||
@@ -305,7 +297,7 @@ pub(crate) async fn connect<R: Runtime>(
|
||||
|
||||
// Add cookies to WS HTTP Upgrade
|
||||
if let Some(id) = cookie_jar_id {
|
||||
let cookie_jar = app_handle.db().get_cookie_jar(id)?;
|
||||
let cookie_jar = app_handle.db().get_cookie_jar(&id)?;
|
||||
|
||||
let cookies = cookie_jar
|
||||
.cookies
|
||||
|
||||
@@ -7,16 +7,19 @@ use tokio_tungstenite::tungstenite::handshake::client::Response;
|
||||
use tokio_tungstenite::tungstenite::http::HeaderValue;
|
||||
use tokio_tungstenite::tungstenite::protocol::WebSocketConfig;
|
||||
use tokio_tungstenite::{
|
||||
connect_async_tls_with_config, Connector, MaybeTlsStream, WebSocketStream,
|
||||
Connector, MaybeTlsStream, WebSocketStream, connect_async_tls_with_config,
|
||||
};
|
||||
|
||||
// Enabling ALPN breaks websocket requests
|
||||
const WITH_ALPN: bool = false;
|
||||
|
||||
pub(crate) async fn ws_connect(
|
||||
url: &str,
|
||||
headers: HeaderMap<HeaderValue>,
|
||||
validate_certificates: bool,
|
||||
) -> crate::error::Result<(WebSocketStream<MaybeTlsStream<TcpStream>>, Response)> {
|
||||
info!("Connecting to WS {url}");
|
||||
let tls_config = yaak_http::tls::get_config(validate_certificates);
|
||||
let tls_config = yaak_http::tls::get_config(validate_certificates, WITH_ALPN);
|
||||
|
||||
let mut req = url.into_client_request()?;
|
||||
let req_headers = req.headers_mut();
|
||||
@@ -34,4 +37,4 @@ pub(crate) async fn ws_connect(
|
||||
)
|
||||
.await?;
|
||||
Ok((stream, response))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,10 @@ use yaak_templates::{parse_and_render, render_json_value_raw, TemplateCallback};
|
||||
|
||||
pub async fn render_websocket_request<T: TemplateCallback>(
|
||||
r: &WebsocketRequest,
|
||||
base_environment: &Environment,
|
||||
environment: Option<&Environment>,
|
||||
environment_chain: Vec<Environment>,
|
||||
cb: &T,
|
||||
) -> Result<WebsocketRequest> {
|
||||
let vars = &make_vars_hashmap(base_environment, environment);
|
||||
let vars = &make_vars_hashmap(environment_chain);
|
||||
|
||||
let mut headers = Vec::new();
|
||||
for p in r.headers.clone() {
|
||||
|
||||
@@ -37,7 +37,8 @@ export const createEnvironmentAndActivate = createFastMutation<
|
||||
name,
|
||||
variables: [],
|
||||
workspaceId,
|
||||
base: false,
|
||||
parentId: baseEnvironment.id,
|
||||
parentModel: 'environment',
|
||||
});
|
||||
},
|
||||
onSuccess: async (environmentId) => {
|
||||
|
||||
@@ -27,7 +27,8 @@ import { useSendAnyHttpRequest } from '../hooks/useSendAnyHttpRequest';
|
||||
import { useSidebarHidden } from '../hooks/useSidebarHidden';
|
||||
import { createRequestAndNavigate } from '../lib/createRequestAndNavigate';
|
||||
import { deleteModelWithConfirm } from '../lib/deleteModelWithConfirm';
|
||||
import { showDialog, toggleDialog } from '../lib/dialog';
|
||||
import { showDialog } from '../lib/dialog';
|
||||
import { editEnvironment } from '../lib/editEnvironment';
|
||||
import { renameModelWithPrompt } from '../lib/renameModelWithPrompt';
|
||||
import { resolvedModelNameWithFolders } from '../lib/resolvedModelName';
|
||||
import { router } from '../lib/router';
|
||||
@@ -40,7 +41,6 @@ import { HttpMethodTag } from './core/HttpMethodTag';
|
||||
import { Icon } from './core/Icon';
|
||||
import { PlainInput } from './core/PlainInput';
|
||||
import { HStack } from './core/Stacks';
|
||||
import { EnvironmentEditDialog } from './EnvironmentEditDialog';
|
||||
|
||||
interface CommandPaletteGroup {
|
||||
key: string;
|
||||
@@ -125,15 +125,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
|
||||
key: 'environment.edit',
|
||||
label: 'Edit Environment',
|
||||
action: 'environmentEditor.toggle',
|
||||
onSelect: () => {
|
||||
toggleDialog({
|
||||
id: 'environment-editor',
|
||||
noPadding: true,
|
||||
size: 'lg',
|
||||
className: 'h-[80vh]',
|
||||
render: () => <EnvironmentEditDialog initialEnvironment={activeEnvironment} />,
|
||||
});
|
||||
},
|
||||
onSelect: () => editEnvironment(activeEnvironment),
|
||||
},
|
||||
{
|
||||
key: 'environment.create',
|
||||
|
||||
@@ -51,7 +51,7 @@ export function ConfirmLargeResponse({ children, response }: Props) {
|
||||
color="secondary"
|
||||
variant="border"
|
||||
size="xs"
|
||||
text={() => getResponseBodyText(response)}
|
||||
text={() => getResponseBodyText({ responseId: response.id, filter: null })}
|
||||
/>
|
||||
)}
|
||||
</HStack>
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import classNames from 'classnames';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useActiveEnvironment } from '../hooks/useActiveEnvironment';
|
||||
import { useEnvironmentsBreakdown } from '../hooks/useEnvironmentsBreakdown';
|
||||
import { toggleDialog } from '../lib/dialog';
|
||||
import { editEnvironment } from '../lib/editEnvironment';
|
||||
import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams';
|
||||
import { Button } from './core/Button';
|
||||
import type { ButtonProps } from './core/Button';
|
||||
import { Button } from './core/Button';
|
||||
import type { DropdownItem } from './core/Dropdown';
|
||||
import { Dropdown } from './core/Dropdown';
|
||||
import { Icon } from './core/Icon';
|
||||
import { EnvironmentColorIndicator } from './EnvironmentColorIndicator';
|
||||
import { EnvironmentEditDialog } from './EnvironmentEditDialog';
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
@@ -23,16 +22,6 @@ export const EnvironmentActionsDropdown = memo(function EnvironmentActionsDropdo
|
||||
const { subEnvironments, baseEnvironment } = useEnvironmentsBreakdown();
|
||||
const activeEnvironment = useActiveEnvironment();
|
||||
|
||||
const showEnvironmentDialog = useCallback(() => {
|
||||
toggleDialog({
|
||||
id: 'environment-editor',
|
||||
noPadding: true,
|
||||
size: 'lg',
|
||||
className: 'h-[80vh]',
|
||||
render: () => <EnvironmentEditDialog initialEnvironment={activeEnvironment} />,
|
||||
});
|
||||
}, [activeEnvironment]);
|
||||
|
||||
const items: DropdownItem[] = useMemo(
|
||||
() => [
|
||||
...subEnvironments.map(
|
||||
@@ -55,14 +44,13 @@ export const EnvironmentActionsDropdown = memo(function EnvironmentActionsDropdo
|
||||
? [{ type: 'separator', label: 'Environments' }]
|
||||
: []) as DropdownItem[]),
|
||||
{
|
||||
key: 'edit',
|
||||
label: 'Manage Environments',
|
||||
hotKeyAction: 'environmentEditor.toggle',
|
||||
leftSlot: <Icon icon="box" />,
|
||||
onSelect: showEnvironmentDialog,
|
||||
onSelect: () => editEnvironment(activeEnvironment),
|
||||
},
|
||||
],
|
||||
[activeEnvironment?.id, subEnvironments, showEnvironmentDialog],
|
||||
[subEnvironments, activeEnvironment],
|
||||
);
|
||||
|
||||
const hasBaseVars =
|
||||
@@ -79,7 +67,7 @@ export const EnvironmentActionsDropdown = memo(function EnvironmentActionsDropdo
|
||||
)}
|
||||
// If no environments, the button simply opens the dialog.
|
||||
// NOTE: We don't create a new button because we want to reuse the hotkey from the menu items
|
||||
onClick={subEnvironments.length === 0 ? showEnvironmentDialog : undefined}
|
||||
onClick={subEnvironments.length === 0 ? () => editEnvironment(null) : undefined}
|
||||
{...buttonProps}
|
||||
>
|
||||
<EnvironmentColorIndicator environment={activeEnvironment ?? null} />
|
||||
|
||||
@@ -1,42 +1,28 @@
|
||||
import type { Environment } from '@yaakapp-internal/models';
|
||||
import { duplicateModel, patchModel } from '@yaakapp-internal/models';
|
||||
import type { GenericCompletionOption } from '@yaakapp-internal/plugins';
|
||||
import classNames from 'classnames';
|
||||
import type { ReactNode } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { createEnvironmentAndActivate } from '../commands/createEnvironment';
|
||||
import { useEnvironmentsBreakdown } from '../hooks/useEnvironmentsBreakdown';
|
||||
import { useIsEncryptionEnabled } from '../hooks/useIsEncryptionEnabled';
|
||||
import { useKeyValue } from '../hooks/useKeyValue';
|
||||
import { useRandomKey } from '../hooks/useRandomKey';
|
||||
import { deleteModelWithConfirm } from '../lib/deleteModelWithConfirm';
|
||||
import { analyzeTemplate, convertTemplateToSecure } from '../lib/encryption';
|
||||
import { isBaseEnvironment } from '../lib/model_util';
|
||||
import { showPrompt } from '../lib/prompt';
|
||||
import { resolvedModelName } from '../lib/resolvedModelName';
|
||||
import {
|
||||
setupOrConfigureEncryption,
|
||||
withEncryptionEnabled,
|
||||
} from '../lib/setupOrConfigureEncryption';
|
||||
import { showColorPicker } from '../lib/showColorPicker';
|
||||
import { BadgeButton } from './core/BadgeButton';
|
||||
import { Banner } from './core/Banner';
|
||||
import { Button } from './core/Button';
|
||||
import { DismissibleBanner } from './core/DismissibleBanner';
|
||||
import type { DropdownItem } from './core/Dropdown';
|
||||
import { ContextMenu } from './core/Dropdown';
|
||||
import type { GenericCompletionConfig } from './core/Editor/genericCompletion';
|
||||
import { Heading } from './core/Heading';
|
||||
import { Icon } from './core/Icon';
|
||||
import { IconButton } from './core/IconButton';
|
||||
import { IconTooltip } from './core/IconTooltip';
|
||||
import { InlineCode } from './core/InlineCode';
|
||||
import type { PairWithId } from './core/PairEditor';
|
||||
import { ensurePairId } from './core/PairEditor';
|
||||
import { PairOrBulkEditor } from './core/PairOrBulkEditor';
|
||||
import { Separator } from './core/Separator';
|
||||
import { SplitLayout } from './core/SplitLayout';
|
||||
import { VStack } from './core/Stacks';
|
||||
import { EnvironmentColorIndicator } from './EnvironmentColorIndicator';
|
||||
import { EnvironmentEditor } from './EnvironmentEditor';
|
||||
import { EnvironmentSharableTooltip } from './EnvironmentSharableTooltip';
|
||||
|
||||
interface Props {
|
||||
initialEnvironment: Environment | null;
|
||||
@@ -97,13 +83,13 @@ export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) {
|
||||
<aside className="w-full min-w-0 pt-2">
|
||||
<div className="min-w-0 h-full overflow-y-auto pt-1">
|
||||
{[baseEnvironment, ...otherBaseEnvironments].map((e) => (
|
||||
<SidebarButton
|
||||
<EnvironmentDialogSidebarButton
|
||||
key={e.id}
|
||||
active={selectedEnvironment?.id == e.id}
|
||||
onClick={() => setSelectedEnvironmentId(e.id)}
|
||||
environment={e}
|
||||
duplicateEnvironment={handleDuplicateEnvironment}
|
||||
// Allow deleting base environment if there are multiples
|
||||
// Allow deleting the base environment if there are multiples
|
||||
deleteEnvironment={
|
||||
otherBaseEnvironments.length > 0 ? handleDeleteEnvironment : null
|
||||
}
|
||||
@@ -121,7 +107,7 @@ export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) {
|
||||
}
|
||||
>
|
||||
{resolvedModelName(e)}
|
||||
</SidebarButton>
|
||||
</EnvironmentDialogSidebarButton>
|
||||
))}
|
||||
{subEnvironments.length > 0 && (
|
||||
<div className="px-2">
|
||||
@@ -129,7 +115,7 @@ export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) {
|
||||
</div>
|
||||
)}
|
||||
{subEnvironments.map((e) => (
|
||||
<SidebarButton
|
||||
<EnvironmentDialogSidebarButton
|
||||
key={e.id}
|
||||
active={selectedEnvironment?.id === e.id}
|
||||
environment={e}
|
||||
@@ -139,7 +125,7 @@ export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) {
|
||||
deleteEnvironment={handleDeleteEnvironment}
|
||||
>
|
||||
{e.name}
|
||||
</SidebarButton>
|
||||
</EnvironmentDialogSidebarButton>
|
||||
))}
|
||||
</div>
|
||||
</aside>
|
||||
@@ -153,7 +139,7 @@ export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) {
|
||||
</div>
|
||||
) : (
|
||||
<EnvironmentEditor
|
||||
className="pt-2 border-l border-border-subtle"
|
||||
className="pl-4 pt-3 border-l border-border-subtle"
|
||||
environment={selectedEnvironment}
|
||||
/>
|
||||
)
|
||||
@@ -162,139 +148,7 @@ export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) {
|
||||
);
|
||||
};
|
||||
|
||||
const EnvironmentEditor = function ({
|
||||
environment: selectedEnvironment,
|
||||
className,
|
||||
}: {
|
||||
environment: Environment;
|
||||
className?: string;
|
||||
}) {
|
||||
const workspaceId = selectedEnvironment.workspaceId;
|
||||
const isEncryptionEnabled = useIsEncryptionEnabled();
|
||||
const valueVisibility = useKeyValue<boolean>({
|
||||
namespace: 'global',
|
||||
key: ['environmentValueVisibility', workspaceId],
|
||||
fallback: false,
|
||||
});
|
||||
const { allEnvironments } = useEnvironmentsBreakdown();
|
||||
const handleChange = useCallback(
|
||||
(variables: PairWithId[]) => patchModel(selectedEnvironment, { variables }),
|
||||
[selectedEnvironment],
|
||||
);
|
||||
const [forceUpdateKey, regenerateForceUpdateKey] = useRandomKey();
|
||||
|
||||
// Gather a list of env names from other environments to help the user get them aligned
|
||||
const nameAutocomplete = useMemo<GenericCompletionConfig>(() => {
|
||||
const options: GenericCompletionOption[] = [];
|
||||
if (selectedEnvironment.base) {
|
||||
return { options };
|
||||
}
|
||||
|
||||
const allVariables = allEnvironments.flatMap((e) => e?.variables);
|
||||
const allVariableNames = new Set(allVariables.map((v) => v?.name));
|
||||
for (const name of allVariableNames) {
|
||||
const containingEnvs = allEnvironments.filter((e) =>
|
||||
e.variables.some((v) => v.name === name),
|
||||
);
|
||||
const isAlreadyInActive = containingEnvs.find((e) => e.id === selectedEnvironment.id);
|
||||
if (isAlreadyInActive) continue;
|
||||
options.push({
|
||||
label: name,
|
||||
type: 'constant',
|
||||
detail: containingEnvs.map((e) => e.name).join(', '),
|
||||
});
|
||||
}
|
||||
return { options };
|
||||
}, [selectedEnvironment.base, selectedEnvironment.id, allEnvironments]);
|
||||
|
||||
const validateName = useCallback((name: string) => {
|
||||
// Empty just means the variable doesn't have a name yet and is unusable
|
||||
if (name === '') return true;
|
||||
return name.match(/^[a-z_][a-z0-9_-]*$/i) != null;
|
||||
}, []);
|
||||
|
||||
const valueType = !isEncryptionEnabled && valueVisibility.value ? 'text' : 'password';
|
||||
const promptToEncrypt = useMemo(() => {
|
||||
if (!isEncryptionEnabled) {
|
||||
return true;
|
||||
} else {
|
||||
return !selectedEnvironment.variables.every(
|
||||
(v) => v.value === '' || analyzeTemplate(v.value) !== 'insecure',
|
||||
);
|
||||
}
|
||||
}, [selectedEnvironment.variables, isEncryptionEnabled]);
|
||||
|
||||
const encryptEnvironment = (environment: Environment) => {
|
||||
withEncryptionEnabled(async () => {
|
||||
const encryptedVariables: PairWithId[] = [];
|
||||
for (const variable of environment.variables) {
|
||||
const value = variable.value ? await convertTemplateToSecure(variable.value) : '';
|
||||
encryptedVariables.push(ensurePairId({ ...variable, value }));
|
||||
}
|
||||
await handleChange(encryptedVariables);
|
||||
regenerateForceUpdateKey();
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<VStack space={4} className={classNames(className, 'pl-4')}>
|
||||
<Heading className="w-full flex items-center gap-0.5">
|
||||
<EnvironmentColorIndicator clickToEdit environment={selectedEnvironment ?? null} />
|
||||
<div className="mr-2">{selectedEnvironment?.name}</div>
|
||||
{isEncryptionEnabled ? (
|
||||
promptToEncrypt ? (
|
||||
<BadgeButton color="notice" onClick={() => encryptEnvironment(selectedEnvironment)}>
|
||||
Encrypt All Variables
|
||||
</BadgeButton>
|
||||
) : (
|
||||
<BadgeButton color="secondary" onClick={setupOrConfigureEncryption}>
|
||||
Encryption Settings
|
||||
</BadgeButton>
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
<BadgeButton color="secondary" onClick={() => valueVisibility.set((v) => !v)}>
|
||||
{valueVisibility.value ? 'Conceal Values' : 'Reveal Values'}
|
||||
</BadgeButton>
|
||||
</>
|
||||
)}
|
||||
</Heading>
|
||||
{selectedEnvironment.public && promptToEncrypt && (
|
||||
<DismissibleBanner
|
||||
id={`warn-unencrypted-${selectedEnvironment.id}`}
|
||||
color="notice"
|
||||
className="mr-3"
|
||||
>
|
||||
This environment is sharable. Ensure variable values are encrypted to avoid accidental
|
||||
leaking of secrets during directory sync or data export.
|
||||
</DismissibleBanner>
|
||||
)}
|
||||
<div className="h-full pr-2 pb-2 grid grid-rows-[minmax(0,1fr)] overflow-auto">
|
||||
<PairOrBulkEditor
|
||||
allowMultilineValues
|
||||
preferenceName="environment"
|
||||
nameAutocomplete={nameAutocomplete}
|
||||
namePlaceholder="VAR_NAME"
|
||||
nameValidate={validateName}
|
||||
valueType={valueType}
|
||||
valueAutocompleteVariables
|
||||
valueAutocompleteFunctions
|
||||
forceUpdateKey={`${selectedEnvironment.id}::${forceUpdateKey}`}
|
||||
pairs={selectedEnvironment.variables}
|
||||
onChange={handleChange}
|
||||
stateKey={`environment.${selectedEnvironment.id}`}
|
||||
forcedEnvironmentId={
|
||||
// Editing the base environment should resolve variables using the active environment.
|
||||
// Editing a sub environment should resolve variables as if it's the active environment
|
||||
selectedEnvironment.base ? undefined : selectedEnvironment.id
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</VStack>
|
||||
);
|
||||
};
|
||||
|
||||
function SidebarButton({
|
||||
function EnvironmentDialogSidebarButton({
|
||||
children,
|
||||
className,
|
||||
active,
|
||||
@@ -359,7 +213,7 @@ function SidebarButton({
|
||||
{
|
||||
label: 'Rename',
|
||||
leftSlot: <Icon icon="pencil" />,
|
||||
hidden: environment.base,
|
||||
hidden: isBaseEnvironment(environment),
|
||||
onSelect: async () => {
|
||||
const name = await showPrompt({
|
||||
id: 'rename-environment',
|
||||
@@ -392,23 +246,13 @@ function SidebarButton({
|
||||
{
|
||||
label: environment.color ? 'Change Color' : 'Assign Color',
|
||||
leftSlot: <Icon icon="palette" />,
|
||||
hidden: environment.base,
|
||||
hidden: isBaseEnvironment(environment),
|
||||
onSelect: async () => showColorPicker(environment),
|
||||
},
|
||||
{
|
||||
label: `Make ${environment.public ? 'Private' : 'Sharable'}`,
|
||||
leftSlot: <Icon icon={environment.public ? 'eye_closed' : 'eye'} />,
|
||||
rightSlot: (
|
||||
<IconTooltip
|
||||
content={
|
||||
<>
|
||||
Sharable environments will be included in Directory Sync or data export. It is
|
||||
recommended to encrypt all variable values within sharable environments to
|
||||
prevent accidentally leaking secrets.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
),
|
||||
rightSlot: <EnvironmentSharableTooltip />,
|
||||
onSelect: async () => {
|
||||
await patchModel(environment, { public: !environment.public });
|
||||
},
|
||||
|
||||
169
src-web/components/EnvironmentEditor.tsx
Normal file
169
src-web/components/EnvironmentEditor.tsx
Normal file
@@ -0,0 +1,169 @@
|
||||
import type { Environment } from '@yaakapp-internal/models';
|
||||
import { patchModel } from '@yaakapp-internal/models';
|
||||
import type { GenericCompletionOption } from '@yaakapp-internal/plugins';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useEnvironmentsBreakdown } from '../hooks/useEnvironmentsBreakdown';
|
||||
import { useIsEncryptionEnabled } from '../hooks/useIsEncryptionEnabled';
|
||||
import { useKeyValue } from '../hooks/useKeyValue';
|
||||
import { useRandomKey } from '../hooks/useRandomKey';
|
||||
import { analyzeTemplate, convertTemplateToSecure } from '../lib/encryption';
|
||||
import { isBaseEnvironment } from '../lib/model_util';
|
||||
import {
|
||||
setupOrConfigureEncryption,
|
||||
withEncryptionEnabled,
|
||||
} from '../lib/setupOrConfigureEncryption';
|
||||
import { BadgeButton } from './core/BadgeButton';
|
||||
import { DismissibleBanner } from './core/DismissibleBanner';
|
||||
import type { GenericCompletionConfig } from './core/Editor/genericCompletion';
|
||||
import { Heading } from './core/Heading';
|
||||
import type { PairWithId } from './core/PairEditor';
|
||||
import { ensurePairId } from './core/PairEditor';
|
||||
import { PairOrBulkEditor } from './core/PairOrBulkEditor';
|
||||
import { VStack } from './core/Stacks';
|
||||
import { EnvironmentColorIndicator } from './EnvironmentColorIndicator';
|
||||
import { EnvironmentSharableTooltip } from './EnvironmentSharableTooltip';
|
||||
|
||||
export function EnvironmentEditor({
|
||||
environment: selectedEnvironment,
|
||||
hideName,
|
||||
className,
|
||||
}: {
|
||||
environment: Environment;
|
||||
hideName?: boolean;
|
||||
className?: string;
|
||||
}) {
|
||||
const workspaceId = selectedEnvironment.workspaceId;
|
||||
const isEncryptionEnabled = useIsEncryptionEnabled();
|
||||
const valueVisibility = useKeyValue<boolean>({
|
||||
namespace: 'global',
|
||||
key: ['environmentValueVisibility', workspaceId],
|
||||
fallback: false,
|
||||
});
|
||||
const { allEnvironments } = useEnvironmentsBreakdown();
|
||||
const handleChange = useCallback(
|
||||
(variables: PairWithId[]) => patchModel(selectedEnvironment, { variables }),
|
||||
[selectedEnvironment],
|
||||
);
|
||||
const [forceUpdateKey, regenerateForceUpdateKey] = useRandomKey();
|
||||
|
||||
// Gather a list of env names from other environments to help the user get them aligned
|
||||
const nameAutocomplete = useMemo<GenericCompletionConfig>(() => {
|
||||
const options: GenericCompletionOption[] = [];
|
||||
if (isBaseEnvironment(selectedEnvironment)) {
|
||||
return { options };
|
||||
}
|
||||
|
||||
const allVariables = allEnvironments.flatMap((e) => e?.variables);
|
||||
const allVariableNames = new Set(allVariables.map((v) => v?.name));
|
||||
for (const name of allVariableNames) {
|
||||
const containingEnvs = allEnvironments.filter((e) =>
|
||||
e.variables.some((v) => v.name === name),
|
||||
);
|
||||
const isAlreadyInActive = containingEnvs.find((e) => e.id === selectedEnvironment.id);
|
||||
if (isAlreadyInActive) continue;
|
||||
options.push({
|
||||
label: name,
|
||||
type: 'constant',
|
||||
detail: containingEnvs.map((e) => e.name).join(', '),
|
||||
});
|
||||
}
|
||||
return { options };
|
||||
}, [selectedEnvironment, allEnvironments]);
|
||||
|
||||
const validateName = useCallback((name: string) => {
|
||||
// Empty just means the variable doesn't have a name yet and is unusable
|
||||
if (name === '') return true;
|
||||
return name.match(/^[a-z_][a-z0-9_-]*$/i) != null;
|
||||
}, []);
|
||||
|
||||
const valueType = !isEncryptionEnabled && valueVisibility.value ? 'text' : 'password';
|
||||
const allVariableAreEncrypted = useMemo(
|
||||
() =>
|
||||
selectedEnvironment.variables.every(
|
||||
(v) => v.value === '' || analyzeTemplate(v.value) !== 'insecure',
|
||||
),
|
||||
[selectedEnvironment.variables],
|
||||
);
|
||||
|
||||
const encryptEnvironment = (environment: Environment) => {
|
||||
withEncryptionEnabled(async () => {
|
||||
const encryptedVariables: PairWithId[] = [];
|
||||
for (const variable of environment.variables) {
|
||||
const value = variable.value ? await convertTemplateToSecure(variable.value) : '';
|
||||
encryptedVariables.push(ensurePairId({ ...variable, value }));
|
||||
}
|
||||
await handleChange(encryptedVariables);
|
||||
regenerateForceUpdateKey();
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<VStack space={4} className={className}>
|
||||
<Heading className="w-full flex items-center gap-0.5">
|
||||
<EnvironmentColorIndicator clickToEdit environment={selectedEnvironment ?? null} />
|
||||
{!hideName && <div className="mr-2">{selectedEnvironment?.name}</div>}
|
||||
{isEncryptionEnabled ? (
|
||||
!allVariableAreEncrypted ? (
|
||||
<BadgeButton color="notice" onClick={() => encryptEnvironment(selectedEnvironment)}>
|
||||
Encrypt All Variables
|
||||
</BadgeButton>
|
||||
) : (
|
||||
<BadgeButton color="secondary" onClick={setupOrConfigureEncryption}>
|
||||
Encryption Settings
|
||||
</BadgeButton>
|
||||
)
|
||||
) : (
|
||||
<BadgeButton color="secondary" onClick={() => valueVisibility.set((v) => !v)}>
|
||||
{valueVisibility.value ? 'Hide Values' : 'Show Values'}
|
||||
</BadgeButton>
|
||||
)}
|
||||
<BadgeButton
|
||||
color="secondary"
|
||||
rightSlot={<EnvironmentSharableTooltip />}
|
||||
onClick={async () => {
|
||||
await patchModel(selectedEnvironment, { public: !selectedEnvironment.public });
|
||||
}}
|
||||
>
|
||||
{selectedEnvironment.public ? 'Sharable' : 'Private'}
|
||||
</BadgeButton>
|
||||
</Heading>
|
||||
{selectedEnvironment.public && (!isEncryptionEnabled || !allVariableAreEncrypted) && (
|
||||
<DismissibleBanner
|
||||
id={`warn-unencrypted-${selectedEnvironment.id}`}
|
||||
color="notice"
|
||||
className="mr-3"
|
||||
actions={[
|
||||
{
|
||||
label: 'Encrypt Variables',
|
||||
onClick: () => encryptEnvironment(selectedEnvironment),
|
||||
color: 'primary',
|
||||
},
|
||||
]}
|
||||
>
|
||||
This sharable environment contains plain-text secrets
|
||||
</DismissibleBanner>
|
||||
)}
|
||||
<div className="h-full pr-2 pb-2 grid grid-rows-[minmax(0,1fr)] overflow-auto">
|
||||
<PairOrBulkEditor
|
||||
allowMultilineValues
|
||||
preferenceName="environment"
|
||||
nameAutocomplete={nameAutocomplete}
|
||||
namePlaceholder="VAR_NAME"
|
||||
nameValidate={validateName}
|
||||
valueType={valueType}
|
||||
valueAutocompleteVariables
|
||||
valueAutocompleteFunctions
|
||||
forceUpdateKey={`${selectedEnvironment.id}::${forceUpdateKey}`}
|
||||
pairs={selectedEnvironment.variables}
|
||||
onChange={handleChange}
|
||||
stateKey={`environment.${selectedEnvironment.id}`}
|
||||
forcedEnvironmentId={
|
||||
// Editing the base environment should resolve variables using the active environment.
|
||||
// Editing a sub environment should resolve variables as if it's the active environment
|
||||
isBaseEnvironment(selectedEnvironment) ? undefined : selectedEnvironment.id
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</VStack>
|
||||
);
|
||||
}
|
||||
8
src-web/components/EnvironmentSharableTooltip.tsx
Normal file
8
src-web/components/EnvironmentSharableTooltip.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import React from 'react';
|
||||
import { IconTooltip } from './core/IconTooltip';
|
||||
|
||||
export function EnvironmentSharableTooltip() {
|
||||
return (
|
||||
<IconTooltip content="Sharable environments are included in Directory Sync and data export." />
|
||||
);
|
||||
}
|
||||
@@ -1,13 +1,19 @@
|
||||
import { foldersAtom, patchModel } from '@yaakapp-internal/models';
|
||||
import { createWorkspaceModel, foldersAtom, patchModel } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useMemo, useState } from 'react';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { useAuthTab } from '../hooks/useAuthTab';
|
||||
import { useEnvironmentsBreakdown } from '../hooks/useEnvironmentsBreakdown';
|
||||
import { useHeadersTab } from '../hooks/useHeadersTab';
|
||||
import { useInheritedHeaders } from '../hooks/useInheritedHeaders';
|
||||
import { Button } from './core/Button';
|
||||
import { CountBadge } from './core/CountBadge';
|
||||
import { Input } from './core/Input';
|
||||
import { Link } from './core/Link';
|
||||
import { VStack } from './core/Stacks';
|
||||
import type { TabItem } from './core/Tabs/Tabs';
|
||||
import { TabContent, Tabs } from './core/Tabs/Tabs';
|
||||
import { EmptyStateText } from './EmptyStateText';
|
||||
import { EnvironmentEditor } from './EnvironmentEditor';
|
||||
import { HeadersEditor } from './HeadersEditor';
|
||||
import { HttpAuthenticationEditor } from './HttpAuthenticationEditor';
|
||||
import { MarkdownEditor } from './MarkdownEditor';
|
||||
@@ -19,9 +25,10 @@ interface Props {
|
||||
|
||||
const TAB_AUTH = 'auth';
|
||||
const TAB_HEADERS = 'headers';
|
||||
const TAB_VARIABLES = 'variables';
|
||||
const TAB_GENERAL = 'general';
|
||||
|
||||
export type FolderSettingsTab = typeof TAB_AUTH | typeof TAB_HEADERS | typeof TAB_GENERAL;
|
||||
export type FolderSettingsTab = typeof TAB_AUTH | typeof TAB_HEADERS | typeof TAB_GENERAL | typeof TAB_VARIABLES;
|
||||
|
||||
export function FolderSettingsDialog({ folderId, tab }: Props) {
|
||||
const folders = useAtomValue(foldersAtom);
|
||||
@@ -30,6 +37,11 @@ export function FolderSettingsDialog({ folderId, tab }: Props) {
|
||||
const authTab = useAuthTab(TAB_AUTH, folder);
|
||||
const headersTab = useHeadersTab(TAB_HEADERS, folder);
|
||||
const inheritedHeaders = useInheritedHeaders(folder);
|
||||
const environments = useEnvironmentsBreakdown();
|
||||
const folderEnvironment = environments.allEnvironments.find(
|
||||
(e) => e.parentModel === 'folder' && e.parentId === folderId,
|
||||
);
|
||||
const numVars = (folderEnvironment?.variables ?? []).filter((v) => v.name).length;
|
||||
|
||||
const tabs = useMemo<TabItem[]>(() => {
|
||||
if (folder == null) return [];
|
||||
@@ -39,10 +51,15 @@ export function FolderSettingsDialog({ folderId, tab }: Props) {
|
||||
value: TAB_GENERAL,
|
||||
label: 'General',
|
||||
},
|
||||
...authTab,
|
||||
...headersTab,
|
||||
...authTab,
|
||||
{
|
||||
value: TAB_VARIABLES,
|
||||
label: 'Variables',
|
||||
rightSlot: numVars > 0 ? <CountBadge count={numVars} /> : null,
|
||||
},
|
||||
];
|
||||
}, [authTab, folder, headersTab]);
|
||||
}, [authTab, folder, headersTab, numVars]);
|
||||
|
||||
if (folder == null) return null;
|
||||
|
||||
@@ -85,6 +102,38 @@ export function FolderSettingsDialog({ folderId, tab }: Props) {
|
||||
stateKey={`headers.${folder.id}`}
|
||||
/>
|
||||
</TabContent>
|
||||
<TabContent value={TAB_VARIABLES} className="pt-3 overflow-y-auto h-full px-4">
|
||||
{folderEnvironment == null ? (
|
||||
<EmptyStateText>
|
||||
<VStack alignItems="center" space={1.5}>
|
||||
<p>
|
||||
Override{' '}
|
||||
<Link href="https://feedback.yaak.app/help/articles/3284139-environments-and-variables">
|
||||
Variables
|
||||
</Link>{' '}
|
||||
for requests within this folder.
|
||||
</p>
|
||||
<Button
|
||||
variant="border"
|
||||
size="sm"
|
||||
onClick={async () => {
|
||||
await createWorkspaceModel({
|
||||
workspaceId: folder.workspaceId,
|
||||
parentModel: 'folder',
|
||||
parentId: folder.id,
|
||||
model: 'environment',
|
||||
name: 'Folder Environment',
|
||||
});
|
||||
}}
|
||||
>
|
||||
Create Folder Environment
|
||||
</Button>
|
||||
</VStack>
|
||||
</EmptyStateText>
|
||||
) : (
|
||||
<EnvironmentEditor hideName environment={folderEnvironment} />
|
||||
)}
|
||||
</TabContent>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ function GrpcProtoSelectionDialogWithRequest({ request }: Props & { request: Grp
|
||||
return (
|
||||
<VStack className="flex-col-reverse mb-3" space={3}>
|
||||
{/* Buttons on top so they get focus first */}
|
||||
<HStack space={2} justifyContent="start" className="flex-row-reverse">
|
||||
<HStack space={2} justifyContent="start" className="flex-row-reverse mt-3">
|
||||
<Button
|
||||
color="primary"
|
||||
variant="border"
|
||||
@@ -135,9 +135,7 @@ function GrpcProtoSelectionDialogWithRequest({ request }: Props & { request: Grp
|
||||
<table className="w-full divide-y divide-surface-highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th />
|
||||
<th className="text-text-subtlest">Added File Paths</th>
|
||||
<th />
|
||||
<th className="text-text-subtlest" colSpan={3}>Added File Paths</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-surface-highlight">
|
||||
|
||||
@@ -32,7 +32,7 @@ export function HttpAuthenticationEditor({ model }: Props) {
|
||||
const authConfig = useHttpAuthenticationConfig(
|
||||
model.authenticationType,
|
||||
model.authentication,
|
||||
model.id,
|
||||
model,
|
||||
);
|
||||
|
||||
const handleChange = useCallback(
|
||||
|
||||
@@ -49,6 +49,21 @@ export function SettingsGeneral() {
|
||||
onClick={() => checkForUpdates.mutateAsync()}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Select
|
||||
name="autoupdate"
|
||||
value={settings.autoupdate ? 'auto' : 'manual'}
|
||||
label="Update Behavior"
|
||||
labelPosition="left"
|
||||
size="sm"
|
||||
labelClassName="w-[14rem]"
|
||||
onChange={(v) => patchModel(settings, { autoupdate: v === 'auto' })}
|
||||
options={[
|
||||
{ label: 'Automatic', value: 'auto' },
|
||||
{ label: 'Manual', value: 'manual' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<Select
|
||||
name="switchWorkspaceBehavior"
|
||||
label="Workspace Window Behavior"
|
||||
|
||||
@@ -75,8 +75,8 @@ export function WorkspaceSettingsDialog({ workspaceId, hide, tab }: Props) {
|
||||
value: TAB_DATA,
|
||||
label: 'Directory Sync',
|
||||
},
|
||||
...authTab,
|
||||
...headersTab,
|
||||
...authTab,
|
||||
]}
|
||||
>
|
||||
<TabContent value={TAB_AUTH} className="overflow-y-auto h-full px-4">
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import type { Color } from '@yaakapp-internal/plugins';
|
||||
import classNames from 'classnames';
|
||||
import { useKeyValue } from '../../hooks/useKeyValue';
|
||||
import type { BannerProps } from './Banner';
|
||||
import { Banner } from './Banner';
|
||||
import { Button } from './Button';
|
||||
import { HStack } from './Stacks';
|
||||
|
||||
export function DismissibleBanner({
|
||||
children,
|
||||
className,
|
||||
id,
|
||||
actions,
|
||||
...props
|
||||
}: BannerProps & { id: string }) {
|
||||
}: BannerProps & { id: string; actions?: { label: string; onClick: () => void; color?: Color }[] }) {
|
||||
const { set: setDismissed, value: dismissed } = useKeyValue<boolean>({
|
||||
namespace: 'global',
|
||||
key: ['dismiss-banner', id],
|
||||
@@ -19,17 +22,34 @@ export function DismissibleBanner({
|
||||
if (dismissed) return null;
|
||||
|
||||
return (
|
||||
<Banner className={classNames(className, 'relative grid grid-cols-[1fr_auto] gap-3')} {...props}>
|
||||
<Banner
|
||||
className={classNames(className, 'relative grid grid-cols-[1fr_auto] gap-3')}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<Button
|
||||
variant="border"
|
||||
color={props.color}
|
||||
size="xs"
|
||||
onClick={() => setDismissed((d) => !d)}
|
||||
title="Dismiss message"
|
||||
>
|
||||
Dismiss
|
||||
</Button>
|
||||
<HStack space={1.5}>
|
||||
{actions?.map((a, i) => (
|
||||
<Button
|
||||
key={a.label + i}
|
||||
variant="border"
|
||||
color={a.color ?? props.color}
|
||||
size="xs"
|
||||
onClick={a.onClick}
|
||||
title="Dismiss message"
|
||||
>
|
||||
{a.label}
|
||||
</Button>
|
||||
))}
|
||||
<Button
|
||||
variant="border"
|
||||
color={props.color}
|
||||
size="xs"
|
||||
onClick={() => setDismissed((d) => !d)}
|
||||
title="Dismiss message"
|
||||
>
|
||||
Dismiss
|
||||
</Button>
|
||||
</HStack>
|
||||
</Banner>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -519,7 +519,7 @@ const Menu = forwardRef<Omit<DropdownRef, 'open' | 'isOpen' | 'toggle' | 'items'
|
||||
className={classNames(
|
||||
className,
|
||||
'x-theme-menu',
|
||||
'outline-none my-1 pointer-events-auto fixed z-50',
|
||||
'outline-none my-1 pointer-events-auto fixed z-40',
|
||||
)}
|
||||
>
|
||||
{showTriangle && (
|
||||
|
||||
@@ -7,7 +7,7 @@ import { emacs } from '@replit/codemirror-emacs';
|
||||
import { vim } from '@replit/codemirror-vim';
|
||||
|
||||
import { vscodeKeymap } from '@replit/codemirror-vscode-keymap';
|
||||
import type { EditorKeymap, EnvironmentVariable } from '@yaakapp-internal/models';
|
||||
import type { EditorKeymap } from '@yaakapp-internal/models';
|
||||
import { settingsAtom } from '@yaakapp-internal/models';
|
||||
import type { EditorLanguage, TemplateFunction } from '@yaakapp-internal/plugins';
|
||||
import { parseTemplate } from '@yaakapp-internal/templates';
|
||||
@@ -28,10 +28,12 @@ import {
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { activeEnvironmentIdAtom } from '../../../hooks/useActiveEnvironment';
|
||||
import type { WrappedEnvironmentVariable } from '../../../hooks/useEnvironmentVariables';
|
||||
import { useEnvironmentVariables } from '../../../hooks/useEnvironmentVariables';
|
||||
import { useRequestEditor } from '../../../hooks/useRequestEditor';
|
||||
import { useTemplateFunctionCompletionOptions } from '../../../hooks/useTemplateFunctions';
|
||||
import { showDialog } from '../../../lib/dialog';
|
||||
import { editEnvironment } from '../../../lib/editEnvironment';
|
||||
import { tryFormatJson, tryFormatXml } from '../../../lib/formatters';
|
||||
import { withEncryptionEnabled } from '../../../lib/setupOrConfigureEncryption';
|
||||
import { TemplateFunctionDialog } from '../../TemplateFunctionDialog';
|
||||
@@ -96,7 +98,7 @@ export interface EditorProps {
|
||||
|
||||
const stateFields = { history: historyField, folds: foldState };
|
||||
|
||||
const emptyVariables: EnvironmentVariable[] = [];
|
||||
const emptyVariables: WrappedEnvironmentVariable[] = [];
|
||||
const emptyExtension: Extension = [];
|
||||
|
||||
export const Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor(
|
||||
@@ -306,24 +308,9 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
);
|
||||
|
||||
const onClickVariable = useCallback(
|
||||
async (_v: EnvironmentVariable, tagValue: string, startPos: number) => {
|
||||
const initialTokens = parseTemplate(tagValue);
|
||||
showDialog({
|
||||
size: 'dynamic',
|
||||
id: 'template-variable',
|
||||
title: 'Change Variable',
|
||||
render: ({ hide }) => (
|
||||
<TemplateVariableDialog
|
||||
hide={hide}
|
||||
initialTokens={initialTokens}
|
||||
onChange={(insert) => {
|
||||
cm.current?.view.dispatch({
|
||||
changes: [{ from: startPos, to: startPos + tagValue.length, insert }],
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async (v: WrappedEnvironmentVariable, _tagValue: string, _startPos: number) => {
|
||||
editEnvironment(v.environment);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
@@ -35,10 +35,10 @@ import {
|
||||
rectangularSelection,
|
||||
} from '@codemirror/view';
|
||||
import { tags as t } from '@lezer/highlight';
|
||||
import type { EnvironmentVariable } from '@yaakapp-internal/models';
|
||||
import { graphql } from 'cm6-graphql';
|
||||
import type { GraphQLSchema } from 'graphql';
|
||||
import { activeRequestIdAtom } from '../../../hooks/useActiveRequestId';
|
||||
import type { WrappedEnvironmentVariable } from '../../../hooks/useEnvironmentVariables';
|
||||
import { jotaiStore } from '../../../lib/jotai';
|
||||
import { renderMarkdown } from '../../../lib/markdown';
|
||||
import { pluralizeCount } from '../../../lib/pluralize';
|
||||
@@ -110,8 +110,8 @@ export function getLanguageExtension({
|
||||
graphQLSchema,
|
||||
}: {
|
||||
useTemplating: boolean;
|
||||
environmentVariables: EnvironmentVariable[];
|
||||
onClickVariable: (option: EnvironmentVariable, tagValue: string, startPos: number) => void;
|
||||
environmentVariables: WrappedEnvironmentVariable[];
|
||||
onClickVariable: (option: WrappedEnvironmentVariable, tagValue: string, startPos: number) => void;
|
||||
onClickMissingVariable: (name: string, tagValue: string, startPos: number) => void;
|
||||
onClickPathParameter: (name: string) => void;
|
||||
completionOptions: TwigCompletionOption[];
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { LanguageSupport } from '@codemirror/language';
|
||||
import { LRLanguage } from '@codemirror/language';
|
||||
import type { Extension } from '@codemirror/state';
|
||||
import { parseMixed } from '@lezer/common';
|
||||
import type { EnvironmentVariable } from '@yaakapp-internal/models';
|
||||
import type { WrappedEnvironmentVariable } from '../../../../hooks/useEnvironmentVariables';
|
||||
import type { GenericCompletionConfig } from '../genericCompletion';
|
||||
import { genericCompletion } from '../genericCompletion';
|
||||
import { textLanguage } from '../text/extension';
|
||||
@@ -21,10 +21,10 @@ export function twig({
|
||||
extraExtensions,
|
||||
}: {
|
||||
base: LanguageSupport;
|
||||
environmentVariables: EnvironmentVariable[];
|
||||
environmentVariables: WrappedEnvironmentVariable[];
|
||||
completionOptions: TwigCompletionOption[];
|
||||
autocomplete?: GenericCompletionConfig;
|
||||
onClickVariable: (option: EnvironmentVariable, tagValue: string, startPos: number) => void;
|
||||
onClickVariable: (option: WrappedEnvironmentVariable, tagValue: string, startPos: number) => void;
|
||||
onClickMissingVariable: (name: string, tagValue: string, startPos: number) => void;
|
||||
onClickPathParameter: (name: string) => void;
|
||||
extraExtensions: Extension[];
|
||||
@@ -33,9 +33,11 @@ export function twig({
|
||||
|
||||
const variableOptions: TwigCompletionOption[] =
|
||||
environmentVariables.map((v) => ({
|
||||
...v,
|
||||
name: v.variable.name,
|
||||
value: v.variable.value,
|
||||
type: 'variable',
|
||||
label: v.name,
|
||||
label: v.variable.name,
|
||||
description: `Inherited from ${v.source}`,
|
||||
onClick: (rawTag: string, startPos: number) => onClickVariable(v, rawTag, startPos),
|
||||
})) ?? [];
|
||||
|
||||
|
||||
@@ -94,13 +94,16 @@ function templateTags(
|
||||
(o) => o.name === name || (o.type === 'function' && o.aliases?.includes(name)),
|
||||
);
|
||||
if (option == null) {
|
||||
const from = node.from; // Cache here so the reference doesn't change
|
||||
option = {
|
||||
invalid: true,
|
||||
type: 'variable',
|
||||
name: inner,
|
||||
value: null,
|
||||
label: inner,
|
||||
onClick: () => onClickMissingVariable(name, rawTag, node.from),
|
||||
onClick: () => {
|
||||
onClickMissingVariable(name, rawTag, from);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -440,7 +440,7 @@ function EncryptionInput({
|
||||
const dropdownItems = useMemo<DropdownItem[]>(
|
||||
() => [
|
||||
{
|
||||
label: state.obscured ? 'Reveal' : 'Conceal',
|
||||
label: state.obscured ? 'Show' : 'Hide',
|
||||
disabled: isEncryptionEnabled && state.fieldType === 'text',
|
||||
leftSlot: <Icon icon={state.obscured ? 'eye' : 'eye_closed'} />,
|
||||
onSelect: () => setState((s) => ({ ...s, obscured: !s.obscured })),
|
||||
|
||||
@@ -10,7 +10,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export function CsvViewer({ response, className }: Props) {
|
||||
const body = useResponseBodyText(response);
|
||||
const body = useResponseBodyText({ response, filter: null });
|
||||
|
||||
const parsed = useMemo(() => {
|
||||
if (body.data == null) return null;
|
||||
|
||||
@@ -13,7 +13,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export function HTMLOrTextViewer({ response, pretty, textViewerClassName }: Props) {
|
||||
const rawTextBody = useResponseBodyText(response);
|
||||
const rawTextBody = useResponseBodyText({ response, filter: null });
|
||||
const contentType = getContentTypeFromHeaders(response.headers);
|
||||
const language = languageFromContentType(contentType, rawTextBody.data ?? '');
|
||||
|
||||
@@ -24,7 +24,7 @@ export function HTMLOrTextViewer({ response, pretty, textViewerClassName }: Prop
|
||||
if (language === 'html' && pretty) {
|
||||
return <WebPageViewer response={response} />;
|
||||
} else if (rawTextBody.data == null) {
|
||||
return <EmptyStateText>Empty response</EmptyStateText>
|
||||
return <EmptyStateText>Empty response</EmptyStateText>;
|
||||
} else {
|
||||
return (
|
||||
<TextViewer
|
||||
@@ -32,7 +32,7 @@ export function HTMLOrTextViewer({ response, pretty, textViewerClassName }: Prop
|
||||
text={rawTextBody.data}
|
||||
pretty={pretty}
|
||||
className={textViewerClassName}
|
||||
responseId={response.id}
|
||||
response={response}
|
||||
requestId={response.requestId}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -9,7 +9,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export function JsonViewer({ response, className }: Props) {
|
||||
const rawBody = useResponseBodyText(response);
|
||||
const rawBody = useResponseBodyText({ response, filter: null });
|
||||
|
||||
if (rawBody.isLoading || rawBody.data == null) return null;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { HttpResponse } from '@yaakapp-internal/models';
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useResponseBodyText } from '../../hooks/useResponseBodyText';
|
||||
|
||||
interface Props {
|
||||
@@ -7,8 +7,24 @@ interface Props {
|
||||
}
|
||||
|
||||
export function SvgViewer({ response }: Props) {
|
||||
const rawTextBody = useResponseBodyText(response);
|
||||
if (rawTextBody.data == null) return null;
|
||||
const src = `data:image/svg+xml;base64,${btoa(rawTextBody.data)}`;
|
||||
const rawTextBody = useResponseBodyText({ response, filter: null });
|
||||
const [src, setSrc] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!rawTextBody.data) {
|
||||
return setSrc(null);
|
||||
}
|
||||
|
||||
const blob = new Blob([rawTextBody.data], { type: 'image/svg+xml;charset=utf-8' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
setSrc(url);
|
||||
|
||||
return () => URL.revokeObjectURL(url);
|
||||
}, [rawTextBody.data]);
|
||||
|
||||
if (src == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <img src={src} alt="Response preview" className="max-w-full max-h-full pb-2" />;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import type { HttpResponse } from '@yaakapp-internal/models';
|
||||
import classNames from 'classnames';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { createGlobalState } from 'react-use';
|
||||
import { useDebouncedValue } from '../../hooks/useDebouncedValue';
|
||||
import { useFilterResponse } from '../../hooks/useFilterResponse';
|
||||
import { useFormatText } from '../../hooks/useFormatText';
|
||||
import { useResponseBodyText } from '../../hooks/useResponseBodyText';
|
||||
import type { EditorProps } from '../core/Editor/Editor';
|
||||
import { Editor } from '../core/Editor/Editor';
|
||||
import { hyperlink } from '../core/Editor/hyperlink/extension';
|
||||
@@ -18,13 +19,13 @@ interface Props {
|
||||
className?: string;
|
||||
text: string;
|
||||
language: EditorProps['language'];
|
||||
responseId: string;
|
||||
response: HttpResponse;
|
||||
requestId: string;
|
||||
}
|
||||
|
||||
const useFilterText = createGlobalState<Record<string, string | null>>({});
|
||||
|
||||
export function TextViewer({ language, text, responseId, requestId, pretty, className }: Props) {
|
||||
export function TextViewer({ language, text, response, requestId, pretty, className }: Props) {
|
||||
const [filterTextMap, setFilterTextMap] = useFilterText();
|
||||
const filterText = filterTextMap[requestId] ?? null;
|
||||
const debouncedFilterText = useDebouncedValue(filterText);
|
||||
@@ -36,7 +37,7 @@ export function TextViewer({ language, text, responseId, requestId, pretty, clas
|
||||
);
|
||||
|
||||
const isSearching = filterText != null;
|
||||
const filteredResponse = useFilterResponse({ filter: debouncedFilterText ?? '', responseId });
|
||||
const filteredResponse = useResponseBodyText({ response, filter: debouncedFilterText ?? null });
|
||||
|
||||
const toggleSearch = useCallback(() => {
|
||||
if (isSearching) {
|
||||
@@ -58,7 +59,7 @@ export function TextViewer({ language, text, responseId, requestId, pretty, clas
|
||||
<div key="input" className="w-full !opacity-100">
|
||||
<Input
|
||||
key={requestId}
|
||||
validate={!(filteredResponse.error || filteredResponse.data?.error)}
|
||||
validate={!filteredResponse.error}
|
||||
hideLabel
|
||||
autoFocus
|
||||
containerClassName="bg-surface"
|
||||
@@ -69,7 +70,7 @@ export function TextViewer({ language, text, responseId, requestId, pretty, clas
|
||||
defaultValue={filterText}
|
||||
onKeyDown={(e) => e.key === 'Escape' && toggleSearch()}
|
||||
onChange={setFilterText}
|
||||
stateKey={`filter.${responseId}`}
|
||||
stateKey={`filter.${response.id}`}
|
||||
/>
|
||||
</div>,
|
||||
);
|
||||
@@ -91,13 +92,12 @@ export function TextViewer({ language, text, responseId, requestId, pretty, clas
|
||||
}, [
|
||||
canFilter,
|
||||
filterText,
|
||||
filteredResponse.data?.error,
|
||||
filteredResponse.error,
|
||||
filteredResponse.isPending,
|
||||
isSearching,
|
||||
language,
|
||||
requestId,
|
||||
responseId,
|
||||
response,
|
||||
setFilterText,
|
||||
toggleSearch,
|
||||
]);
|
||||
@@ -112,7 +112,7 @@ export function TextViewer({ language, text, responseId, requestId, pretty, clas
|
||||
if (filteredResponse.error) {
|
||||
body = '';
|
||||
} else {
|
||||
body = filteredResponse.data?.content != null ? filteredResponse.data.content : '';
|
||||
body = filteredResponse.data != null ? filteredResponse.data : '';
|
||||
}
|
||||
} else {
|
||||
body = formattedBody;
|
||||
|
||||
@@ -8,7 +8,7 @@ interface Props {
|
||||
|
||||
export function WebPageViewer({ response }: Props) {
|
||||
const { url } = response;
|
||||
const body = useResponseBodyText(response).data ?? '';
|
||||
const body = useResponseBodyText({ response, filter: null }).data ?? '';
|
||||
|
||||
const contentForIframe: string | undefined = useMemo(() => {
|
||||
if (body.includes('<head>')) {
|
||||
@@ -21,9 +21,10 @@ export function WebPageViewer({ response }: Props) {
|
||||
<div className="h-full pb-3">
|
||||
<iframe
|
||||
key={body ? 'has-body' : 'no-body'}
|
||||
title="Response preview"
|
||||
title="Yaak response preview"
|
||||
srcDoc={contentForIframe}
|
||||
sandbox="allow-scripts allow-same-origin"
|
||||
sandbox="allow-scripts allow-forms"
|
||||
referrerPolicy="no-referrer"
|
||||
className="h-full w-full rounded border border-border-subtle"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -37,11 +37,6 @@ export function SidebarItemContextMenu({ child, show, close }: Props) {
|
||||
const items = useMemo((): DropdownItem[] => {
|
||||
if (child.model === 'folder') {
|
||||
return [
|
||||
{
|
||||
label: 'Send All',
|
||||
leftSlot: <Icon icon="send_horizontal" />,
|
||||
onSelect: () => sendManyRequests.mutate(child.children.map((c) => c.id)),
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
leftSlot: <Icon icon="settings" />,
|
||||
@@ -52,6 +47,11 @@ export function SidebarItemContextMenu({ child, show, close }: Props) {
|
||||
leftSlot: <Icon icon="copy" />,
|
||||
onSelect: () => duplicateModelById(child.model, child.id),
|
||||
},
|
||||
{
|
||||
label: 'Send All',
|
||||
leftSlot: <Icon icon="send_horizontal" />,
|
||||
onSelect: () => sendManyRequests.mutate(child.children.map((c) => c.id)),
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
color: 'danger',
|
||||
|
||||
@@ -4,5 +4,5 @@ import { useEnvironmentVariables } from './useEnvironmentVariables';
|
||||
|
||||
export function useActiveEnvironmentVariables() {
|
||||
const activeEnvironment = useAtomValue(activeEnvironmentAtom);
|
||||
return useEnvironmentVariables(activeEnvironment?.id ?? null);
|
||||
return useEnvironmentVariables(activeEnvironment?.id ?? null).map((v) => v.variable);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ export function useCopyHttpResponse(response: HttpResponse) {
|
||||
return useFastMutation({
|
||||
mutationKey: ['copy_http_response', response.id],
|
||||
async mutationFn() {
|
||||
const body = await getResponseBodyText(response);
|
||||
const body = await getResponseBodyText({ responseId: response.id, filter: null });
|
||||
copyToClipboard(body);
|
||||
},
|
||||
});
|
||||
|
||||
10
src-web/hooks/useEnvironmentValueVisibility.ts
Normal file
10
src-web/hooks/useEnvironmentValueVisibility.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { Environment } from '@yaakapp-internal/models';
|
||||
import { useKeyValue } from './useKeyValue';
|
||||
|
||||
export function useEnvironmentValueVisibility(environment: Environment) {
|
||||
return useKeyValue<boolean>({
|
||||
namespace: 'global',
|
||||
key: ['environmentValueVisibility', environment.workspaceId],
|
||||
fallback: false,
|
||||
});
|
||||
}
|
||||
@@ -1,25 +1,52 @@
|
||||
import type { EnvironmentVariable } from '@yaakapp-internal/models';
|
||||
import { environmentsAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import type { Environment, EnvironmentVariable } from '@yaakapp-internal/models';
|
||||
import { foldersAtom } from '@yaakapp-internal/models';
|
||||
import { useMemo } from 'react';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { useActiveRequest } from './useActiveRequest';
|
||||
import { useEnvironmentsBreakdown } from './useEnvironmentsBreakdown';
|
||||
import { useParentFolders } from './useParentFolders';
|
||||
|
||||
export function useEnvironmentVariables(environmentId: string | null) {
|
||||
const { baseEnvironment } = useEnvironmentsBreakdown();
|
||||
const activeEnvironment =
|
||||
useAtomValue(environmentsAtom).find((e) => e.id === environmentId) ?? null;
|
||||
const { baseEnvironment, folderEnvironments, subEnvironments } = useEnvironmentsBreakdown();
|
||||
const activeEnvironment = subEnvironments.find((e) => e.id === environmentId) ?? null;
|
||||
const activeRequest = useActiveRequest();
|
||||
const parentFolders = useParentFolders(activeRequest);
|
||||
|
||||
return useMemo(() => {
|
||||
const varMap: Record<string, EnvironmentVariable> = {};
|
||||
const varMap: Record<string, WrappedEnvironmentVariable> = {};
|
||||
const folderVariables = parentFolders.flatMap((f) =>
|
||||
wrapVariables(folderEnvironments.find((fe) => fe.parentId === f.id) ?? null),
|
||||
);
|
||||
|
||||
const allVariables = [
|
||||
...(baseEnvironment?.variables ?? []),
|
||||
...(activeEnvironment?.variables ?? []),
|
||||
...folderVariables,
|
||||
...wrapVariables(activeEnvironment),
|
||||
...wrapVariables(baseEnvironment),
|
||||
];
|
||||
|
||||
for (const v of allVariables) {
|
||||
if (!v.enabled || !v.name) continue;
|
||||
varMap[v.name] = v;
|
||||
if (!v.variable.enabled || !v.variable.name || v.variable.name in varMap) {
|
||||
continue;
|
||||
}
|
||||
varMap[v.variable.name] = v;
|
||||
}
|
||||
|
||||
return Object.values(varMap);
|
||||
}, [activeEnvironment, baseEnvironment]);
|
||||
}, [activeEnvironment, baseEnvironment, folderEnvironments, parentFolders]);
|
||||
}
|
||||
|
||||
export interface WrappedEnvironmentVariable {
|
||||
variable: EnvironmentVariable;
|
||||
environment: Environment;
|
||||
source: string;
|
||||
}
|
||||
|
||||
function wrapVariables(e: Environment | null): WrappedEnvironmentVariable[] {
|
||||
if (e == null) return [];
|
||||
const folders = jotaiStore.get(foldersAtom);
|
||||
return e.variables.map((v) => {
|
||||
const folder = e.parentModel === 'folder' ? folders.find((f) => f.id === e.parentId) : null;
|
||||
const source = folder?.name ?? e.name;
|
||||
return { variable: v, environment: e, source };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,12 +5,15 @@ import { useMemo } from 'react';
|
||||
export function useEnvironmentsBreakdown() {
|
||||
const allEnvironments = useAtomValue(environmentsAtom);
|
||||
return useMemo(() => {
|
||||
const baseEnvironments = allEnvironments.filter((e) => e.base) ?? [];
|
||||
const subEnvironments = allEnvironments.filter((e) => !e.base) ?? [];
|
||||
const baseEnvironments = allEnvironments.filter((e) => e.parentId == null) ?? [];
|
||||
const subEnvironments =
|
||||
allEnvironments.filter((e) => e.parentModel === 'environment' && e.parentId != null) ?? [];
|
||||
const folderEnvironments =
|
||||
allEnvironments.filter((e) => e.parentModel === 'folder' && e.parentId != null) ?? [];
|
||||
|
||||
const baseEnvironment = baseEnvironments[0] ?? null;
|
||||
const otherBaseEnvironments =
|
||||
baseEnvironments.filter((e) => e.id !== baseEnvironment?.id) ?? [];
|
||||
return { allEnvironments, baseEnvironment, subEnvironments, otherBaseEnvironments };
|
||||
return { allEnvironments, baseEnvironment, subEnvironments, folderEnvironments, otherBaseEnvironments };
|
||||
}, [allEnvironments]);
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { FilterResponse } from '@yaakapp-internal/plugins';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
|
||||
export function useFilterResponse({
|
||||
responseId,
|
||||
filter,
|
||||
}: {
|
||||
responseId: string | null;
|
||||
filter: string;
|
||||
}) {
|
||||
return useQuery({
|
||||
queryKey: ['filter_response', responseId, filter],
|
||||
queryFn: async () => {
|
||||
if (filter === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const result = (await invokeCmd('cmd_filter_response', {
|
||||
responseId,
|
||||
filter,
|
||||
})) as FilterResponse;
|
||||
|
||||
if (result.error) {
|
||||
console.log("Failed to filter response:", result.error);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -40,7 +40,7 @@ const hotkeys: Record<HotkeyAction, string[]> = {
|
||||
'request_switcher.prev': ['Control+Tab'],
|
||||
'request_switcher.toggle': ['CmdCtrl+p'],
|
||||
'settings.show': ['CmdCtrl+,'],
|
||||
'sidebar.delete_selected_item': ['Backspace'],
|
||||
'sidebar.delete_selected_item': ['Delete'],
|
||||
'sidebar.focus': ['CmdCtrl+b'],
|
||||
'url_bar.focus': ['CmdCtrl+l'],
|
||||
'workspace_settings.show': ['CmdCtrl+;'],
|
||||
@@ -98,7 +98,7 @@ export function useHotKey(
|
||||
|
||||
// Don't add key if not holding modifier
|
||||
const isValidKeymapKey =
|
||||
e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.key === 'Backspace';
|
||||
e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.key === 'Backspace' || e.key === 'Delete';
|
||||
if (!isValidKeymapKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
export function useHttpAuthenticationConfig(
|
||||
authName: string | null,
|
||||
values: Record<string, JsonPrimitive>,
|
||||
requestId: string,
|
||||
request: HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace,
|
||||
) {
|
||||
const workspaceId = useAtomValue(activeWorkspaceIdAtom);
|
||||
const environmentId = useAtomValue(activeEnvironmentIdAtom);
|
||||
@@ -37,7 +37,7 @@ export function useHttpAuthenticationConfig(
|
||||
return useQuery({
|
||||
queryKey: [
|
||||
'http_authentication_config',
|
||||
requestId,
|
||||
request,
|
||||
authName,
|
||||
values,
|
||||
responseKey,
|
||||
@@ -53,8 +53,7 @@ export function useHttpAuthenticationConfig(
|
||||
{
|
||||
authName,
|
||||
values,
|
||||
requestId,
|
||||
workspaceId,
|
||||
request,
|
||||
environmentId,
|
||||
},
|
||||
);
|
||||
@@ -63,17 +62,16 @@ export function useHttpAuthenticationConfig(
|
||||
...config,
|
||||
actions: config.actions?.map((a, i) => ({
|
||||
...a,
|
||||
call: async ({
|
||||
id: modelId,
|
||||
}: HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace) => {
|
||||
call: async (
|
||||
model: HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace,
|
||||
) => {
|
||||
await invokeCmd('cmd_call_http_authentication_action', {
|
||||
pluginRefId: config.pluginRefId,
|
||||
actionIndex: i,
|
||||
authName,
|
||||
values,
|
||||
modelId,
|
||||
model,
|
||||
environmentId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
// Ensure the config is refreshed after the action is done
|
||||
|
||||
@@ -66,7 +66,7 @@ export function useIntrospectGraphQL(
|
||||
return setError(response.error);
|
||||
}
|
||||
|
||||
const bodyText = await getResponseBodyText(response);
|
||||
const bodyText = await getResponseBodyText({ responseId: response.id, filter: null });
|
||||
if (response.status < 200 || response.status >= 300) {
|
||||
return setError(
|
||||
`Request failed with status ${response.status}.\nThe response text is:\n\n${bodyText}`,
|
||||
@@ -108,12 +108,11 @@ export function useIntrospectGraphQL(
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const schema = buildClientSchema(JSON.parse(introspection.data.content).data);
|
||||
setSchema(schema);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (e: any) {
|
||||
setError('message' in e ? e.message : String(e));
|
||||
const parseResult = tryParseIntrospectionToSchema(introspection.data.content);
|
||||
if ('error' in parseResult) {
|
||||
setError(parseResult.error);
|
||||
} else {
|
||||
setSchema(parseResult.schema);
|
||||
}
|
||||
}, [introspection.data?.content]);
|
||||
|
||||
@@ -135,7 +134,26 @@ export function useCurrentGraphQLSchema(request: HttpRequest) {
|
||||
return useMemo(() => {
|
||||
if (result.data == null) return null;
|
||||
if (result.data.content == null || result.data.content === '') return null;
|
||||
const schema = buildClientSchema(JSON.parse(result.data.content).data);
|
||||
return schema;
|
||||
const r = tryParseIntrospectionToSchema(result.data.content);
|
||||
return 'error' in r ? null : r.schema;
|
||||
}, [result.data]);
|
||||
}
|
||||
|
||||
function tryParseIntrospectionToSchema(
|
||||
content: string,
|
||||
): { schema: GraphQLSchema } | { error: string } {
|
||||
let parsedResponse;
|
||||
try {
|
||||
parsedResponse = JSON.parse(content).data;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (e: any) {
|
||||
return { error: String('message' in e ? e.message : e) };
|
||||
}
|
||||
|
||||
try {
|
||||
return { schema: buildClientSchema(parsedResponse, {}) };
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (e: any) {
|
||||
return { error: String('message' in e ? e.message : e) };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {useAtomValue} from "jotai/index";
|
||||
import {activeWorkspaceMetaAtom} from "./useActiveWorkspace";
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { activeWorkspaceMetaAtom } from './useActiveWorkspace';
|
||||
|
||||
export function useIsEncryptionEnabled() {
|
||||
const workspaceMeta = useAtomValue(activeWorkspaceMetaAtom);
|
||||
return workspaceMeta?.encryptionKey != null;
|
||||
const workspaceMeta = useAtomValue(activeWorkspaceMetaAtom);
|
||||
return workspaceMeta?.encryptionKey != null;
|
||||
}
|
||||
|
||||
24
src-web/hooks/useParentFolders.ts
Normal file
24
src-web/hooks/useParentFolders.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { Folder, GrpcRequest, HttpRequest, WebsocketRequest } from '@yaakapp-internal/models';
|
||||
import { foldersAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export function useParentFolders(m: Folder | HttpRequest | GrpcRequest | WebsocketRequest | null) {
|
||||
const folders = useAtomValue(foldersAtom);
|
||||
|
||||
return useMemo(() => getParentFolders(folders, m), [folders, m]);
|
||||
}
|
||||
|
||||
function getParentFolders(
|
||||
folders: Folder[],
|
||||
currentModel: Folder | HttpRequest | GrpcRequest | WebsocketRequest | null,
|
||||
): Folder[] {
|
||||
if (currentModel == null) return [];
|
||||
|
||||
const folder = currentModel.folderId ? folders.find((f) => f.id === currentModel.folderId) : null;
|
||||
if (folder == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [folder, ...getParentFolders(folders, folder)];
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { HttpResponse } from '@yaakapp-internal/models';
|
||||
import { getResponseBodyBlob } from '../lib/responseBody';
|
||||
|
||||
export function useResponseBodyBlob(response: HttpResponse) {
|
||||
return useQuery<Uint8Array | null>({
|
||||
enabled: response != null,
|
||||
queryKey: ['response-body-binary', response?.updatedAt],
|
||||
initialData: null,
|
||||
queryFn: () => getResponseBodyBlob(response),
|
||||
}).data;
|
||||
}
|
||||
@@ -2,10 +2,21 @@ import { useQuery } from '@tanstack/react-query';
|
||||
import type { HttpResponse } from '@yaakapp-internal/models';
|
||||
import { getResponseBodyText } from '../lib/responseBody';
|
||||
|
||||
export function useResponseBodyText(response: HttpResponse) {
|
||||
export function useResponseBodyText({
|
||||
response,
|
||||
filter,
|
||||
}: {
|
||||
response: HttpResponse;
|
||||
filter: string | null;
|
||||
}) {
|
||||
return useQuery({
|
||||
placeholderData: (prev) => prev, // Keep previous data on refetch
|
||||
queryKey: ['response-body-text', response.id, response.updatedAt, response.contentLength],
|
||||
queryFn: () => getResponseBodyText(response),
|
||||
queryKey: [
|
||||
'response_body_text',
|
||||
response.id,
|
||||
response.updatedAt,
|
||||
response.contentLength,
|
||||
filter ?? '',
|
||||
],
|
||||
queryFn: () => getResponseBodyText({ responseId: response.id, filter }),
|
||||
});
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user