mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-02-17 16:17:45 +01:00
Compare commits
18 Commits
v2025.8.0
...
v2025.9.0-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75dc82570b | ||
|
|
d7a7a64ec4 | ||
|
|
3aae1b52d1 | ||
|
|
9eddf716e1 | ||
|
|
554e632c19 | ||
|
|
054916b7af | ||
|
|
f2a63087b0 | ||
|
|
6f0d4ad5e4 | ||
|
|
cd3530f598 | ||
|
|
53aea914ac | ||
|
|
dc0c1decee | ||
|
|
32d56f2274 | ||
|
|
ef86c1d189 | ||
|
|
e264c50427 | ||
|
|
f05ad62301 | ||
|
|
0a6228bf16 | ||
|
|
fa3a0b57f9 | ||
|
|
4390c02117 |
193
package-lock.json
generated
193
package-lock.json
generated
@@ -19,6 +19,7 @@
|
||||
"plugins/auth-bearer",
|
||||
"plugins/auth-jwt",
|
||||
"plugins/auth-oauth2",
|
||||
"plugins/auth-oauth1",
|
||||
"plugins/filter-jsonpath",
|
||||
"plugins/filter-xpath",
|
||||
"plugins/importer-curl",
|
||||
@@ -111,7 +112,6 @@
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
|
||||
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.27.1",
|
||||
@@ -400,7 +400,6 @@
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
|
||||
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -1926,7 +1925,6 @@
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nodelib/fs.stat": "2.0.5",
|
||||
@@ -1940,7 +1938,6 @@
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
|
||||
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
@@ -1950,7 +1947,6 @@
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
|
||||
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nodelib/fs.scandir": "2.1.5",
|
||||
@@ -1971,6 +1967,142 @@
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@prantlf/jsonlint": {
|
||||
"version": "16.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@prantlf/jsonlint/-/jsonlint-16.0.0.tgz",
|
||||
"integrity": "sha512-L0jFtcsBRJZOr4T6sbePb1R6XYF6Nofj6kmEAxqTKCHEr50uvyxBFnB1UKaehWaMhHnvtyqWfTR5Go25RywXIQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "8.17.1",
|
||||
"ajv-draft-04": "1.0.0",
|
||||
"cosmiconfig": "9.0.0",
|
||||
"diff": "5.2.0",
|
||||
"fast-glob": "3.3.2"
|
||||
},
|
||||
"bin": {
|
||||
"jsonlint": "lib/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@prantlf/jsonlint/node_modules/ajv": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/@prantlf/jsonlint/node_modules/ajv-draft-04": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz",
|
||||
"integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"ajv": "^8.5.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"ajv": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@prantlf/jsonlint/node_modules/cosmiconfig": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
|
||||
"integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"env-paths": "^2.2.1",
|
||||
"import-fresh": "^3.3.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"parse-json": "^5.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/d-fischer"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.9.5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@prantlf/jsonlint/node_modules/diff": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
|
||||
"integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@prantlf/jsonlint/node_modules/fast-glob": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
|
||||
"integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nodelib/fs.stat": "^2.0.2",
|
||||
"@nodelib/fs.walk": "^1.2.3",
|
||||
"glob-parent": "^5.1.2",
|
||||
"merge2": "^1.3.0",
|
||||
"micromatch": "^4.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@prantlf/jsonlint/node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/@prantlf/jsonlint/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@prantlf/jsonlint/node_modules/parse-json": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"error-ex": "^1.3.1",
|
||||
"json-parse-even-better-errors": "^2.3.0",
|
||||
"lines-and-columns": "^1.1.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@replit/codemirror-emacs": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@replit/codemirror-emacs/-/codemirror-emacs-6.1.0.tgz",
|
||||
@@ -4163,6 +4295,10 @@
|
||||
"resolved": "plugins/auth-jwt",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@yaak/auth-oauth1": {
|
||||
"resolved": "plugins/auth-oauth1",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@yaak/auth-oauth2": {
|
||||
"resolved": "plugins/auth-oauth2",
|
||||
"link": true
|
||||
@@ -5106,7 +5242,6 @@
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.1.1"
|
||||
@@ -5301,7 +5436,6 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@@ -6846,11 +6980,19 @@
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/env-paths": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
|
||||
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/error-ex": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-arrayish": "^0.2.1"
|
||||
@@ -7906,7 +8048,6 @@
|
||||
"version": "1.19.1",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
|
||||
"integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"reusify": "^1.0.4"
|
||||
@@ -7962,7 +8103,6 @@
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
@@ -9370,7 +9510,6 @@
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
||||
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"parent-module": "^1.0.0",
|
||||
@@ -9567,7 +9706,6 @@
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-async-function": {
|
||||
@@ -9757,7 +9895,6 @@
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -9811,7 +9948,6 @@
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-extglob": "^2.1.1"
|
||||
@@ -9883,7 +10019,6 @@
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
@@ -10270,7 +10405,6 @@
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-pointer": {
|
||||
@@ -10788,7 +10922,6 @@
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/liquid-json": {
|
||||
@@ -11449,7 +11582,6 @@
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
@@ -12038,7 +12170,6 @@
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"braces": "^3.0.3",
|
||||
@@ -13082,6 +13213,12 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/oauth-1.0a": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/oauth-1.0a/-/oauth-1.0a-2.2.6.tgz",
|
||||
"integrity": "sha512-6bkxv3N4Gu5lty4viIcIAnq5GbxECviMBeKR3WX/q87SPQ8E8aursPZUtsXDnxCs787af09WPRBLqYrf/lwoYQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
@@ -13637,7 +13774,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"callsites": "^3.0.0"
|
||||
@@ -13842,14 +13978,12 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
@@ -14325,7 +14459,6 @@
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -15135,7 +15268,6 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
@@ -15172,7 +15304,6 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
|
||||
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"iojs": ">=1.0.0",
|
||||
@@ -15271,7 +15402,6 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -17174,7 +17304,6 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
@@ -17408,7 +17537,7 @@
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
@@ -18823,6 +18952,13 @@
|
||||
"@types/jsonwebtoken": "^9.0.7"
|
||||
}
|
||||
},
|
||||
"plugins/auth-oauth1": {
|
||||
"name": "@yaak/auth-oauth1",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"oauth-1.0a": "^2.2.6"
|
||||
}
|
||||
},
|
||||
"plugins/auth-oauth2": {
|
||||
"name": "@yaak/auth-oauth2",
|
||||
"version": "0.1.0"
|
||||
@@ -19052,6 +19188,7 @@
|
||||
"@gilbarbara/deep-equal": "^0.3.1",
|
||||
"@lezer/highlight": "^1.1.3",
|
||||
"@lezer/lr": "^1.3.3",
|
||||
"@prantlf/jsonlint": "^16.0.0",
|
||||
"@replit/codemirror-emacs": "^6.1.0",
|
||||
"@replit/codemirror-vim": "^6.3.0",
|
||||
"@replit/codemirror-vscode-keymap": "^6.0.2",
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
"plugins/auth-bearer",
|
||||
"plugins/auth-jwt",
|
||||
"plugins/auth-oauth2",
|
||||
"plugins/auth-oauth1",
|
||||
"plugins/filter-jsonpath",
|
||||
"plugins/filter-xpath",
|
||||
"plugins/importer-curl",
|
||||
|
||||
@@ -46,7 +46,7 @@ export async function convertToCurl(request: Partial<HttpRequest>) {
|
||||
// Add API key authentication
|
||||
if (request.authenticationType === 'apikey') {
|
||||
if (request.authentication?.location === 'query') {
|
||||
const sep = request.url?.includes('?') ? '&' : '?';
|
||||
const sep = finalUrl.includes('?') ? '&' : '?';
|
||||
finalUrl = [
|
||||
finalUrl,
|
||||
sep,
|
||||
|
||||
@@ -275,11 +275,9 @@ describe('exporter-curl', () => {
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
[
|
||||
`curl 'https://yaak.app'`,
|
||||
`--aws-sigv4 aws:amz:us-east-1:s3`,
|
||||
`--user 'ak:sk'`,
|
||||
].join(` \\\n `),
|
||||
[`curl 'https://yaak.app'`, `--aws-sigv4 aws:amz:us-east-1:s3`, `--user 'ak:sk'`].join(
|
||||
` \\\n `,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -314,15 +312,40 @@ describe('exporter-curl', () => {
|
||||
authentication: {
|
||||
location: 'header',
|
||||
key: 'X-Header',
|
||||
value: 'my-token'
|
||||
value: 'my-token',
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
[
|
||||
`curl 'https://yaak.app'`,
|
||||
`--header 'X-Header: my-token'`,
|
||||
].join(` \\\n `),
|
||||
);
|
||||
).toEqual([`curl 'https://yaak.app'`, `--header 'X-Header: my-token'`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('API key auth header query', async () => {
|
||||
expect(
|
||||
await convertToCurl({
|
||||
url: 'https://yaak.app?hi=there',
|
||||
urlParameters: [{ name: 'param', value: 'hi' }],
|
||||
authenticationType: 'apikey',
|
||||
authentication: {
|
||||
location: 'query',
|
||||
key: 'foo',
|
||||
value: 'bar',
|
||||
},
|
||||
}),
|
||||
).toEqual([`curl 'https://yaak.app?hi=there¶m=hi&foo=bar'`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('API key auth header query with params', async () => {
|
||||
expect(
|
||||
await convertToCurl({
|
||||
url: 'https://yaak.app',
|
||||
urlParameters: [{ name: 'param', value: 'hi' }],
|
||||
authenticationType: 'apikey',
|
||||
authentication: {
|
||||
location: 'query',
|
||||
key: 'foo',
|
||||
value: 'bar',
|
||||
},
|
||||
}),
|
||||
).toEqual([`curl 'https://yaak.app?param=hi&foo=bar'`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('API key auth header default', async () => {
|
||||
@@ -334,12 +357,7 @@ describe('exporter-curl', () => {
|
||||
location: 'header',
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
[
|
||||
`curl 'https://yaak.app'`,
|
||||
`--header 'X-Api-Key: '`,
|
||||
].join(` \\\n `),
|
||||
);
|
||||
).toEqual([`curl 'https://yaak.app'`, `--header 'X-Api-Key: '`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('API key auth query', async () => {
|
||||
@@ -350,14 +368,10 @@ describe('exporter-curl', () => {
|
||||
authentication: {
|
||||
location: 'query',
|
||||
key: 'foo',
|
||||
value: 'bar-baz'
|
||||
value: 'bar-baz',
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
[
|
||||
`curl 'https://yaak.app?foo=bar-baz'`,
|
||||
].join(` \\\n `),
|
||||
);
|
||||
).toEqual([`curl 'https://yaak.app?foo=bar-baz'`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('API key auth query with existing', async () => {
|
||||
@@ -368,14 +382,10 @@ describe('exporter-curl', () => {
|
||||
authentication: {
|
||||
location: 'query',
|
||||
key: 'hi',
|
||||
value: 'there'
|
||||
value: 'there',
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
[
|
||||
`curl 'https://yaak.app?foo=bar&baz=qux&hi=there'`,
|
||||
].join(` \\\n `),
|
||||
);
|
||||
).toEqual([`curl 'https://yaak.app?foo=bar&baz=qux&hi=there'`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('API key auth query default', async () => {
|
||||
@@ -387,11 +397,7 @@ describe('exporter-curl', () => {
|
||||
location: 'query',
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
[
|
||||
`curl 'https://yaak.app?foo=bar&baz=qux&token='`,
|
||||
].join(` \\\n `),
|
||||
);
|
||||
).toEqual([`curl 'https://yaak.app?foo=bar&baz=qux&token='`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('Stale body data', async () => {
|
||||
|
||||
20
plugins/auth-oauth1/package.json
Normal file
20
plugins/auth-oauth1/package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "@yaak/auth-oauth1",
|
||||
"displayName": "OAuth 1.0",
|
||||
"description": "Authenticate requests using OAuth 1.0a",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mountain-loop/yaak.git",
|
||||
"directory": "plugins/auth-oauth1"
|
||||
},
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "tsc --noEmit && eslint . --ext .ts,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"oauth-1.0a": "^2.2.6"
|
||||
}
|
||||
}
|
||||
197
plugins/auth-oauth1/src/index.ts
Normal file
197
plugins/auth-oauth1/src/index.ts
Normal file
@@ -0,0 +1,197 @@
|
||||
import type { Context, GetHttpAuthenticationConfigRequest, PluginDefinition } from '@yaakapp/api';
|
||||
import crypto from 'node:crypto';
|
||||
import OAuth from 'oauth-1.0a';
|
||||
|
||||
const signatures = {
|
||||
HMAC_SHA1: 'HMAC-SHA1',
|
||||
HMAC_SHA256: 'HMAC-SHA256',
|
||||
HMAC_SHA512: 'HMAC-SHA512',
|
||||
RSA_SHA1: 'RSA-SHA1',
|
||||
RSA_SHA256: 'RSA-SHA256',
|
||||
RSA_SHA512: 'RSA-SHA512',
|
||||
PLAINTEXT: 'PLAINTEXT',
|
||||
} as const;
|
||||
const defaultSig = signatures.HMAC_SHA1;
|
||||
|
||||
const pkSigs = Object.values(signatures).filter((k) => k.startsWith('RSA-'));
|
||||
const nonPkSigs = Object.values(signatures).filter((k) => !pkSigs.includes(k));
|
||||
|
||||
type SigMethod = (typeof signatures)[keyof typeof signatures];
|
||||
|
||||
function hiddenIfNot(
|
||||
sigMethod: SigMethod[],
|
||||
...other: ((values: GetHttpAuthenticationConfigRequest['values']) => boolean)[]
|
||||
) {
|
||||
return (_ctx: Context, { values }: GetHttpAuthenticationConfigRequest) => {
|
||||
const hasGrantType = sigMethod.find((t) => t === String(values.signatureMethod ?? defaultSig));
|
||||
const hasOtherBools = other.every((t) => t(values));
|
||||
const show = hasGrantType && hasOtherBools;
|
||||
return { hidden: !show };
|
||||
};
|
||||
}
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
authentication: {
|
||||
name: 'oauth1',
|
||||
label: 'OAuth 1.0',
|
||||
shortLabel: 'OAuth 1',
|
||||
args: [
|
||||
{
|
||||
name: 'signatureMethod',
|
||||
label: 'Signature Method',
|
||||
type: 'select',
|
||||
defaultValue: defaultSig,
|
||||
options: Object.values(signatures).map((v) => ({ label: v, value: v })),
|
||||
},
|
||||
{ name: 'consumerKey', label: 'Consumer Key', type: 'text', password: true, optional: true },
|
||||
{
|
||||
name: 'consumerSecret',
|
||||
label: 'Consumer Secret',
|
||||
type: 'text',
|
||||
password: true,
|
||||
optional: true,
|
||||
},
|
||||
{
|
||||
name: 'tokenKey',
|
||||
label: 'Access Token',
|
||||
type: 'text',
|
||||
password: true,
|
||||
optional: true,
|
||||
},
|
||||
{
|
||||
name: 'tokenSecret',
|
||||
label: 'Token Secret',
|
||||
type: 'text',
|
||||
password: true,
|
||||
optional: true,
|
||||
dynamic: hiddenIfNot(nonPkSigs),
|
||||
},
|
||||
{
|
||||
name: 'privateKey',
|
||||
label: 'Private Key (RSA-SHA1)',
|
||||
type: 'text',
|
||||
multiLine: true,
|
||||
optional: true,
|
||||
password: true,
|
||||
placeholder:
|
||||
'-----BEGIN RSA PRIVATE KEY-----\nPrivate key in PEM format\n-----END RSA PRIVATE KEY-----',
|
||||
dynamic: hiddenIfNot(pkSigs),
|
||||
},
|
||||
{
|
||||
type: 'accordion',
|
||||
label: 'Advanced',
|
||||
inputs: [
|
||||
{ name: 'callback', label: 'Callback Url', type: 'text', optional: true },
|
||||
{ name: 'verifier', label: 'Verifier', type: 'text', optional: true, password: true },
|
||||
{ name: 'timestamp', label: 'Timestamp', type: 'text', optional: true },
|
||||
{ name: 'nonce', label: 'Nonce', type: 'text', optional: true },
|
||||
{
|
||||
name: 'version',
|
||||
label: 'OAuth Version',
|
||||
type: 'text',
|
||||
optional: true,
|
||||
defaultValue: '1.0',
|
||||
},
|
||||
{ name: 'realm', label: 'Realm', type: 'text', optional: true },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
onApply(
|
||||
_ctx,
|
||||
{ values, method, url },
|
||||
): {
|
||||
setHeaders?: { name: string; value: string }[];
|
||||
setQueryParameters?: { name: string; value: string }[];
|
||||
} {
|
||||
const consumerKey = String(values.consumerKey || '');
|
||||
const consumerSecret = String(values.consumerSecret || '');
|
||||
|
||||
const signatureMethod = String(values.signatureMethod || signatures.HMAC_SHA1) as SigMethod;
|
||||
const version = String(values.version || '1.0');
|
||||
const realm = String(values.realm || '') || undefined;
|
||||
|
||||
const oauth = new OAuth({
|
||||
consumer: { key: consumerKey, secret: consumerSecret },
|
||||
signature_method: signatureMethod,
|
||||
version,
|
||||
hash_function: hashFunction(signatureMethod),
|
||||
realm,
|
||||
});
|
||||
|
||||
if (pkSigs.includes(signatureMethod)) {
|
||||
oauth.getSigningKey = (tokenSecret?: string) => tokenSecret || '';
|
||||
}
|
||||
|
||||
const requestUrl = new URL(url);
|
||||
|
||||
// Base request options passed to oauth-1.0a
|
||||
const requestData: Omit<OAuth.RequestOptions, 'data'> & {
|
||||
data: Record<string, string | string[]>;
|
||||
} = {
|
||||
method,
|
||||
url: requestUrl.toString(),
|
||||
includeBodyHash: false,
|
||||
data: {},
|
||||
};
|
||||
|
||||
// (1) Include existing query params in signature base string
|
||||
for (const key of requestUrl.searchParams.keys()) {
|
||||
if (key.startsWith('oauth_')) continue;
|
||||
const all = requestUrl.searchParams.getAll(key);
|
||||
requestData.data[key] = all.length > 1 ? all : all[0]!;
|
||||
}
|
||||
|
||||
// (2) Manual oauth_* overrides
|
||||
if (values.callback) requestData.data.oauth_callback = String(values.callback);
|
||||
if (values.nonce) requestData.data.oauth_nonce = String(values.nonce);
|
||||
if (values.timestamp) requestData.data.oauth_timestamp = String(values.timestamp);
|
||||
if (values.verifier) requestData.data.oauth_verifier = String(values.verifier);
|
||||
|
||||
let token: OAuth.Token | { key: string } | undefined;
|
||||
|
||||
if (pkSigs.includes(signatureMethod)) {
|
||||
token = {
|
||||
key: String(values.tokenKey || ''),
|
||||
secret: String(values.privateKey || ''),
|
||||
};
|
||||
} else if (values.tokenKey && values.tokenSecret) {
|
||||
token = { key: String(values.tokenKey), secret: String(values.tokenSecret) };
|
||||
} else if (values.tokenKey) {
|
||||
token = { key: String(values.tokenKey) };
|
||||
}
|
||||
|
||||
const authParams = oauth.authorize(requestData, token as OAuth.Token | undefined);
|
||||
const { Authorization } = oauth.toHeader(authParams);
|
||||
return { setHeaders: [{ name: 'Authorization', value: Authorization }] };
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function hashFunction(signatureMethod: SigMethod) {
|
||||
switch (signatureMethod) {
|
||||
case signatures.HMAC_SHA1:
|
||||
return (base: string, key: string) =>
|
||||
crypto.createHmac('sha1', key).update(base).digest('base64');
|
||||
case signatures.HMAC_SHA256:
|
||||
return (base: string, key: string) =>
|
||||
crypto.createHmac('sha256', key).update(base).digest('base64');
|
||||
case signatures.HMAC_SHA512:
|
||||
return (base: string, key: string) =>
|
||||
crypto.createHmac('sha512', key).update(base).digest('base64');
|
||||
case signatures.RSA_SHA1:
|
||||
return (base: string, privateKey: string) =>
|
||||
crypto.createSign('RSA-SHA1').update(base).sign(privateKey, 'base64');
|
||||
case signatures.RSA_SHA256:
|
||||
return (base: string, privateKey: string) =>
|
||||
crypto.createSign('RSA-SHA256').update(base).sign(privateKey, 'base64');
|
||||
case signatures.RSA_SHA512:
|
||||
return (base: string, privateKey: string) =>
|
||||
crypto.createSign('RSA-SHA512').update(base).sign(privateKey, 'base64');
|
||||
case signatures.PLAINTEXT:
|
||||
return (base: string) => base;
|
||||
default:
|
||||
return (base: string, key: string) =>
|
||||
crypto.createHmac('sha1', key).update(base).digest('base64');
|
||||
}
|
||||
}
|
||||
3
plugins/auth-oauth1/tsconfig.json
Normal file
3
plugins/auth-oauth1/tsconfig.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json"
|
||||
}
|
||||
@@ -745,5 +745,47 @@ export const plugin: PluginDefinition = {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triangle',
|
||||
dark: true,
|
||||
label: 'Triangle',
|
||||
base: {
|
||||
surface: 'rgb(0,0,0)',
|
||||
surfaceHighlight: 'rgb(21,21,21)',
|
||||
surfaceActive: 'rgb(31,31,31)',
|
||||
text: 'rgb(237,237,237)',
|
||||
textSubtle: 'rgb(161,161,161)',
|
||||
textSubtlest: 'rgb(115,115,115)',
|
||||
border: 'rgb(31,31,31)',
|
||||
primary: 'rgb(196,114,251)',
|
||||
secondary: 'rgb(161,161,161)',
|
||||
info: 'rgb(71,168,255)',
|
||||
success: 'rgb(0,202,81)',
|
||||
notice: 'rgb(255,175,0)',
|
||||
warning: '#FF4C8D',
|
||||
danger: '#fd495a',
|
||||
},
|
||||
components: {
|
||||
editor: {
|
||||
danger: '#FF4C8D',
|
||||
warning: '#fd495a',
|
||||
},
|
||||
dialog: {
|
||||
surface: 'rgb(10,10,10)',
|
||||
border: 'rgb(31,31,31)',
|
||||
},
|
||||
sidebar: {
|
||||
border: 'rgb(31,31,31)',
|
||||
},
|
||||
responsePane: {
|
||||
surface: 'rgb(10,10,10)',
|
||||
border: 'rgb(31,31,31)',
|
||||
},
|
||||
appHeader: {
|
||||
surface: 'rgb(10,10,10)',
|
||||
border: 'rgb(31,31,31)',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use log::debug;
|
||||
use mime_guess::{Mime, mime};
|
||||
use mime_guess::{mime, Mime};
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use tokio::fs;
|
||||
@@ -7,14 +6,8 @@ use tokio::fs;
|
||||
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
|
||||
);
|
||||
let (cow, _real_encoding, _exist_replace) = decoder.decode(&body);
|
||||
return cow.into_owned().into();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
use crate::error::Result;
|
||||
use crate::{LicenseCheckStatus, activate_license, check_license, deactivate_license};
|
||||
use log::{debug, info};
|
||||
use tauri::{Runtime, WebviewWindow, command};
|
||||
|
||||
#[command]
|
||||
pub async fn check<R: Runtime>(window: WebviewWindow<R>) -> Result<LicenseCheckStatus> {
|
||||
debug!("Checking license");
|
||||
check_license(&window).await
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub async fn activate<R: Runtime>(license_key: &str, window: WebviewWindow<R>) -> Result<()> {
|
||||
info!("Activating license {}", license_key);
|
||||
activate_license(&window, license_key).await
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub async fn deactivate<R: Runtime>(window: WebviewWindow<R>) -> Result<()> {
|
||||
info!("Deactivating activation");
|
||||
deactivate_license(&window).await
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ pub async fn activate_license<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
license_key: &str,
|
||||
) -> Result<()> {
|
||||
info!("Activating license {}", license_key);
|
||||
let client = reqwest::Client::new();
|
||||
let payload = ActivateLicenseRequestPayload {
|
||||
license_key: license_key.to_string(),
|
||||
@@ -103,6 +104,7 @@ pub async fn activate_license<R: Runtime>(
|
||||
}
|
||||
|
||||
pub async fn deactivate_license<R: Runtime>(window: &WebviewWindow<R>) -> Result<()> {
|
||||
info!("Deactivating activation");
|
||||
let app_handle = window.app_handle();
|
||||
let activation_id = get_activation_id(app_handle).await;
|
||||
|
||||
@@ -159,8 +161,6 @@ pub async fn check_license<R: Runtime>(window: &WebviewWindow<R>) -> Result<Lice
|
||||
let settings = window.db().get_settings();
|
||||
let trial_end = settings.created_at.add(Duration::from_secs(TRIAL_SECONDS));
|
||||
|
||||
debug!("Trial ending at {trial_end:?}");
|
||||
|
||||
let has_activation_id = !activation_id.is_empty();
|
||||
let trial_period_active = Utc::now().naive_utc() < trial_end;
|
||||
|
||||
@@ -185,11 +185,13 @@ pub async fn check_license<R: Runtime>(window: &WebviewWindow<R>) -> Result<Lice
|
||||
}
|
||||
|
||||
if response.status().is_server_error() {
|
||||
warn!("Failed to check license {}", response.status());
|
||||
return Err(ServerError);
|
||||
}
|
||||
|
||||
let body: CheckActivationResponsePayload = response.json().await?;
|
||||
if !body.active {
|
||||
info!("Inactive License {:?}", body);
|
||||
return Ok(LicenseCheckStatus::InvalidLicense);
|
||||
}
|
||||
|
||||
|
||||
@@ -247,6 +247,7 @@ function TextArg({
|
||||
name={arg.name}
|
||||
multiLine={arg.multiLine}
|
||||
onChange={onChange}
|
||||
className={arg.multiLine ? 'min-h-[4rem]' : undefined}
|
||||
defaultValue={value === DYNAMIC_FORM_NULL_ARG ? arg.defaultValue : value}
|
||||
required={!arg.optional}
|
||||
disabled={arg.disabled}
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
setupOrConfigureEncryption,
|
||||
withEncryptionEnabled,
|
||||
} from '../lib/setupOrConfigureEncryption';
|
||||
import { BadgeButton } from './core/BadgeButton';
|
||||
import { PillButton } from './core/PillButton';
|
||||
import { DismissibleBanner } from './core/DismissibleBanner';
|
||||
import type { GenericCompletionConfig } from './core/Editor/genericCompletion';
|
||||
import { Heading } from './core/Heading';
|
||||
@@ -113,20 +113,20 @@ export function EnvironmentEditor({ environment, hideName, className, setRef }:
|
||||
{!hideName && <div className="mr-2">{environment?.name}</div>}
|
||||
{isEncryptionEnabled ? (
|
||||
!allVariableAreEncrypted ? (
|
||||
<BadgeButton color="notice" onClick={() => encryptEnvironment(environment)}>
|
||||
<PillButton color="notice" onClick={() => encryptEnvironment(environment)}>
|
||||
Encrypt All Variables
|
||||
</BadgeButton>
|
||||
</PillButton>
|
||||
) : (
|
||||
<BadgeButton color="secondary" onClick={setupOrConfigureEncryption}>
|
||||
<PillButton color="secondary" onClick={setupOrConfigureEncryption}>
|
||||
Encryption Settings
|
||||
</BadgeButton>
|
||||
</PillButton>
|
||||
)
|
||||
) : (
|
||||
<BadgeButton color="secondary" onClick={() => valueVisibility.set((v) => !v)}>
|
||||
<PillButton color="secondary" onClick={() => valueVisibility.set((v) => !v)}>
|
||||
{valueVisibility.value ? 'Hide Values' : 'Show Values'}
|
||||
</BadgeButton>
|
||||
</PillButton>
|
||||
)}
|
||||
<BadgeButton
|
||||
<PillButton
|
||||
color="secondary"
|
||||
rightSlot={<EnvironmentSharableTooltip />}
|
||||
onClick={async () => {
|
||||
@@ -134,7 +134,7 @@ export function EnvironmentEditor({ environment, hideName, className, setRef }:
|
||||
}}
|
||||
>
|
||||
{environment.public ? 'Sharable' : 'Private'}
|
||||
</BadgeButton>
|
||||
</PillButton>
|
||||
</Heading>
|
||||
{environment.public && (!isEncryptionEnabled || !allVariableAreEncrypted) && (
|
||||
<DismissibleBanner
|
||||
|
||||
@@ -343,7 +343,7 @@ function SetupSyncDropdown({ workspaceMeta }: { workspaceMeta: WorkspaceMeta })
|
||||
color: 'success',
|
||||
label: 'Open Workspace Settings',
|
||||
leftSlot: <Icon icon="settings" />,
|
||||
onSelect: () => openWorkspaceSettings('general'),
|
||||
onSelect: () => openWorkspaceSettings('data'),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
|
||||
@@ -174,7 +174,6 @@ export function GrpcEditor({
|
||||
language="json"
|
||||
autocompleteFunctions
|
||||
autocompleteVariables
|
||||
forceUpdateKey={request.id}
|
||||
defaultValue={request.message}
|
||||
heightMode="auto"
|
||||
placeholder="..."
|
||||
|
||||
@@ -354,7 +354,6 @@ export function HttpRequestPane({ style, fullHeight, className, activeRequest }:
|
||||
isLoading={activeResponse != null && activeResponse.state !== 'closed'}
|
||||
/>
|
||||
<Tabs
|
||||
key={activeRequest.id} // Freshen tabs on request change
|
||||
value={activeTab}
|
||||
label="Request"
|
||||
onChangeValue={setActiveTab}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useAtomValue } from 'jotai';
|
||||
import type { ReactNode } from 'react';
|
||||
import { openSettings } from '../commands/openSettings';
|
||||
import { CargoFeature } from './CargoFeature';
|
||||
import { BadgeButton } from './core/BadgeButton';
|
||||
import { PillButton } from './core/PillButton';
|
||||
import type { ButtonProps } from './core/Button';
|
||||
|
||||
const details: Record<
|
||||
@@ -52,11 +52,11 @@ function LicenseBadgeCmp() {
|
||||
}
|
||||
|
||||
return (
|
||||
<BadgeButton
|
||||
<PillButton
|
||||
color={detail.color}
|
||||
onClick={() => openSettings.mutate('license')}
|
||||
>
|
||||
{detail.label}
|
||||
</BadgeButton>
|
||||
</PillButton>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { Button } from '../core/Button';
|
||||
import { Icon } from '../core/Icon';
|
||||
import { Link } from '../core/Link';
|
||||
import { PlainInput } from '../core/PlainInput';
|
||||
import { Separator } from '../core/Separator';
|
||||
import { HStack, VStack } from '../core/Stacks';
|
||||
import { LocalImage } from '../LocalImage';
|
||||
|
||||
@@ -34,57 +35,70 @@ function SettingsLicenseCmp() {
|
||||
<div className="flex flex-col gap-6 max-w-xl">
|
||||
{check.data?.type === 'commercial_use' ? (
|
||||
<Banner color="success">Your license is active 🥳</Banner>
|
||||
) : check.data?.type == 'trialing' ? (
|
||||
<Banner color="info" className="flex flex-col gap-3 max-w-lg">
|
||||
<p>
|
||||
<strong>
|
||||
{pluralizeCount('day', differenceInDays(check.data.end, new Date()))} remaining
|
||||
</strong>{' '}
|
||||
on your commercial-use trial
|
||||
</p>
|
||||
</Banner>
|
||||
) : check.data?.type == 'personal_use' ? (
|
||||
<Banner color="notice" className="flex flex-col gap-3 max-w-lg">
|
||||
<p>You are able to use Yaak for personal use only</p>
|
||||
</Banner>
|
||||
) : null}
|
||||
|
||||
{check.data?.type !== 'commercial_use' && (
|
||||
<div className="grid grid-cols-[auto_minmax(0,1fr)] gap-6 items-center my-3 ">
|
||||
<LocalImage src="static/greg.jpeg" className="rounded-full h-20 w-20" />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h2 className="text-lg font-bold">Hey, I'm Greg 👋🏼</h2>
|
||||
<p>
|
||||
Yaak is free for personal projects and learning.{' '}
|
||||
{check.data?.type === 'trialing' ? 'Once your trial ends, a ' : 'A '}
|
||||
license will be required for work or commercial use.
|
||||
</p>
|
||||
<p>
|
||||
) : check.data?.type === 'trialing' ? (
|
||||
<Banner color="info" className="@container flex items-center gap-x-5 max-w-xl">
|
||||
<LocalImage src="static/greg.jpeg" className="hidden @sm:block rounded-full h-14 w-14" />
|
||||
<p className="w-full">
|
||||
<strong>{pluralizeCount('day', differenceInDays(check.data.end, new Date()))}</strong>{' '}
|
||||
left to evaluate Yaak for commercial use.
|
||||
<br />
|
||||
<span className="opacity-50">Personal use is always free, forever.</span>
|
||||
<Separator className="my-2" />
|
||||
<div className="flex flex-wrap items-center gap-x-2 text-sm text-notice">
|
||||
<Link noUnderline href="mailto:support@yaak.app">
|
||||
Contact Support
|
||||
</Link>
|
||||
<Icon icon="dot" size="sm" color="secondary" />
|
||||
<Link
|
||||
noUnderline
|
||||
href={`https://yaak.app/pricing?s=learn&t=${check.data?.type ?? ''}`}
|
||||
className="text-sm text-notice opacity-80 hover:opacity-100"
|
||||
>
|
||||
Learn More
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</p>
|
||||
</Banner>
|
||||
) : check.data?.type === 'personal_use' ? (
|
||||
<Banner color="notice" className="@container flex items-center gap-x-5 max-w-xl">
|
||||
<LocalImage src="static/greg.jpeg" className="hidden @sm:block rounded-full h-14 w-14" />
|
||||
<p className="w-full">
|
||||
Your commercial-use trial has ended.
|
||||
<br />
|
||||
<span className="opacity-50">
|
||||
You may continue using Yaak for personal use free, forever.
|
||||
<br />
|
||||
A license is required for commercial use.
|
||||
</span>
|
||||
<Separator className="my-2" />
|
||||
<div className="flex flex-wrap items-center gap-x-2 text-sm text-notice">
|
||||
<Link noUnderline href="mailto:support@yaak.app">
|
||||
Contact Support
|
||||
</Link>
|
||||
<Icon icon="dot" size="sm" color="secondary" />
|
||||
<Link
|
||||
noUnderline
|
||||
href={`https://yaak.app/pricing?s=learn&t=${check.data?.type ?? ''}`}
|
||||
>
|
||||
Learn More
|
||||
</Link>
|
||||
</div>
|
||||
</p>
|
||||
</Banner>
|
||||
) : null}
|
||||
|
||||
{check.error && <Banner color="danger">{check.error}</Banner>}
|
||||
{activate.error && <Banner color="danger">{activate.error}</Banner>}
|
||||
|
||||
{check.data?.type === 'invalid_license' && (
|
||||
<Banner color="danger">
|
||||
Your license is invalid. Please <Link href="https://yaak.app/dashboard">Sign In</Link> for
|
||||
more details
|
||||
</Banner>
|
||||
)}
|
||||
|
||||
{check.data?.type === 'commercial_use' ? (
|
||||
<HStack space={2}>
|
||||
<Button
|
||||
variant="border"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
deactivate.mutate();
|
||||
}}
|
||||
>
|
||||
<Button variant="border" color="secondary" size="sm" onClick={() => deactivate.mutate()}>
|
||||
Deactivate License
|
||||
</Button>
|
||||
<Button
|
||||
@@ -104,12 +118,12 @@ function SettingsLicenseCmp() {
|
||||
<Button
|
||||
size="sm"
|
||||
color="primary"
|
||||
rightSlot={<Icon icon="external_link" />}
|
||||
onClick={() =>
|
||||
openUrl(
|
||||
`https://yaak.app/pricing?s=purchase&ref=app.yaak.desktop&t=${check.data?.type ?? ''}`,
|
||||
)
|
||||
}
|
||||
rightSlot={<Icon icon="external_link" />}
|
||||
>
|
||||
Purchase License
|
||||
</Button>
|
||||
|
||||
@@ -223,10 +223,12 @@ function Sidebar({ className }: { className?: string }) {
|
||||
for (const n of node.children ?? []) {
|
||||
if (n.item.model !== 'folder') continue;
|
||||
collapsed[n.item.id] = true;
|
||||
collapsed = next(n, collapsed);
|
||||
}
|
||||
return collapsed;
|
||||
};
|
||||
jotaiStore.set(collapsedFamily(treeId), next(tree, {}));
|
||||
const collapsed = next(tree, {});
|
||||
jotaiStore.set(collapsedFamily(treeId), collapsed);
|
||||
},
|
||||
},
|
||||
'sidebar.selected.delete': {
|
||||
@@ -435,7 +437,7 @@ function Sidebar({ className }: { className?: string }) {
|
||||
aria-hidden={hidden ?? undefined}
|
||||
className={classNames(className, 'h-full grid grid-rows-[auto_minmax(0,1fr)_auto]')}
|
||||
>
|
||||
<div className="w-full px-3 pt-3 grid grid-cols-[minmax(0,1fr)_auto] items-center -mr-2.5">
|
||||
<div className="w-full pl-3 pr-0.5 pt-3 grid grid-cols-[minmax(0,1fr)_auto] items-center">
|
||||
{(tree.children?.length ?? 0) > 0 && (
|
||||
<>
|
||||
<Input
|
||||
@@ -485,7 +487,6 @@ function Sidebar({ className }: { className?: string }) {
|
||||
size="xs"
|
||||
className="ml-0.5 text-text-subtle hover:text-text"
|
||||
icon="ellipsis_vertical"
|
||||
hotkeyAction="sidebar.collapse_all"
|
||||
title="Show sidebar actions menu"
|
||||
/>
|
||||
</Dropdown>
|
||||
@@ -630,7 +631,7 @@ const sidebarTreeAtom = atom<[TreeNode<SidebarModel>, FieldDef[]] | null>((get)
|
||||
|
||||
const root: TreeNode<SidebarModel> = {
|
||||
item: activeWorkspace,
|
||||
parent: null,
|
||||
parent: null,
|
||||
children: [],
|
||||
depth: 0,
|
||||
};
|
||||
|
||||
@@ -229,7 +229,6 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque
|
||||
/>
|
||||
</div>
|
||||
<Tabs
|
||||
key={activeRequest.id} // Freshen tabs on request change
|
||||
value={activeTab}
|
||||
label="Request"
|
||||
onChangeValue={setActiveTab}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useToggleCommandPalette } from '../hooks/useToggleCommandPalette';
|
||||
import { workspaceLayoutAtom } from '../lib/atoms';
|
||||
import { setupOrConfigureEncryption } from '../lib/setupOrConfigureEncryption';
|
||||
import { CookieDropdown } from './CookieDropdown';
|
||||
import { BadgeButton } from './core/BadgeButton';
|
||||
import { PillButton } from './core/PillButton';
|
||||
import { Icon } from './core/Icon';
|
||||
import { IconButton } from './core/IconButton';
|
||||
import { HStack } from './core/Stacks';
|
||||
@@ -52,9 +52,9 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop
|
||||
<div className="flex-1 flex gap-1 items-center h-full justify-end pointer-events-none pr-1">
|
||||
<ImportCurlButton />
|
||||
{showEncryptionSetup ? (
|
||||
<BadgeButton color="danger" onClick={setupOrConfigureEncryption}>
|
||||
<PillButton color="danger" onClick={setupOrConfigureEncryption}>
|
||||
Enter Encryption Key
|
||||
</BadgeButton>
|
||||
</PillButton>
|
||||
) : (
|
||||
<LicenseBadge />
|
||||
)}
|
||||
|
||||
@@ -73,7 +73,7 @@ export function WorkspaceSettingsDialog({ workspaceId, hide, tab }: Props) {
|
||||
{ value: TAB_GENERAL, label: 'General' },
|
||||
{
|
||||
value: TAB_DATA,
|
||||
label: 'Directory Sync',
|
||||
label: 'Data',
|
||||
},
|
||||
...headersTab,
|
||||
...authTab,
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
}
|
||||
|
||||
/* Matching bracket */
|
||||
|
||||
.cm-matchingBracket {
|
||||
@apply bg-transparent border-b border-b-text-subtle;
|
||||
}
|
||||
@@ -76,6 +77,16 @@
|
||||
.cm-gutterElement {
|
||||
@apply cursor-default;
|
||||
}
|
||||
|
||||
.cm-lint-marker {
|
||||
@apply cursor-default opacity-80 hover:opacity-100 transition-opacity;
|
||||
@apply rounded-full w-[0.9em] h-[0.9em];
|
||||
content: '';
|
||||
|
||||
&.cm-lint-marker-error {
|
||||
@apply bg-danger;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.template-tag {
|
||||
@@ -110,6 +121,7 @@
|
||||
@apply font-mono text-xs;
|
||||
|
||||
/* Hide scrollbars */
|
||||
|
||||
&::-webkit-scrollbar-corner,
|
||||
&::-webkit-scrollbar {
|
||||
@apply hidden !important;
|
||||
@@ -202,7 +214,7 @@
|
||||
}
|
||||
|
||||
.cm-editor .cm-activeLineGutter {
|
||||
@apply bg-transparent;
|
||||
@apply bg-transparent text-text-subtle;
|
||||
}
|
||||
|
||||
/* Cursor and mouse cursor for readonly mode */
|
||||
@@ -226,17 +238,35 @@
|
||||
}
|
||||
}
|
||||
|
||||
.cm-tooltip-lint {
|
||||
@apply font-mono text-editor rounded overflow-hidden bg-surface-highlight border border-border shadow !important;
|
||||
|
||||
.cm-diagnostic-error {
|
||||
@apply border-l-danger px-4 py-2;
|
||||
}
|
||||
}
|
||||
|
||||
.cm-lintPoint {
|
||||
&.cm-lintPoint-error {
|
||||
&::after {
|
||||
@apply border-b-danger;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cm-tooltip.cm-tooltip-hover {
|
||||
@apply shadow-lg bg-surface rounded text-text-subtle border border-border-subtle z-50 pointer-events-auto text-sm;
|
||||
@apply p-1.5;
|
||||
|
||||
/* Style the tooltip for popping up "open in browser" and other stuff */
|
||||
|
||||
a, button {
|
||||
@apply text-text hover:bg-surface-highlight w-full h-sm flex items-center px-2 rounded;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply cursor-default !important;
|
||||
|
||||
&::after {
|
||||
@apply text-text bg-text h-3 w-3 ml-1;
|
||||
content: '';
|
||||
|
||||
@@ -91,6 +91,7 @@ export interface EditorProps {
|
||||
placeholder?: string;
|
||||
readOnly?: boolean;
|
||||
singleLine?: boolean;
|
||||
containerOnly?: boolean;
|
||||
stateKey: string | null;
|
||||
tooltipContainer?: HTMLElement;
|
||||
type?: 'text' | 'password';
|
||||
@@ -131,6 +132,7 @@ export function Editor({
|
||||
placeholder,
|
||||
readOnly,
|
||||
singleLine,
|
||||
containerOnly,
|
||||
stateKey,
|
||||
type,
|
||||
wrapLines,
|
||||
@@ -376,7 +378,7 @@ export function Editor({
|
||||
|
||||
// Initialize the editor when ref mounts
|
||||
const initEditorRef = useCallback(
|
||||
function initializeCodemirror(container: HTMLDivElement | null) {
|
||||
function initEditorRef(container: HTMLDivElement | null) {
|
||||
if (container === null) {
|
||||
cm.current?.view.destroy();
|
||||
cm.current = null;
|
||||
@@ -455,17 +457,9 @@ export function Editor({
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[],
|
||||
[forceUpdateKey],
|
||||
);
|
||||
|
||||
// Update editor doc when force update key changes
|
||||
useEffect(() => {
|
||||
if (cm.current?.view != null) {
|
||||
updateContents(cm.current.view, defaultValue || '');
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [forceUpdateKey]);
|
||||
|
||||
// For read-only mode, update content when `defaultValue` changes
|
||||
useEffect(
|
||||
function updateReadOnlyEditor() {
|
||||
@@ -548,7 +542,7 @@ export function Editor({
|
||||
/>
|
||||
);
|
||||
|
||||
if (singleLine) {
|
||||
if (singleLine || containerOnly) {
|
||||
return cmContainer;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
indentOnInput,
|
||||
syntaxHighlighting,
|
||||
} from '@codemirror/language';
|
||||
import { lintKeymap } from '@codemirror/lint';
|
||||
import { linter, lintGutter, lintKeymap } from '@codemirror/lint';
|
||||
|
||||
import { search, searchKeymap } from '@codemirror/search';
|
||||
import type { Extension } from '@codemirror/state';
|
||||
@@ -45,6 +45,7 @@ import { renderMarkdown } from '../../../lib/markdown';
|
||||
import { pluralizeCount } from '../../../lib/pluralize';
|
||||
import { showGraphQLDocExplorerAtom } from '../../graphql/graphqlAtoms';
|
||||
import type { EditorProps } from './Editor';
|
||||
import { jsonParseLinter } from './json-lint';
|
||||
import { pairs } from './pairs/extension';
|
||||
import { text } from './text/extension';
|
||||
import type { TwigCompletionOption } from './twig/completion';
|
||||
@@ -62,7 +63,7 @@ export const syntaxHighlightStyle = HighlightStyle.define([
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
{
|
||||
tag: [t.paren, t.bracket, t.squareBracket, t.brace, t.separator, t.punctuation],
|
||||
tag: [t.angleBracket, t.paren, t.bracket, t.squareBracket, t.brace, t.separator, t.punctuation],
|
||||
color: 'var(--textSubtle)',
|
||||
},
|
||||
{
|
||||
@@ -153,6 +154,10 @@ export function getLanguageExtension({
|
||||
];
|
||||
}
|
||||
|
||||
if (language === 'json') {
|
||||
extraExtensions.push(linter(jsonParseLinter()), lintGutter());
|
||||
}
|
||||
|
||||
const maybeBase = language ? syntaxExtensions[language] : null;
|
||||
const base = typeof maybeBase === 'function' ? maybeBase() : null;
|
||||
if (base == null) {
|
||||
|
||||
@@ -112,7 +112,6 @@ function fieldValueCompletions(
|
||||
): Completion[] | null {
|
||||
if (!def || !def.values) return null;
|
||||
const vals = Array.isArray(def.values) ? def.values : def.values();
|
||||
// console.log("HELLO", v, v.match(IDENT));
|
||||
return vals.map((v) => ({
|
||||
label: v.match(IDENT_ONLY) ? v : `"${v}"`,
|
||||
displayLabel: v,
|
||||
|
||||
36
src-web/components/core/Editor/json-lint.ts
Normal file
36
src-web/components/core/Editor/json-lint.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import type { Diagnostic } from '@codemirror/lint';
|
||||
import type { EditorView } from '@codemirror/view';
|
||||
import { parse as jsonLintParse } from '@prantlf/jsonlint';
|
||||
|
||||
const TEMPLATE_SYNTAX_REGEX = /\$\{\[[\s\S]*?]}/g;
|
||||
|
||||
export function jsonParseLinter() {
|
||||
return (view: EditorView): Diagnostic[] => {
|
||||
try {
|
||||
const doc = view.state.doc.toString();
|
||||
// We need lint to not break on stuff like {"foo:" ${[ ... ]}} so we'll replace all template
|
||||
// syntax with repeating `1` characters, so it's valid JSON and the position is still correct.
|
||||
const escapedDoc = doc.replace(TEMPLATE_SYNTAX_REGEX, '1');
|
||||
jsonLintParse(escapedDoc);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (err: any) {
|
||||
if (!('location' in err)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// const line = location?.start?.line;
|
||||
// const column = location?.start?.column;
|
||||
if (err.location.start.offset) {
|
||||
return [
|
||||
{
|
||||
from: err.location.start.offset,
|
||||
to: err.location.start.offset,
|
||||
severity: 'error',
|
||||
message: err.message,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
};
|
||||
}
|
||||
@@ -302,6 +302,7 @@ function BaseInput({
|
||||
id={id.current}
|
||||
hideGutter
|
||||
singleLine={!multiLine}
|
||||
containerOnly
|
||||
stateKey={stateKey}
|
||||
wrapLines={wrapLines}
|
||||
heightMode="auto"
|
||||
@@ -401,7 +402,7 @@ function EncryptionInput({
|
||||
setState({ fieldType: 'encrypted', security, value, obscured: true, error: null });
|
||||
// We're calling this here because we want the input to be fully initialized so the caller
|
||||
// can do stuff like change the selection.
|
||||
setRef?.(inputRef.current);
|
||||
requestAnimationFrame(() => setRef?.(inputRef.current));
|
||||
},
|
||||
onError: (value) => {
|
||||
setState({
|
||||
|
||||
@@ -51,7 +51,7 @@ export function KeyValueRow({
|
||||
<span className="select-text cursor-text">{label}</span>
|
||||
</td>
|
||||
<td className="select-none py-0.5 break-all align-top max-w-[15rem]">
|
||||
<div className="select-text cursor-text max-h-[5rem] overflow-y-auto grid grid-cols-[auto_minmax(0,1fr)_auto]">
|
||||
<div className="select-text cursor-text max-h-[12rem] overflow-y-auto grid grid-cols-[auto_minmax(0,1fr)_auto]">
|
||||
{leftSlot ?? <span aria-hidden />}
|
||||
{children}
|
||||
{rightSlot ? <div className="ml-1.5">{rightSlot}</div> : <span aria-hidden />}
|
||||
|
||||
@@ -12,32 +12,37 @@ interface Props extends HTMLAttributes<HTMLAnchorElement> {
|
||||
export function Link({ href, children, noUnderline, className, ...other }: Props) {
|
||||
const isExternal = href.match(/^https?:\/\//);
|
||||
|
||||
className = classNames(className, 'relative');
|
||||
className = classNames(
|
||||
className,
|
||||
'relative',
|
||||
'inline-flex items-center hover:underline group',
|
||||
!noUnderline && 'underline',
|
||||
);
|
||||
|
||||
if (isExternal) {
|
||||
const isYaakLink = href.startsWith('https://yaak.app');
|
||||
let finalHref = href;
|
||||
if (href.startsWith('https://yaak.app')) {
|
||||
if (isYaakLink) {
|
||||
const url = new URL(href);
|
||||
url.searchParams.set('ref', appInfo.identifier);
|
||||
finalHref = url.toString();
|
||||
}
|
||||
return (
|
||||
// eslint-disable-next-line react/jsx-no-target-blank
|
||||
<a
|
||||
href={finalHref}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={classNames(
|
||||
className,
|
||||
'pr-4 inline-flex items-center hover:underline group',
|
||||
!noUnderline && 'underline',
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
rel={isYaakLink ? undefined : 'noopener noreferrer'}
|
||||
onClick={(e) => e.preventDefault()}
|
||||
className={className}
|
||||
{...other}
|
||||
>
|
||||
<span className="pr-0.5">{children}</span>
|
||||
<Icon className="inline absolute right-0.5 top-[0.3em] opacity-70 group-hover:opacity-100" size="xs" icon="external_link" />
|
||||
<span className="pr-5">{children}</span>
|
||||
<Icon
|
||||
className="inline absolute right-0.5 top-[0.3em] opacity-70 group-hover:opacity-100"
|
||||
size="xs"
|
||||
icon="external_link"
|
||||
/>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -128,14 +128,19 @@ export function PairEditor({
|
||||
|
||||
const initPairEditorRow = useCallback(
|
||||
(id: string, n: RowHandle | null) => {
|
||||
const isLast = id === pairs[pairs.length - 1]?.id;
|
||||
if (isLast) return; // Never add the last pair
|
||||
|
||||
rowsRef.current[id] = n;
|
||||
const ready =
|
||||
Object.values(rowsRef.current).filter((v) => v != null).length === pairs.length - 1; // Ignore the last placeholder pair
|
||||
const validHandles = Object.values(rowsRef.current).filter((v) => v != null);
|
||||
|
||||
// NOTE: Ignore the last placeholder pair
|
||||
const ready = validHandles.length === pairs.length - 1;
|
||||
if (ready) {
|
||||
setRef?.(handle);
|
||||
}
|
||||
},
|
||||
[handle, pairs.length, setRef],
|
||||
[handle, pairs, setRef],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import classNames from 'classnames';
|
||||
import type { ButtonProps } from './Button';
|
||||
import { Button } from './Button';
|
||||
|
||||
export function BadgeButton({ className, ...props }: ButtonProps) {
|
||||
export function PillButton({ className, ...props }: ButtonProps) {
|
||||
return (
|
||||
<Button
|
||||
size="2xs"
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { Color } from '@yaakapp-internal/plugins';
|
||||
import classNames from 'classnames';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
@@ -6,9 +7,10 @@ interface Props {
|
||||
dashed?: boolean;
|
||||
className?: string;
|
||||
children?: ReactNode;
|
||||
color?: Color;
|
||||
}
|
||||
|
||||
export function Separator({ className, dashed, orientation = 'horizontal', children }: Props) {
|
||||
export function Separator({ color, className, dashed, orientation = 'horizontal', children }: Props) {
|
||||
return (
|
||||
<div role="separator" className={classNames(className, 'flex items-center w-full')}>
|
||||
{children && (
|
||||
@@ -16,7 +18,15 @@ export function Separator({ className, dashed, orientation = 'horizontal', child
|
||||
)}
|
||||
<div
|
||||
className={classNames(
|
||||
'h-0 border-t border-t-border-subtle',
|
||||
'h-0 border-t opacity-60',
|
||||
color == null && 'border-border',
|
||||
color === 'primary' && 'border-primary',
|
||||
color === 'secondary' && 'border-secondary',
|
||||
color === 'success' && 'border-success',
|
||||
color === 'notice' && 'border-notice',
|
||||
color === 'warning' && 'border-warning',
|
||||
color === 'danger' && 'border-danger',
|
||||
color === 'info' && 'border-info',
|
||||
dashed && 'border-dashed',
|
||||
orientation === 'horizontal' && 'w-full h-[1px]',
|
||||
orientation === 'vertical' && 'h-full w-[1px]',
|
||||
|
||||
@@ -9,6 +9,7 @@ export interface TooltipProps {
|
||||
content: ReactNode;
|
||||
tabIndex?: number;
|
||||
size?: 'md' | 'lg';
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const hiddenStyles: CSSProperties = {
|
||||
@@ -19,7 +20,7 @@ const hiddenStyles: CSSProperties = {
|
||||
opacity: 0,
|
||||
};
|
||||
|
||||
export function Tooltip({ children, content, tabIndex, size = 'md' }: TooltipProps) {
|
||||
export function Tooltip({ children, className, content, tabIndex, size = 'md' }: TooltipProps) {
|
||||
const [isOpen, setIsOpen] = useState<CSSProperties>();
|
||||
const triggerRef = useRef<HTMLButtonElement>(null);
|
||||
const tooltipRef = useRef<HTMLDivElement>(null);
|
||||
@@ -94,8 +95,8 @@ export function Tooltip({ children, content, tabIndex, size = 'md' }: TooltipPro
|
||||
ref={triggerRef}
|
||||
role="button"
|
||||
aria-describedby={isOpen ? id.current : undefined}
|
||||
tabIndex={tabIndex ?? 0}
|
||||
className="flex-grow-0 flex items-center"
|
||||
tabIndex={tabIndex ?? -1}
|
||||
className={classNames(className, 'flex-grow-0 flex items-center')}
|
||||
onClick={handleToggleImmediate}
|
||||
onMouseEnter={handleOpen}
|
||||
onMouseLeave={handleClose}
|
||||
|
||||
@@ -248,7 +248,7 @@ function TreeItem_<T extends { id: string }>({
|
||||
attributes,
|
||||
listeners,
|
||||
setNodeRef: setDraggableRef,
|
||||
} = useDraggable({ id: node.item.id, disabled: node.draggable === false });
|
||||
} = useDraggable({ id: node.item.id, disabled: node.draggable === false || editing });
|
||||
|
||||
const { setNodeRef: setDroppableRef } = useDroppable({ id: node.item.id });
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { HttpRequest } from '@yaakapp-internal/models';
|
||||
|
||||
import { formatSdl } from 'format-graphql';
|
||||
import { useAtom } from 'jotai';
|
||||
import { useMemo } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useLocalStorage } from 'react-use';
|
||||
import { useIntrospectGraphQL } from '../../hooks/useIntrospectGraphQL';
|
||||
import { useStateWithDeps } from '../../hooks/useStateWithDeps';
|
||||
@@ -45,20 +45,31 @@ export function GraphQLEditor({ request, onChange, baseRequest, ...extraEditorPr
|
||||
|
||||
return { query: request.body.query ?? '', variables: request.body.variables ?? '' };
|
||||
}, [extraEditorProps.forceUpdateKey]);
|
||||
|
||||
const [isDocOpenRecord, setGraphqlDocStateAtomValue] = useAtom(showGraphQLDocExplorerAtom);
|
||||
const isDocOpen = isDocOpenRecord[request.id] !== undefined;
|
||||
|
||||
const handleChangeQuery = (query: string) => {
|
||||
const newBody = { query, variables: currentBody.variables || undefined };
|
||||
setCurrentBody(newBody);
|
||||
onChange(newBody);
|
||||
};
|
||||
const handleChangeQuery = useCallback(
|
||||
(query: string) => {
|
||||
setCurrentBody(({ variables }) => {
|
||||
const newBody = { query, variables };
|
||||
onChange(newBody);
|
||||
return newBody;
|
||||
});
|
||||
},
|
||||
[onChange, setCurrentBody],
|
||||
);
|
||||
|
||||
const handleChangeVariables = (variables: string) => {
|
||||
const newBody = { query: currentBody.query, variables: variables || undefined };
|
||||
setCurrentBody(newBody);
|
||||
onChange(newBody);
|
||||
};
|
||||
const handleChangeVariables = useCallback(
|
||||
(variables: string) => {
|
||||
setCurrentBody(({ query }) => {
|
||||
const newBody = { query, variables: variables || undefined };
|
||||
onChange(newBody);
|
||||
return newBody;
|
||||
});
|
||||
},
|
||||
[onChange, setCurrentBody],
|
||||
);
|
||||
|
||||
const actions = useMemo<EditorProps['actions']>(
|
||||
() => [
|
||||
|
||||
@@ -35,7 +35,7 @@ export type HotkeyAction =
|
||||
| 'workspace_settings.show';
|
||||
|
||||
const hotkeys: Record<HotkeyAction, string[]> = {
|
||||
'app.zoom_in': ['CmdCtrl+Plus'],
|
||||
'app.zoom_in': ['CmdCtrl+Equal'],
|
||||
'app.zoom_out': ['CmdCtrl+Minus'],
|
||||
'app.zoom_reset': ['CmdCtrl+0'],
|
||||
'command_palette.toggle': ['CmdCtrl+k'],
|
||||
@@ -50,7 +50,7 @@ const hotkeys: Record<HotkeyAction, string[]> = {
|
||||
'switcher.toggle': ['CmdCtrl+p'],
|
||||
'settings.show': ['CmdCtrl+,'],
|
||||
'sidebar.filter': ['CmdCtrl+f'],
|
||||
'sidebar.expand_all': ['CmdCtrl+Shift+Plus'],
|
||||
'sidebar.expand_all': ['CmdCtrl+Shift+Equal'],
|
||||
'sidebar.collapse_all': ['CmdCtrl+Shift+Minus'],
|
||||
'sidebar.selected.delete': ['Delete', 'CmdCtrl+Backspace'],
|
||||
'sidebar.selected.duplicate': ['CmdCtrl+d'],
|
||||
@@ -190,7 +190,7 @@ function handleKeyDown(e: KeyboardEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't support certain single-key combinrations within inputs
|
||||
// Don't support certain single-key combinations within inputs
|
||||
if (
|
||||
(e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) &&
|
||||
currentKeysWithModifiers.size === 1 &&
|
||||
@@ -212,16 +212,7 @@ function handleKeyDown(e: KeyboardEvent) {
|
||||
|
||||
for (const hkKey of hkKeys) {
|
||||
const keys = hkKey.split('+');
|
||||
const adjustedKeys = keys
|
||||
.map((k) => {
|
||||
// Special case for Plus
|
||||
if (keys.includes('Shift') && k === 'Plus') {
|
||||
return 'Equal';
|
||||
} else {
|
||||
return k;
|
||||
}
|
||||
})
|
||||
.map(resolveHotkeyKey);
|
||||
const adjustedKeys = keys.map(resolveHotkeyKey);
|
||||
if (compareKeys(adjustedKeys, Array.from(currentKeysWithModifiers))) {
|
||||
if (!options.allowDefault) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -39,6 +39,8 @@ export async function editEnvironment(
|
||||
}
|
||||
}
|
||||
|
||||
let didFocusVariable = false;
|
||||
|
||||
toggleDialog({
|
||||
id: 'environment-editor',
|
||||
noPadding: true,
|
||||
@@ -48,8 +50,9 @@ export async function editEnvironment(
|
||||
<EnvironmentEditDialog
|
||||
initialEnvironmentId={environment?.id ?? null}
|
||||
setRef={(pairEditor: PairEditorHandle | null) => {
|
||||
if (focusId) {
|
||||
if (focusId && !didFocusVariable) {
|
||||
pairEditor?.focusValue(focusId);
|
||||
didFocusVariable = true;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"@gilbarbara/deep-equal": "^0.3.1",
|
||||
"@lezer/highlight": "^1.1.3",
|
||||
"@lezer/lr": "^1.3.3",
|
||||
"@prantlf/jsonlint": "^16.0.0",
|
||||
"@replit/codemirror-emacs": "^6.1.0",
|
||||
"@replit/codemirror-vim": "^6.3.0",
|
||||
"@replit/codemirror-vscode-keymap": "^6.0.2",
|
||||
|
||||
Reference in New Issue
Block a user