Custom JSON formatter that works with template syntax (#128)

This commit is contained in:
Gregory Schier
2024-10-21 22:17:14 +00:00
committed by GitHub
parent aa7f18a16f
commit e216214085
17 changed files with 414 additions and 117 deletions

88
package-lock.json generated
View File

@@ -18,7 +18,7 @@
"src-web" "src-web"
], ],
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "^2.0.3", "@tauri-apps/cli": "^2.0.4",
"@typescript-eslint/eslint-plugin": "^8.5.0", "@typescript-eslint/eslint-plugin": "^8.5.0",
"@typescript-eslint/parser": "^8.5.0", "@typescript-eslint/parser": "^8.5.0",
"eslint": "^8", "eslint": "^8",
@@ -2689,9 +2689,9 @@
} }
}, },
"node_modules/@tauri-apps/cli": { "node_modules/@tauri-apps/cli": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.0.4.tgz",
"integrity": "sha512-JwEyhc5BAVpn4E8kxzY/h7+bVOiXQdudR1r3ODMfyyumZBfgIWqpD/WuTcPq6Yjchju1BSS+80jAE/oYwI/RKg==", "integrity": "sha512-Hl9eFXz+O366+6su9PfaSzu2EJdFe1p8K8ghkWmi40dz8VmSE7vsMTaOStD0I71ckSOkh2ICDX7FQTBgjlpjWw==",
"dev": true, "dev": true,
"license": "Apache-2.0 OR MIT", "license": "Apache-2.0 OR MIT",
"bin": { "bin": {
@@ -2705,22 +2705,22 @@
"url": "https://opencollective.com/tauri" "url": "https://opencollective.com/tauri"
}, },
"optionalDependencies": { "optionalDependencies": {
"@tauri-apps/cli-darwin-arm64": "2.0.3", "@tauri-apps/cli-darwin-arm64": "2.0.4",
"@tauri-apps/cli-darwin-x64": "2.0.3", "@tauri-apps/cli-darwin-x64": "2.0.4",
"@tauri-apps/cli-linux-arm-gnueabihf": "2.0.3", "@tauri-apps/cli-linux-arm-gnueabihf": "2.0.4",
"@tauri-apps/cli-linux-arm64-gnu": "2.0.3", "@tauri-apps/cli-linux-arm64-gnu": "2.0.4",
"@tauri-apps/cli-linux-arm64-musl": "2.0.3", "@tauri-apps/cli-linux-arm64-musl": "2.0.4",
"@tauri-apps/cli-linux-x64-gnu": "2.0.3", "@tauri-apps/cli-linux-x64-gnu": "2.0.4",
"@tauri-apps/cli-linux-x64-musl": "2.0.3", "@tauri-apps/cli-linux-x64-musl": "2.0.4",
"@tauri-apps/cli-win32-arm64-msvc": "2.0.3", "@tauri-apps/cli-win32-arm64-msvc": "2.0.4",
"@tauri-apps/cli-win32-ia32-msvc": "2.0.3", "@tauri-apps/cli-win32-ia32-msvc": "2.0.4",
"@tauri-apps/cli-win32-x64-msvc": "2.0.3" "@tauri-apps/cli-win32-x64-msvc": "2.0.4"
} }
}, },
"node_modules/@tauri-apps/cli-darwin-arm64": { "node_modules/@tauri-apps/cli-darwin-arm64": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.0.4.tgz",
"integrity": "sha512-jIsbxGWS+As1ZN7umo90nkql/ZAbrDK0GBT6UsgHSz5zSwwArICsZFFwE1pLZip5yoiV5mn3TGG2c1+v+0puzQ==", "integrity": "sha512-siH7rOHobb16rPbc11k64p1mxIpiRCkWmzs2qmL5IX21Gx9K5onI3Tk67Oqpf2uNupbYzItrOttaDT4NHFC7tw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -2735,9 +2735,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-darwin-x64": { "node_modules/@tauri-apps/cli-darwin-x64": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.0.4.tgz",
"integrity": "sha512-ROITHtLTA1muyrwgyuwyasmaLCGtT4as/Kd1kerXaSDtFcYrnxiM984ZD0+FDUEDl5BgXtYa/sKKkKQFjgmM0A==", "integrity": "sha512-zIccfbCoZMfmUpnk6PFCV0keFyfVj1A9XV3Oiiitj/dkTZ9CQvzjhX3XC0XcK4rsTWegfr2PjSrK06aiPAROAw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -2752,9 +2752,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.0.4.tgz",
"integrity": "sha512-bQ3EZwCFfrLg/ZQ2I8sLuifSxESz4TP56SleTkKsPtTIZgNnKpM88PRDz4neiRroHVOq8NK0X276qi9LjGcXPw==", "integrity": "sha512-fgQqJzefOGWCBNg4yrVA82Rg4s1XQr5K0dc2rCxBhJfa696e8dQ1LDrnWq/AiO5r+uHfVaoQTIUvxxpFicYRSA==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -2769,9 +2769,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-linux-arm64-gnu": { "node_modules/@tauri-apps/cli-linux-arm64-gnu": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.0.4.tgz",
"integrity": "sha512-aLfAA8P9OTErVUk3sATxtXqpAtlfDPMPp4fGjDysEELG/MyekGhmh2k/kG/i32OdPeCfO+Nr37wJksARJKubGw==", "integrity": "sha512-u8wbt5tPA9pI6j+d7jGrfOz9UVCiTp+IYzKNiIqlrDsAjqAUFaNXYHKqOUboeFWEmI4zoCWj6LgpS2OJTQ5FKg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -2786,9 +2786,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-linux-arm64-musl": { "node_modules/@tauri-apps/cli-linux-arm64-musl": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.0.4.tgz",
"integrity": "sha512-I4MVD7nf6lLLRmNQPpe5beEIFM6q7Zkmh77ROA5BNu/+vHNL5kiTMD+bmd10ZL2r753A6pO7AvqkIxcBuIl0tg==", "integrity": "sha512-hntF1V8e3V1hlrESm93PsghDhf3lA5pbvFrRfYxU1c+fVD/jRXGVw8BH3O1lW8MWwhEg1YdhKk01oAgsuHLuig==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -2803,9 +2803,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-linux-x64-gnu": { "node_modules/@tauri-apps/cli-linux-x64-gnu": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.0.4.tgz",
"integrity": "sha512-C6Jkx2zZGKkoi+sg5FK9GoH/0EvAaOgrZfF5azV5EALGba46g7VpWcZgp9zFUd7K2IzTi+0OOY8TQ2OVfKZgew==", "integrity": "sha512-Iq1GGJb+oT1T0ZV8izrgf0cBtlzPCJaWcNueRbf1ZXquMf+FSTyQv+/Lo8rq5T6buOIJOH7cAOTuEWWqiCZteg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -2820,9 +2820,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-linux-x64-musl": { "node_modules/@tauri-apps/cli-linux-x64-musl": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.0.4.tgz",
"integrity": "sha512-qi4ghmTfSAl+EEUDwmwI9AJUiOLNSmU1RgiGgcPRE+7A/W+Am9UnxYySAiRbB/gJgTl9sj/pqH5Y9duP1/sqHg==", "integrity": "sha512-9NTk6Pf0bSwXqCBdAA+PDYts9HeHebZzIo8mbRzRyUbER6QngG5HZb9Ka36Z1QWtJjdRy6uxSb4zb/9NuTeHfA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -2837,9 +2837,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-win32-arm64-msvc": { "node_modules/@tauri-apps/cli-win32-arm64-msvc": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.0.4.tgz",
"integrity": "sha512-UXxHkYmFesC97qVmZre4vY7oDxRDtC2OeKNv0bH+iSnuUp/ROxzJYGyaelnv9Ybvgl4YVqDCnxgB28qMM938TA==", "integrity": "sha512-OF2e9oxiBFR8A8wVMOhUx9QGN/I1ZkquWC7gVQBnA56nx9PabJlDT08QBy5UD8USqZFVznnfNr2ehlheQahb3g==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -2854,9 +2854,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-win32-ia32-msvc": { "node_modules/@tauri-apps/cli-win32-ia32-msvc": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.0.4.tgz",
"integrity": "sha512-D+xoaa35RGlkXDpnL5uDTpj29untuC5Wp6bN9snfgFDagD0wnFfC8+2ZQGu16bD0IteWqDI0OSoIXhNvy+F+wg==", "integrity": "sha512-T+hCKB3rFP6q0saHHtR02hm6wr1ZPJ0Mkii3oRTxjPG6BBXoVzHNCYzvdgEGJPTA2sFuAQtJH764NRtNlDMifw==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@@ -2871,9 +2871,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-win32-x64-msvc": { "node_modules/@tauri-apps/cli-win32-x64-msvc": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.0.4.tgz",
"integrity": "sha512-eWV9XWb4dSYHXl13OtYWLjX1JHphUEkHkkGwJrhr8qFBm7RbxXxQvrsUEprSi51ug/dwJenjJgM4zR8By4htfw==", "integrity": "sha512-GVaiI3KWRFLomjJmApHqihhYlkJ+7FqhumhVfBO6Z2tWzZjQyVQgTdNp0kYEuW2WoAYEj0dKY6qd4YM33xYcUA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],

View File

@@ -31,7 +31,7 @@
"tauri-before-dev": "npm run --workspaces --if-present dev" "tauri-before-dev": "npm run --workspaces --if-present dev"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "^2.0.3", "@tauri-apps/cli": "^2.0.4",
"@typescript-eslint/eslint-plugin": "^8.5.0", "@typescript-eslint/eslint-plugin": "^8.5.0",
"@typescript-eslint/parser": "^8.5.0", "@typescript-eslint/parser": "^8.5.0",
"eslint": "^8", "eslint": "^8",

99
src-tauri/Cargo.lock generated
View File

@@ -209,7 +209,7 @@ version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa"
dependencies = [ dependencies = [
"brotli", "brotli 6.0.0",
"flate2", "flate2",
"futures-core", "futures-core",
"memchr", "memchr",
@@ -638,6 +638,17 @@ dependencies = [
"brotli-decompressor", "brotli-decompressor",
] ]
[[package]]
name = "brotli"
version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
"brotli-decompressor",
]
[[package]] [[package]]
name = "brotli-decompressor" name = "brotli-decompressor"
version = "4.0.1" version = "4.0.1"
@@ -2938,7 +2949,19 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc" checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc"
dependencies = [ dependencies = [
"jsonptr", "jsonptr 0.4.7",
"serde",
"serde_json",
"thiserror",
]
[[package]]
name = "json-patch"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08"
dependencies = [
"jsonptr 0.6.3",
"serde", "serde",
"serde_json", "serde_json",
"thiserror", "thiserror",
@@ -2955,6 +2978,16 @@ dependencies = [
"serde_json", "serde_json",
] ]
[[package]]
name = "jsonptr"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70"
dependencies = [
"serde",
"serde_json",
]
[[package]] [[package]]
name = "keyboard-types" name = "keyboard-types"
version = "0.7.0" version = "0.7.0"
@@ -4767,9 +4800,9 @@ dependencies = [
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.10.6" version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@@ -4779,9 +4812,9 @@ dependencies = [
[[package]] [[package]]
name = "regex-automata" name = "regex-automata"
version = "0.4.7" version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@@ -4790,9 +4823,9 @@ dependencies = [
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.8.4" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]] [[package]]
name = "rend" name = "rend"
@@ -6095,9 +6128,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]] [[package]]
name = "tauri" name = "tauri"
version = "2.0.4" version = "2.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44438500b50708bfc1e6083844e135d1b516325aae58710dcd8fb67e050ae87c" checksum = "d3889b392db6d32a105d3757230ea0220090b8f94c90d3e60b6c5eb91178ab1b"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@@ -6146,16 +6179,16 @@ dependencies = [
[[package]] [[package]]
name = "tauri-build" name = "tauri-build"
version = "2.0.1" version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "935f9b3c49b22b3e2e485a57f46d61cd1ae07b1cbb2ba87387a387caf2d8c4e7" checksum = "9f96827ccfb1aa40d55d0ded79562d18ba18566657a553f992a982d755148376"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cargo_toml", "cargo_toml",
"dirs", "dirs",
"glob", "glob",
"heck 0.5.0", "heck 0.5.0",
"json-patch", "json-patch 3.0.1",
"schemars", "schemars",
"semver", "semver",
"serde", "serde",
@@ -6168,14 +6201,14 @@ dependencies = [
[[package]] [[package]]
name = "tauri-codegen" name = "tauri-codegen"
version = "2.0.1" version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95d7443dd4f0b597704b6a14b964ee2ed16e99928d8e6292ae9825f09fbcd30e" checksum = "8947f16f47becd9e9cd39b74ee337fd1981574d78819be18e4384d85e5a0b82f"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"brotli", "brotli 7.0.0",
"ico", "ico",
"json-patch", "json-patch 2.0.0",
"plist", "plist",
"png", "png",
"proc-macro2", "proc-macro2",
@@ -6195,9 +6228,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-macros" name = "tauri-macros"
version = "2.0.1" version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d2c0963ccfc3f5194415f2cce7acc975942a8797fbabfb0aa1ed6f59326ae7f" checksum = "8bd1c8d4a66799d3438747c3a79705cd665a95d6f24cb5f315413ff7a981fe2a"
dependencies = [ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
@@ -6242,9 +6275,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-dialog" name = "tauri-plugin-dialog"
version = "2.0.1" version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddb2fe88b602461c118722c574e2775ab26a4e68886680583874b2f6520608b7" checksum = "4307310e1d2c09ab110235834722e7c2b85099b683e1eb7342ab351b0be5ada3"
dependencies = [ dependencies = [
"log", "log",
"raw-window-handle", "raw-window-handle",
@@ -6260,9 +6293,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-fs" name = "tauri-plugin-fs"
version = "2.0.1" version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab300488ebec3487ca5f56289692e7e45feb07eea8d5e1dba497f7dc9dd9c407" checksum = "96ba7d46e86db8c830d143ef90ab5a453328365b0cc834c24edea4267b16aba0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"dunce", "dunce",
@@ -6321,9 +6354,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-shell" name = "tauri-plugin-shell"
version = "2.0.1" version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "371fb9aca2823990a2d0db7970573be5fdf07881fcaa2b835b29631feb84aec1" checksum = "0ad7880c5586b6b2104be451e3d7fc0f3800c84bda69e9ba81c828f87cb34267"
dependencies = [ dependencies = [
"encoding_rs", "encoding_rs",
"log", "log",
@@ -6387,9 +6420,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-runtime" name = "tauri-runtime"
version = "2.1.0" version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8f437293d6f5e5dce829250f4dbdce4e0b52905e297a6689cc2963eb53ac728" checksum = "a1ef7363e7229ac8d04e8a5d405670dbd43dde8fc4bc3bc56105c35452d03784"
dependencies = [ dependencies = [
"dpi", "dpi",
"gtk", "gtk",
@@ -6406,9 +6439,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-runtime-wry" name = "tauri-runtime-wry"
version = "2.1.1" version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1431602bcc71f2f840ad623915c9842ecc32999b867c4a787d975a17a9625cc6" checksum = "62fa2068e8498ad007b54d5773d03d57c3ff6dd96f8c8ce58beff44d0d5e0d30"
dependencies = [ dependencies = [
"gtk", "gtk",
"http 1.1.0", "http 1.1.0",
@@ -6432,18 +6465,18 @@ dependencies = [
[[package]] [[package]]
name = "tauri-utils" name = "tauri-utils"
version = "2.0.1" version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c38b0230d6880cf6dd07b6d7dd7789a0869f98ac12146e0d18d1c1049215a045" checksum = "1fc65d6f5c54e56b66258948a6d9e47a82ea41f4b5a7612bfbdd1634c2913ed0"
dependencies = [ dependencies = [
"brotli", "brotli 7.0.0",
"cargo_metadata", "cargo_metadata",
"ctor", "ctor",
"dunce", "dunce",
"glob", "glob",
"html5ever", "html5ever",
"infer", "infer",
"json-patch", "json-patch 2.0.0",
"kuchikiki", "kuchikiki",
"log", "log",
"memchr", "memchr",

View File

@@ -16,7 +16,7 @@ crate-type = ["staticlib", "cdylib", "lib"]
strip = true # Automatically strip symbols from the binary. strip = true # Automatically strip symbols from the binary.
[build-dependencies] [build-dependencies]
tauri-build = { version = "2.0.1", features = [] } tauri-build = { version = "2.0.2", features = [] }
[target.'cfg(target_os = "macos")'.dependencies] [target.'cfg(target_os = "macos")'.dependencies]
objc = "0.2.7" objc = "0.2.7"
@@ -48,8 +48,8 @@ serde_yaml = "0.9.34"
tauri = { workspace = true } tauri = { workspace = true }
tauri-plugin-shell = { workspace = true } tauri-plugin-shell = { workspace = true }
tauri-plugin-clipboard-manager = "2.0.1" tauri-plugin-clipboard-manager = "2.0.1"
tauri-plugin-dialog = "2.0.1" tauri-plugin-dialog = "2.0.3"
tauri-plugin-fs = "2.0.1" tauri-plugin-fs = "2.0.3"
tauri-plugin-log = { version = "2.0.1", features = ["colored"] } tauri-plugin-log = { version = "2.0.1", features = ["colored"] }
tauri-plugin-os = "2.0.1" tauri-plugin-os = "2.0.1"
tauri-plugin-updater = "2.0.2" tauri-plugin-updater = "2.0.2"
@@ -65,5 +65,5 @@ eventsource-client = { git = "https://github.com/yaakapp/rust-eventsource-client
[workspace.dependencies] [workspace.dependencies]
yaak_models = { path = "yaak_models" } yaak_models = { path = "yaak_models" }
yaak_plugin_runtime = { path = "yaak_plugin_runtime" } yaak_plugin_runtime = { path = "yaak_plugin_runtime" }
tauri-plugin-shell = "2.0.1" tauri-plugin-shell = "2.0.2"
tauri = { version = "2.0.4", features = ["devtools", "protocol-asset"] } tauri = { version = "2.0.6", features = ["devtools", "protocol-asset"] }

View File

@@ -37,7 +37,7 @@
], ],
"definitions": { "definitions": {
"Capability": { "Capability": {
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, \"platforms\": [\"macOS\",\"windows\"] } ```", "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```",
"type": "object", "type": "object",
"required": [ "required": [
"identifier", "identifier",

View File

@@ -37,7 +37,7 @@
], ],
"definitions": { "definitions": {
"Capability": { "Capability": {
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, \"platforms\": [\"macOS\",\"windows\"] } ```", "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```",
"type": "object", "type": "object",
"required": [ "required": [
"identifier", "identifier",

View File

@@ -75,6 +75,7 @@ use yaak_plugin_runtime::events::{
use yaak_plugin_runtime::plugin_handle::PluginHandle; use yaak_plugin_runtime::plugin_handle::PluginHandle;
use yaak_sse::sse::ServerSentEvent; use yaak_sse::sse::ServerSentEvent;
use yaak_templates::{Parser, Tokens}; use yaak_templates::{Parser, Tokens};
use yaak_templates::format::format_json;
mod analytics; mod analytics;
mod export_resources; mod export_resources;
@@ -737,6 +738,11 @@ async fn cmd_send_ephemeral_request(
send_http_request(&window, &request, &response, environment, cookie_jar, &mut cancel_rx).await send_http_request(&window, &request, &response, environment, cookie_jar, &mut cancel_rx).await
} }
#[tauri::command]
async fn cmd_format_json(text: &str) -> Result<String, String> {
Ok(format_json(text, " "))
}
#[tauri::command] #[tauri::command]
async fn cmd_filter_response<R: Runtime>( async fn cmd_filter_response<R: Runtime>(
window: WebviewWindow<R>, window: WebviewWindow<R>,
@@ -1711,12 +1717,10 @@ pub fn run() {
cmd_create_folder, cmd_create_folder,
cmd_create_grpc_request, cmd_create_grpc_request,
cmd_create_http_request, cmd_create_http_request,
cmd_install_plugin,
cmd_create_workspace, cmd_create_workspace,
cmd_curl_to_request, cmd_curl_to_request,
cmd_delete_all_grpc_connections, cmd_delete_all_grpc_connections,
cmd_delete_all_http_responses, cmd_delete_all_http_responses,
cmd_delete_send_history,
cmd_delete_cookie_jar, cmd_delete_cookie_jar,
cmd_delete_environment, cmd_delete_environment,
cmd_delete_folder, cmd_delete_folder,
@@ -1724,26 +1728,28 @@ pub fn run() {
cmd_delete_grpc_request, cmd_delete_grpc_request,
cmd_delete_http_request, cmd_delete_http_request,
cmd_delete_http_response, cmd_delete_http_response,
cmd_uninstall_plugin, cmd_delete_send_history,
cmd_delete_workspace, cmd_delete_workspace,
cmd_dismiss_notification, cmd_dismiss_notification,
cmd_duplicate_grpc_request, cmd_duplicate_grpc_request,
cmd_duplicate_http_request, cmd_duplicate_http_request,
cmd_export_data, cmd_export_data,
cmd_filter_response, cmd_filter_response,
cmd_format_json,
cmd_get_cookie_jar, cmd_get_cookie_jar,
cmd_get_environment, cmd_get_environment,
cmd_get_folder, cmd_get_folder,
cmd_get_grpc_request, cmd_get_grpc_request,
cmd_get_http_request, cmd_get_http_request,
cmd_get_sse_events,
cmd_get_key_value, cmd_get_key_value,
cmd_get_settings, cmd_get_settings,
cmd_get_sse_events,
cmd_get_workspace, cmd_get_workspace,
cmd_grpc_go, cmd_grpc_go,
cmd_grpc_reflect, cmd_grpc_reflect,
cmd_http_request_actions, cmd_http_request_actions,
cmd_import_data, cmd_import_data,
cmd_install_plugin,
cmd_list_cookie_jars, cmd_list_cookie_jars,
cmd_list_environments, cmd_list_environments,
cmd_list_folders, cmd_list_folders,
@@ -1769,6 +1775,7 @@ pub fn run() {
cmd_template_functions, cmd_template_functions,
cmd_template_tokens_to_string, cmd_template_tokens_to_string,
cmd_track_event, cmd_track_event,
cmd_uninstall_plugin,
cmd_update_cookie_jar, cmd_update_cookie_jar,
cmd_update_environment, cmd_update_environment,
cmd_update_folder, cmd_update_folder,

View File

@@ -0,0 +1,230 @@
enum FormatState {
TemplateTag,
String,
None,
}
/// Formats JSON that might contain template tags (skipped entirely)
pub fn format_json(text: &str, tab: &str) -> String {
let mut chars = text.chars().peekable();
let mut new_json = "".to_string();
let mut depth = 0;
let mut state = FormatState::None;
loop {
let rest_of_chars = chars.clone();
let current_char = match chars.next() {
None => break,
Some(c) => c,
};
// Handle JSON string states
if let FormatState::String = state {
match current_char {
'"' => {
state = FormatState::None;
new_json.push(current_char);
continue;
}
'\\' => {
new_json.push(current_char);
if let Some(c) = chars.next() {
new_json.push(c);
}
continue;
}
_ => {
new_json.push(current_char);
continue;
}
}
}
// Close Template tag states
if let FormatState::TemplateTag = state {
if rest_of_chars.take(2).collect::<String>() == "]}" {
state = FormatState::None;
new_json.push_str("]}");
chars.next(); // Skip the second closing bracket
continue;
} else {
new_json.push(current_char);
continue;
}
}
if rest_of_chars.take(3).collect::<String>() == "${[" {
state = FormatState::TemplateTag;
new_json.push_str("${[");
chars.next(); // Skip {
chars.next(); // Skip [
continue;
}
match current_char {
',' => {
new_json.push(current_char);
new_json.push('\n');
new_json.push_str(tab.to_string().repeat(depth).as_str());
}
'{' => match chars.peek() {
Some('}') => {
new_json.push(current_char);
new_json.push('}');
}
_ => {
depth += 1;
new_json.push(current_char);
new_json.push('\n');
new_json.push_str(tab.to_string().repeat(depth).as_str());
}
},
'[' => match chars.peek() {
Some(']') => {
new_json.push(current_char);
new_json.push(']');
}
_ => {
depth += 1;
new_json.push(current_char);
new_json.push('\n');
new_json.push_str(tab.to_string().repeat(depth).as_str());
}
},
'}' => {
depth -= 1;
new_json.push('\n');
new_json.push_str(tab.to_string().repeat(depth).as_str());
new_json.push(current_char);
// Pad with space if the next char is not a comma
if let Some(',') = chars.peek() {
new_json.push(' ');
}
}
']' => {
depth -= 1;
new_json.push('\n');
new_json.push_str(tab.to_string().repeat(depth).as_str());
new_json.push(current_char);
// Pad with space if the next char is not a comma
if let Some(',') = chars.peek() {
new_json.push(' ');
}
}
':' => {
new_json.push(current_char);
new_json.push(' '); // Pad with space
}
'"' => {
state = FormatState::String;
new_json.push(current_char);
}
_ => {
if current_char == ' '
|| current_char == '\n'
|| current_char == '\t'
|| current_char == '\r'
{
// Don't add these
} else {
new_json.push(current_char);
}
}
}
}
// Replace only lines containing whitespace with nothing
new_json
.lines()
.filter(|line| !line.trim().is_empty()) // Filter out whitespace-only lines
.collect::<Vec<&str>>() // Collect the non-empty lines into a vector
.join("\n") // Join the lines back into a single string
}
#[cfg(test)]
mod test {
use crate::format::format_json;
#[test]
fn test_simple_object() {
assert_eq!(
format_json(r#"{"foo":"bar","baz":"qux"}"#, " "),
r#"
{
"foo": "bar",
"baz": "qux"
}
"#
.trim()
);
}
#[test]
fn test_simple_array() {
assert_eq!(
format_json(r#"["foo","bar","baz","qux"]"#, " "),
r#"
[
"foo",
"bar",
"baz",
"qux"
]
"#
.trim()
);
}
#[test]
fn test_extra_whitespace() {
assert_eq!(
format_json(
r#"["foo", "bar", "baz","qux"
]"#,
" "
),
r#"
[
"foo",
"bar",
"baz",
"qux"
]
"#
.trim()
);
}
#[test]
fn test_invalid_json() {
assert_eq!(
format_json(r#"["foo", {"bar", }"baz",["qux" ]]"#, " "),
r#"
[
"foo",
{
"bar",
}"baz",
[
"qux"
]
]
"#
.trim()
);
}
#[test]
fn test_skip_template_tags() {
assert_eq!(
format_json(r#"{"foo":${[ fn("hello", "world") ]} }"#, " "),
r#"
{
"foo": ${[ fn("hello", "world") ]}
}
"#
.trim()
);
}
}

View File

@@ -1,5 +1,6 @@
pub mod parser; pub mod parser;
pub mod renderer; pub mod renderer;
pub mod format;
pub use parser::*; pub use parser::*;
pub use renderer::*; pub use renderer::*;

View File

@@ -61,7 +61,7 @@ export interface EditorProps {
onKeyDown?: (e: KeyboardEvent) => void; onKeyDown?: (e: KeyboardEvent) => void;
singleLine?: boolean; singleLine?: boolean;
wrapLines?: boolean; wrapLines?: boolean;
format?: (v: string) => string; format?: (v: string) => Promise<string>;
autocomplete?: GenericCompletionConfig; autocomplete?: GenericCompletionConfig;
autocompleteVariables?: boolean; autocompleteVariables?: boolean;
extraExtensions?: Extension[]; extraExtensions?: Extension[];
@@ -387,10 +387,10 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
icon="magic_wand" icon="magic_wand"
variant="border" variant="border"
className={classNames(actionClassName)} className={classNames(actionClassName)}
onClick={() => { onClick={async () => {
if (cm.current === null) return; if (cm.current === null) return;
const { doc } = cm.current.view.state; const { doc } = cm.current.view.state;
const formatted = format(doc.toString()); const formatted = await format(doc.toString());
// Update editor and blur because the cursor will reset anyway // Update editor and blur because the cursor will reset anyway
cm.current.view.dispatch({ cm.current.view.dispatch({
changes: { from: 0, to: doc.length, insert: formatted }, changes: { from: 0, to: doc.length, insert: formatted },

View File

@@ -4,10 +4,11 @@ import type { ServerSentEvent } from '@yaakapp-internal/sse';
import classNames from 'classnames'; import classNames from 'classnames';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import React, { Fragment, useMemo, useRef, useState } from 'react'; import React, { Fragment, useMemo, useRef, useState } from 'react';
import { useFormatText } from '../../hooks/useFormatText';
import { useResponseBodyEventSource } from '../../hooks/useResponseBodyEventSource'; import { useResponseBodyEventSource } from '../../hooks/useResponseBodyEventSource';
import { isJSON } from '../../lib/contentType'; import { isJSON } from '../../lib/contentType';
import { tryFormatJson } from '../../lib/formatters';
import { Button } from '../core/Button'; import { Button } from '../core/Button';
import type { EditorProps } from '../core/Editor';
import { Editor } from '../core/Editor'; import { Editor } from '../core/Editor';
import { Icon } from '../core/Icon'; import { Icon } from '../core/Icon';
import { InlineCode } from '../core/InlineCode'; import { InlineCode } from '../core/InlineCode';
@@ -95,11 +96,7 @@ function ActualEventStreamViewer({ response }: Props) {
</div> </div>
</VStack> </VStack>
) : ( ) : (
<Editor <FormattedEditor language={language} text={activeEvent.data} />
readOnly
defaultValue={tryFormatJson(activeEvent.data)}
language={language}
/>
)} )}
</div> </div>
</div> </div>
@@ -110,6 +107,12 @@ function ActualEventStreamViewer({ response }: Props) {
); );
} }
function FormattedEditor({ text, language }: { text: string; language: EditorProps['language'] }) {
const formatted = useFormatText({ text, language, pretty: true });
if (formatted.data == null) return null;
return <Editor readOnly defaultValue={formatted.data} language={language} />;
}
function EventStreamEventsVirtual({ function EventStreamEventsVirtual({
events, events,
activeEventIndex, activeEventIndex,

View File

@@ -31,7 +31,7 @@ export function HTMLOrTextViewer({ response, pretty, textViewerClassName }: Prop
} }
if (language === 'html' && pretty) { if (language === 'html' && pretty) {
return <WebPageViewer response={response} />; return <WebPageViewer response={response}/>;
} else { } else {
return ( return (
<TextViewer <TextViewer

View File

@@ -5,8 +5,8 @@ import { createGlobalState } from 'react-use';
import { useCopy } from '../../hooks/useCopy'; import { useCopy } from '../../hooks/useCopy';
import { useDebouncedValue } from '../../hooks/useDebouncedValue'; import { useDebouncedValue } from '../../hooks/useDebouncedValue';
import { useFilterResponse } from '../../hooks/useFilterResponse'; import { useFilterResponse } from '../../hooks/useFilterResponse';
import { useFormatText } from '../../hooks/useFormatText';
import { useToggle } from '../../hooks/useToggle'; import { useToggle } from '../../hooks/useToggle';
import { tryFormatJson, tryFormatXml } from '../../lib/formatters';
import { CopyButton } from '../CopyButton'; import { CopyButton } from '../CopyButton';
import { Banner } from '../core/Banner'; import { Banner } from '../core/Banner';
import { Button } from '../core/Button'; import { Button } from '../core/Button';
@@ -117,6 +117,8 @@ export function TextViewer({
toggleSearch, toggleSearch,
]); ]);
const formattedBody = useFormatText({ text, language, pretty });
if (!showLargeResponse && text.length > LARGE_RESPONSE_BYTES) { if (!showLargeResponse && text.length > LARGE_RESPONSE_BYTES) {
return ( return (
<Banner color="primary" className="h-full flex flex-col gap-3"> <Banner color="primary" className="h-full flex flex-col gap-3">
@@ -140,12 +142,9 @@ export function TextViewer({
); );
} }
const formattedBody = if (formattedBody.isFetching) {
pretty && language === 'json' return null;
? tryFormatJson(text) }
: pretty && (language === 'xml' || language === 'html')
? tryFormatXml(text)
: text;
let body; let body;
if (isSearching && filterText?.length > 0) { if (isSearching && filterText?.length > 0) {
@@ -155,7 +154,7 @@ export function TextViewer({
body = filteredResponse.data != null ? filteredResponse.data : ''; body = filteredResponse.data != null ? filteredResponse.data : '';
} }
} else { } else {
body = formattedBody; body = formattedBody.data;
} }
return ( return (

View File

@@ -0,0 +1,28 @@
import { useQuery } from '@tanstack/react-query';
import type { EditorProps } from '../components/core/Editor';
import { tryFormatJson, tryFormatXml } from '../lib/formatters';
export function useFormatText({
text,
language,
pretty,
}: {
text: string;
language: EditorProps['language'];
pretty: boolean;
}) {
return useQuery({
queryKey: [text],
queryFn: async () => {
if (text === '' || !pretty) {
return text;
} else if (language === 'json') {
return tryFormatJson(text);
} else if (language === 'xml' || language === 'html') {
return tryFormatXml(text);
} else {
return text;
}
},
});
}

View File

@@ -23,6 +23,7 @@ export function useSyncWorkspaceChildModels() {
const workspaceId = workspace?.id ?? 'n/a'; const workspaceId = workspace?.id ?? 'n/a';
useEffect(() => { useEffect(() => {
(async function () { (async function () {
console.log('Syncing model stores', { workspaceId });
// Set the things we need first, first // Set the things we need first, first
setHttpRequests(await invokeCmd('cmd_list_http_requests', { workspaceId })); setHttpRequests(await invokeCmd('cmd_list_http_requests', { workspaceId }));
setGrpcRequests(await invokeCmd('cmd_list_grpc_requests', { workspaceId })); setGrpcRequests(await invokeCmd('cmd_list_grpc_requests', { workspaceId }));

View File

@@ -1,20 +1,14 @@
import xmlFormat from 'xml-formatter'; import xmlFormat from 'xml-formatter';
import { invokeCmd } from './tauri';
const INDENT = ' '; const INDENT = ' ';
export function tryFormatJson(text: string, pretty = true): string { export async function tryFormatJson(text: string): Promise<string> {
if (text === '') return text; if (text === '') return text;
return invokeCmd('cmd_format_json', { text });
try {
if (pretty) return JSON.stringify(JSON.parse(text), null, INDENT);
else return JSON.stringify(JSON.parse(text));
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (err) {
return text;
}
} }
export function tryFormatXml(text: string): string { export async function tryFormatXml(text: string): Promise<string> {
if (text === '') return text; if (text === '') return text;
try { try {

View File

@@ -28,6 +28,7 @@ type TauriCmd =
| 'cmd_duplicate_http_request' | 'cmd_duplicate_http_request'
| 'cmd_export_data' | 'cmd_export_data'
| 'cmd_filter_response' | 'cmd_filter_response'
| 'cmd_format_json'
| 'cmd_get_cookie_jar' | 'cmd_get_cookie_jar'
| 'cmd_get_environment' | 'cmd_get_environment'
| 'cmd_get_folder' | 'cmd_get_folder'