diff --git a/package-lock.json b/package-lock.json
index 82d34f95..ea80eff9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,6 +10,9 @@
"license": "MIT",
"dependencies": {
"@breejs/later": "^4.2.0",
+ "@fastify/rate-limit": "^9.1.0",
+ "@fastify/sensible": "^5.6.0",
+ "@fastify/static": "^7.0.4",
"@influxdata/influxdb-client": "^1.35.0",
"@influxdata/influxdb-client-apis": "^1.35.0",
"axios": "^1.7.4",
@@ -19,6 +22,7 @@
"fastify-healthcheck": "^4.4.0",
"fastify-metrics": "^11.0.0",
"fs-extra": "^11.2.0",
+ "handlebars": "^4.7.7",
"influx": "^5.9.3",
"js-yaml": "^4.1.0",
"lodash.clonedeep": "^4.5.0",
@@ -600,6 +604,15 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
+ "node_modules/@fastify/accept-negotiator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-1.1.0.tgz",
+ "integrity": "sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/@fastify/ajv-compiler": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.5.0.tgz",
@@ -648,6 +661,59 @@
"fast-json-stringify": "^5.7.0"
}
},
+ "node_modules/@fastify/rate-limit": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/@fastify/rate-limit/-/rate-limit-9.1.0.tgz",
+ "integrity": "sha512-h5dZWCkuZXN0PxwqaFQLxeln8/LNwQwH9popywmDCFdKfgpi4b/HoMH1lluy6P+30CG9yzzpSpwTCIPNB9T1JA==",
+ "license": "MIT",
+ "dependencies": {
+ "@lukeed/ms": "^2.0.1",
+ "fastify-plugin": "^4.0.0",
+ "toad-cache": "^3.3.1"
+ }
+ },
+ "node_modules/@fastify/send": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@fastify/send/-/send-2.1.0.tgz",
+ "integrity": "sha512-yNYiY6sDkexoJR0D8IDy3aRP3+L4wdqCpvx5WP+VtEU58sn7USmKynBzDQex5X42Zzvw2gNzzYgP90UfWShLFA==",
+ "license": "MIT",
+ "dependencies": {
+ "@lukeed/ms": "^2.0.1",
+ "escape-html": "~1.0.3",
+ "fast-decode-uri-component": "^1.0.1",
+ "http-errors": "2.0.0",
+ "mime": "^3.0.0"
+ }
+ },
+ "node_modules/@fastify/sensible": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/@fastify/sensible/-/sensible-5.6.0.tgz",
+ "integrity": "sha512-Vq6Z2ZQy10GDqON+hvLF52K99s9et5gVVxTul5n3SIAf0Kq5QjPRUKkAMT3zPAiiGvoHtS3APa/3uaxfDgCODQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@lukeed/ms": "^2.0.1",
+ "fast-deep-equal": "^3.1.1",
+ "fastify-plugin": "^4.0.0",
+ "forwarded": "^0.2.0",
+ "http-errors": "^2.0.0",
+ "type-is": "^1.6.18",
+ "vary": "^1.1.2"
+ }
+ },
+ "node_modules/@fastify/static": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/@fastify/static/-/static-7.0.4.tgz",
+ "integrity": "sha512-p2uKtaf8BMOZWLs6wu+Ihg7bWNBdjNgCwDza4MJtTqg+5ovKmcbgbR9Xs5/smZ1YISfzKOCNYmZV8LaCj+eJ1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@fastify/accept-negotiator": "^1.0.0",
+ "@fastify/send": "^2.0.0",
+ "content-disposition": "^0.5.3",
+ "fastify-plugin": "^4.0.0",
+ "fastq": "^1.17.0",
+ "glob": "^10.3.4"
+ }
+ },
"node_modules/@fastify/under-pressure": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/@fastify/under-pressure/-/under-pressure-8.3.0.tgz",
@@ -699,6 +765,82 @@
"@influxdata/influxdb-client": "*"
}
},
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "/~https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "license": "MIT"
+ },
+ "node_modules/@isaacs/cliui/node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "/~https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "/~https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@lukeed/ms": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz",
+ "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -742,6 +884,16 @@
"node": ">=8.0.0"
}
},
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/@pkgr/core": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz",
@@ -981,7 +1133,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -990,7 +1141,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -1063,8 +1213,7 @@
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/base64-js": {
"version": "1.5.1",
@@ -1191,7 +1340,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -1307,6 +1455,18 @@
"node": ">= 10.0.0"
}
},
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
@@ -1319,7 +1479,6 @@
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
- "dev": true,
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -1375,17 +1534,31 @@
"node": ">=0.4.0"
}
},
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/detect-node": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
"integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
"dev": true
},
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "license": "MIT"
+ },
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/enabled": {
"version": "2.0.0",
@@ -1438,6 +1611,12 @@
"@esbuild/win32-x64": "0.23.1"
}
},
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "license": "MIT"
+ },
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -2005,6 +2184,22 @@
}
}
},
+ "node_modules/foreground-child": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
+ "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.0",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "/~https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
@@ -2063,6 +2258,26 @@
"url": "/~https://github.com/sponsors/ljharb"
}
},
+ "node_modules/glob": {
+ "version": "10.4.5",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
+ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "/~https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -2075,6 +2290,30 @@
"node": ">=10.13.0"
}
},
+ "node_modules/glob/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "/~https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/global-agent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz",
@@ -2152,6 +2391,27 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
},
+ "node_modules/handlebars": {
+ "version": "4.7.8",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
+ "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.5",
+ "neo-async": "^2.6.2",
+ "source-map": "^0.6.1",
+ "wordwrap": "^1.0.0"
+ },
+ "bin": {
+ "handlebars": "bin/handlebars"
+ },
+ "engines": {
+ "node": ">=0.4.7"
+ },
+ "optionalDependencies": {
+ "uglify-js": "^3.1.4"
+ }
+ },
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@@ -2215,6 +2475,22 @@
"integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==",
"license": "MIT"
},
+ "node_modules/http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
@@ -2319,7 +2595,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -2348,8 +2623,22 @@
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
+ },
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "/~https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
},
"node_modules/js-sdsl": {
"version": "4.3.0",
@@ -2540,6 +2829,27 @@
"node": ">=10"
}
},
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
+ "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@@ -2579,6 +2889,15 @@
"url": "/~https://github.com/sponsors/ljharb"
}
},
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
"node_modules/moment": {
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
@@ -2641,6 +2960,12 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true
},
+ "node_modules/neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+ "license": "MIT"
+ },
"node_modules/number-allocator": {
"version": "1.0.14",
"resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz",
@@ -2731,6 +3056,12 @@
"url": "/~https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
+ "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==",
+ "license": "BlueOak-1.0.0"
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -2757,11 +3088,26 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true,
"engines": {
"node": ">=8"
}
},
+ "node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "/~https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/pg": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.12.0.tgz",
@@ -3259,11 +3605,16 @@
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz",
"integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ=="
},
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "license": "ISC"
+ },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
"dependencies": {
"shebang-regex": "^3.0.0"
},
@@ -3275,11 +3626,22 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true,
"engines": {
"node": ">=8"
}
},
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "/~https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
@@ -3331,6 +3693,15 @@
"atomic-sleep": "^1.0.0"
}
},
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/split2": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
@@ -3353,6 +3724,15 @@
"node": "*"
}
},
+ "node_modules/statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
@@ -3365,7 +3745,21 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -3379,7 +3773,19 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
@@ -3521,13 +3927,23 @@
}
},
"node_modules/toad-cache": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.3.0.tgz",
- "integrity": "sha512-3oDzcogWGHZdkwrHyvJVpPjA7oNzY6ENOV3PsWJY9XYPZ6INo94Yd47s5may1U+nleBPwDhrRiTPMIvKaa3MQg==",
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz",
+ "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==",
+ "license": "MIT",
"engines": {
"node": ">=12"
}
},
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
"node_modules/triple-beam": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
@@ -3553,6 +3969,19 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "license": "MIT",
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
@@ -3581,6 +4010,19 @@
"node": "*"
}
},
+ "node_modules/uglify-js": {
+ "version": "3.19.2",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.2.tgz",
+ "integrity": "sha512-S8KA6DDI47nQXJSi2ctQ629YzwOVs+bQML6DAtvy0wgNdpi+0ySpQK0g2pxBq2xfF2z3YCscu7NNA8nXT9PlIQ==",
+ "license": "BSD-2-Clause",
+ "optional": true,
+ "bin": {
+ "uglifyjs": "bin/uglifyjs"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
"node_modules/undici-types": {
"version": "6.18.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.18.2.tgz",
@@ -3621,11 +4063,19 @@
"uuid": "dist/bin/uuid"
}
},
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
"dependencies": {
"isexe": "^2.0.0"
},
@@ -3725,6 +4175,12 @@
"node": ">= 6"
}
},
+ "node_modules/wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
+ "license": "MIT"
+ },
"node_modules/worker-timers": {
"version": "7.1.8",
"resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-7.1.8.tgz",
@@ -3759,6 +4215,103 @@
"tslib": "^2.6.2"
}
},
+ "node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "/~https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "/~https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "/~https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "/~https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi/node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "/~https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "/~https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
"node_modules/ws": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
diff --git a/package.json b/package.json
index 7a170459..e0d6579a 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,9 @@
"type": "module",
"dependencies": {
"@breejs/later": "^4.2.0",
+ "@fastify/rate-limit": "^9.1.0",
+ "@fastify/sensible": "^5.6.0",
+ "@fastify/static": "^7.0.4",
"@influxdata/influxdb-client": "^1.35.0",
"@influxdata/influxdb-client-apis": "^1.35.0",
"axios": "^1.7.4",
@@ -40,6 +43,7 @@
"fastify-healthcheck": "^4.4.0",
"fastify-metrics": "^11.0.0",
"fs-extra": "^11.2.0",
+ "handlebars": "^4.7.7",
"influx": "^5.9.3",
"js-yaml": "^4.1.0",
"lodash.clonedeep": "^4.5.0",
diff --git a/src/butler-sos.js b/src/butler-sos.js
index 03c95634..70b2aaa5 100755
--- a/src/butler-sos.js
+++ b/src/butler-sos.js
@@ -24,6 +24,7 @@ import { udpInitLogEventServer } from './lib/udp_handlers_log_events.js';
import { setupAnonUsageReportTimer } from './lib/telemetry.js';
import { setupPromClient } from './lib/prom-client.js';
import { verifyConfigFile } from './lib/config-file-verify.js';
+import { setupConfigVisServer } from './lib/config-visualise.js';
// Suppress experimental warnings
// https://stackoverflow.com/questions/55778283/how-to-disable-warnings-when-node-is-launched-via-a-global-shell-script
@@ -279,6 +280,11 @@ async function mainScript() {
if (globals.config.get('Butler-SOS.appNames.enableAppNameExtract') === true) {
setupAppNamesExtractTimer();
}
+
+ // Set up config server, if enabled
+ if (globals.config.get('Butler-SOS.configVisualisation.enable') === true) {
+ await setupConfigVisServer();
+ }
}
mainScript();
diff --git a/src/config/production_template.yaml b/src/config/production_template.yaml
index 3f79675b..82e67130 100644
--- a/src/config/production_template.yaml
+++ b/src/config/production_template.yaml
@@ -10,6 +10,13 @@ Butler-SOS:
# More info on whata data is collected: https://butler-sos.ptarmiganlabs.com/docs/about/telemetry/
# Please consider leaving this at true - it really helps future development of Butler SOS!
+ # Should Butler SOS start a web server that serves an obfuscated view of the Butler SOS config file?
+ configVisualisation:
+ enable: false
+ host: localhost # Hostname or IP address where the web server will listen. Should be localhost in most cases.
+ port: 3100 # Port where the web server will listen. Change if port 3100 is already in use.
+ obfuscate: true # Should the config file shown in the web UI be obfuscated?
+
# Heartbeats can be used to send "I'm alive" messages to some other tool, e.g. an infrastructure monitoring tool
# The concept is simple: The remoteURL will be called at the specified frequency. The receiving tool will then know
# that Butler SOS is alive.
diff --git a/src/lib/config-file-schema.js b/src/lib/config-file-schema.js
index 778bc0a9..82203cf9 100755
--- a/src/lib/config-file-schema.js
+++ b/src/lib/config-file-schema.js
@@ -4,6 +4,12 @@ export const confifgFileSchema = {
fileLogging: 'boolean',
logDirectory: 'string',
anonTelemetry: 'boolean',
+ configVisualisation: {
+ enable: 'boolean',
+ host: 'string',
+ port: 'number',
+ obfuscate: 'boolean',
+ },
heartbeat: {
enable: 'boolean',
remoteURL: 'string',
diff --git a/src/lib/config-obfuscate.js b/src/lib/config-obfuscate.js
new file mode 100644
index 00000000..fb482801
--- /dev/null
+++ b/src/lib/config-obfuscate.js
@@ -0,0 +1,152 @@
+import globals from '../globals.js';
+
+function configObfuscate(config) {
+ try {
+ const obfuscatedConfig = { ...config };
+
+ // Keep first 10 chars of remote URL, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].heartbeat.remoteURL = obfuscatedConfig['Butler-SOS'].heartbeat.remoteURL.substring(0, 10) + '*'.repeat(10);
+
+ // Update entries in the array obfuscatedConfig['Butler-SOS'].thirdPartyToolsCredentials.newRelic
+ obfuscatedConfig['Butler-SOS'].thirdPartyToolsCredentials.newRelic = obfuscatedConfig['Butler-SOS'].thirdPartyToolsCredentials.newRelic?.map(
+ (element) => ({
+ ...element,
+ insertApiKey: element.insertApiKey.substring(0, 5) + '*'.repeat(10),
+ accountId: element.accountId.toString().substring(0, 3) + '*'.repeat(10),
+ }),
+ );
+
+ // Obfuscate Butler-SOS.iuserEvents.udpServerConfig.serverHost, keep first 3 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].userEvents.udpServerConfig.serverHost =
+ obfuscatedConfig['Butler-SOS'].userEvents.udpServerConfig.serverHost.substring(0, 3) + '*'.repeat(10);
+
+ // Obfuscate Butler-SOS.iuserEvents.sendToMQTT.postTo.everythingTopic.topic, keep first 10 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].userEvents.sendToMQTT.postTo.everythingTopic.topic =
+ obfuscatedConfig['Butler-SOS'].userEvents.sendToMQTT.postTo.everythingTopic.topic.substring(0, 10) + '*'.repeat(10);
+
+ // Obfuscate Butler-SOS.iuserEvents.sendToMQTT.postTo.sessionStartTopic.topic, keep first 10 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].userEvents.sendToMQTT.postTo.sessionStartTopic.topic =
+ obfuscatedConfig['Butler-SOS'].userEvents.sendToMQTT.postTo.sessionStartTopic.topic.substring(0, 10) + '*'.repeat(10);
+
+ // Obfuscate Butler-SOS.iuserEvents.sendToMQTT.postTo.sessionStopTopic.topic, keep first 10 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].userEvents.sendToMQTT.postTo.sessionStopTopic.topic =
+ obfuscatedConfig['Butler-SOS'].userEvents.sendToMQTT.postTo.sessionStopTopic.topic.substring(0, 10) + '*'.repeat(10);
+
+ // Obfuscate Butler-SOS.iuserEvents.sendToMQTT.postTo.connectionOpenTopic.topic, keep first 10 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].userEvents.sendToMQTT.postTo.connectionOpenTopic.topic =
+ obfuscatedConfig['Butler-SOS'].userEvents.sendToMQTT.postTo.connectionOpenTopic.topic.substring(0, 10) + '*'.repeat(10);
+
+ // Obfuscate Butler-SOS.iuserEvents.sendToMQTT.postTo.connectionCloseTopic.topic, keep first 10 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].userEvents.sendToMQTT.postTo.connectionCloseTopic.topic =
+ obfuscatedConfig['Butler-SOS'].userEvents.sendToMQTT.postTo.connectionCloseTopic.topic.substring(0, 10) + '*'.repeat(10);
+
+ // Obfuscate Butler-SOS.logEvents.udpServerConfig.serverHost, keep first 3 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].logEvents.udpServerConfig.serverHost =
+ obfuscatedConfig['Butler-SOS'].logEvents.udpServerConfig.serverHost.substring(0, 3) + '*'.repeat(10);
+
+ // Obfuscate Butler-SOS.logEvents.sendToMQTT.baseTopic, keep first 10 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].logEvents.sendToMQTT.baseTopic =
+ obfuscatedConfig['Butler-SOS'].logEvents.sendToMQTT.baseTopic.substring(0, 10) + '*'.repeat(10);
+
+ // Log db - may not be present in the config in future versions of Butler SOS
+ if (obfuscatedConfig['Butler-SOS'].logdb) {
+ // Obfuscate Butler-SOS.logdb.host, keep first 3 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].logdb.host = obfuscatedConfig['Butler-SOS'].logdb.host.substring(0, 3) + '*'.repeat(10);
+
+ // Obfuscate Butler-SOS.logdb.qlogsReaderUser, keep first 3 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].logdb.qlogsReaderUser = obfuscatedConfig['Butler-SOS'].logdb.qlogsReaderUser.substring(0, 3) + '*'.repeat(10);
+
+ // Obfuscate Butler-SOS.logdb.qlogsReaderPwdd, keep first 0 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].logdb.qlogsReaderPwdd = '*'.repeat(10);
+ }
+
+ // Obfuscate Butler-SOS.cert.clientCert, keep first 10 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].cert.clientCert = obfuscatedConfig['Butler-SOS'].cert.clientCert.substring(0, 10) + '*'.repeat(10);
+
+ // Obfuscate Butler-SOS.cert.clientCertKey, keep first 10 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].cert.clientCertKey = obfuscatedConfig['Butler-SOS'].cert.clientCertKey.substring(0, 10) + '*'.repeat(10);
+
+ // Obfuscate Butler-SOS.cert.clientCertCA, keep first 10 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].cert.clientCertCA = obfuscatedConfig['Butler-SOS'].cert.clientCertCA.substring(0, 10) + '*'.repeat(10);
+
+ // Obfuscate Butler-SOS.cert.clientCertPassphrase, keep first 0 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].cert.clientCertPassphrase = '*'.repeat(10);
+
+ // Obfuscate Butler-SOS.mqttConfig.brokerHost, keep first 3 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].mqttConfig.brokerHost = obfuscatedConfig['Butler-SOS'].mqttConfig.brokerHost.substring(0, 3) + '*'.repeat(10);
+
+
+ // Obfuscate Butler-SOS.prometheus.host, keep first 3 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].prometheus.host = obfuscatedConfig['Butler-SOS'].prometheus.host.substring(0, 3) + '*'.repeat(10);
+
+ // Obfuscate Butler-SOS.influxdbConfig.host, keep first 3 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].influxdbConfig.host = obfuscatedConfig['Butler-SOS'].influxdbConfig.host.substring(0, 3) + '*'.repeat(10);
+
+ // Obfuscate Butler-SOS.influxdbConfig.v2Config.org, keep first 3 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].influxdbConfig.v2Config.org = obfuscatedConfig['Butler-SOS'].influxdbConfig.v2Config.org.substring(0, 3) + '*'.repeat(10);
+
+ // Obfuscate Butler-SOS.influxdbConfig.v2Config.bucket, keep first 3 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].influxdbConfig.v2Config.bucket = obfuscatedConfig['Butler-SOS'].influxdbConfig.v2Config.bucket.substring(0, 3) + '*'.repeat(10);
+
+ // Obfuscate Butler-SOS.influxdbConfig.v2Config.token, keep first 0 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].influxdbConfig.v2Config.token = '*'.repeat(10);
+
+ // Obfuscate Butler-SOS.influxdbConfig.v1Config.auth.username, keep first 3 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].influxdbConfig.v1Config.auth.username = obfuscatedConfig['Butler-SOS'].influxdbConfig.v1Config.auth.username.substring(0, 3) + '*'.repeat(10);
+
+ // Obfuscate Butler-SOS.influxdbConfig.v1Config.auth.password, keep first 0 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].influxdbConfig.v1Config.auth.password = '*'.repeat(10);
+
+ // Obfuscate Butler-SOS.appNames.hostIP, keep first 3 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].appNames.hostIP = obfuscatedConfig['Butler-SOS'].appNames.hostIP.substring(0, 3) + '*'.repeat(10);
+
+
+
+ // Obfuscate Butler-SOS.serversToMonitor.servers[].host, keep first 3 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].serversToMonitor.servers = obfuscatedConfig['Butler-SOS'].serversToMonitor.servers?.map((element) => ({
+ ...element,
+ host: element.host.substring(0, 3) + '*'.repeat(10),
+ }));
+
+ // Obfuscate Butler-SOS.serversToMonitor.servers[].logDbHost, keep first 3 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].serversToMonitor.servers = obfuscatedConfig['Butler-SOS'].serversToMonitor.servers?.map((element) => ({
+ ...element,
+ logDbHost: element.logDbHost.substring(0, 3) + '*'.repeat(10),
+ }));
+
+ // Obfuscate Butler-SOS.serversToMonitor.servers[].userSessions.host, keep first 3 chars, mask the rest with *
+ obfuscatedConfig['Butler-SOS'].serversToMonitor.servers = obfuscatedConfig['Butler-SOS'].serversToMonitor.servers?.map((element) => ({
+ ...element,
+ userSessions: element.userSessions.host.substring(0, 3) + '*'.repeat(10),
+ }));
+
+ // Obfuscate Butler-SOS.serversToMonitor.servers[].headers, keep first 5 chars, mask the rest with *
+ // Butler-SOS.serversToMonitor.servers[].headers is an object, so we need to obfuscate each key-value pair
+ // If the array Butler-SOS.serversToMonitor.servers[].headers is empty, no obfuscation should be done
+ obfuscatedConfig['Butler-SOS'].serversToMonitor.servers = obfuscatedConfig['Butler-SOS'].serversToMonitor.servers?.map((element) => {
+ const newHeaders = {};
+
+ // Is elemnt.headers an object with more than 0 key-value pairs?
+ if (element?.headers && Object.keys(element?.headers)?.length > 0) {
+ Object.entries(element?.headers).forEach(([key, value]) => {
+ newHeaders[key] = value.substring(0, 5) + '*'.repeat(10);
+ });
+ }
+
+ return {
+ ...element,
+ headers: newHeaders,
+ };
+ });
+
+ return obfuscatedConfig;
+ } catch (err) {
+ globals.logger.error(`CONFIG OBFUSCATE: Error obfuscating config: ${err.message}`);
+ if (err.stack) {
+ globals.logger.error(`CONFIG OBFUSCATE: ${err.stack}`);
+ }
+ throw err;
+ }
+}
+
+export default configObfuscate;
diff --git a/src/lib/config-visualise.js b/src/lib/config-visualise.js
new file mode 100644
index 00000000..2129c1b9
--- /dev/null
+++ b/src/lib/config-visualise.js
@@ -0,0 +1,138 @@
+import Fastify from 'fastify';
+import FastifyRateLimit from '@fastify/rate-limit';
+import FastifyStatic from '@fastify/static';
+import fs from 'fs';
+import path from 'path';
+import yaml from 'js-yaml';
+import handlebars from 'handlebars';
+
+import globals from '../globals.js';
+import configObfuscate from './config-obfuscate.js';
+
+export async function setupConfigVisServer(logger, config) {
+ try {
+ // Register rate limit for API
+ // 0 means no rate limit
+
+ // This code registers the FastifyRateLimit plugin.
+ // The plugin limits the number of API requests that
+ // can be made from a given IP address within a given
+ // time window.
+
+ const configVisServer = Fastify({ logger: true });
+
+ // Set Fastify log level based on log level in Butler config file
+ const currLogLevel = globals.getLoggingLevel();
+ if (currLogLevel === 'debug' || currLogLevel === 'silly') {
+ configVisServer.log.level = 'info';
+ } else {
+ configVisServer.log.level = 'silent';
+ }
+
+ // 30 requests per minute
+ await configVisServer.register(FastifyRateLimit, {
+ max: 300,
+ timeWindow: '1 minute',
+ });
+
+ // Add custom error handler for 429 errors (rate limit exceeded)
+ configVisServer.setErrorHandler((error, request, reply) => {
+ if (error.statusCode === 429) {
+ globals.logger.warn(
+ `CONFIG VIS: Rate limit exceeded for source IP address ${request.ip}. Method=${request.method}, endpoint=${request.url}`,
+ );
+ }
+ reply.send(error);
+ });
+
+ // This loads all plugins defined in plugins.
+ // Those should be support plugins that are reused through your application
+ await configVisServer.register(import('../plugins/sensible.js'), { options: {} });
+ await configVisServer.register(import('../plugins/support.js'), { options: {} });
+
+ // Create absolute path to the html directory
+ // dirname points to the directory where this file (app.js) is located, taking into account
+ // if the app is running as a packaged app or as a Node.js app.
+ globals.logger.verbose(`----------------2: ${globals.appBasePath}`);
+
+ // Get directory contents of dirname
+ const dirContents = fs.readdirSync(globals.appBasePath);
+ globals.logger.verbose(`CONFIG VIS: Directory contents of "${globals.appBasePath}": ${dirContents}`);
+
+
+ const htmlDir = path.resolve(globals.appBasePath, 'static/configvis');
+ globals.logger.info(`CONFIG VIS: Serving static files from ${htmlDir}`);
+
+ await configVisServer.register(FastifyStatic, {
+ root: htmlDir,
+ constraints: {}, // optional: default {}. Example: { host: 'example.com' }
+ redirect: true, // Redirect to trailing '/' when the pathname is a dir
+ });
+
+ configVisServer.get('/', async (request, reply) => {
+ // Obfuscate the config object before sending it to the client
+ // First get clean copy of the config object
+ let newConfig = JSON.parse(JSON.stringify(globals.config));
+
+ if (globals.config.get('Butler-SOS.configVisualisation.obfuscate')) {
+ // Obfuscate config file before presenting it to the user
+ // This is done to avoid leaking sensitive information
+ // to users who should not have access to it.
+ // The obfuscation is done by replacing parts of the
+ // config file with masked strings.
+ newConfig = configObfuscate(newConfig);
+ }
+
+ // Convert the (potentially obfuscated) config object to YAML format (=string)
+ const butlerConfigYaml = yaml.dump(newConfig);
+
+ // Read index.html from disk
+ // dirname points to the directory where this file (app.js) is located, taking into account
+ // if the app is running as a packaged app or as a Node.js app.
+ globals.logger.verbose(`----------------3: ${globals.appBasePath}`);
+ const filePath = path.resolve(globals.appBasePath, 'static/configvis', 'index.html');
+ const template = fs.readFileSync(filePath, 'utf8');
+
+ // Compile handlebars template
+ const compiledTemplate = handlebars.compile(template);
+
+ // Get config as HTML encoded JSON string
+ const butlerConfigJsonEncoded = JSON.stringify(newConfig);
+
+ // Render the template
+ const renderedText = compiledTemplate({ butlerConfigJsonEncoded, butlerConfigYaml });
+
+ globals.logger.debug(`CONFIG VIS: Rendered text: ${renderedText}`);
+
+ // Send reply as HTML
+ reply.code(200).header('Content-Type', 'text/html; charset=utf-8').send(renderedText);
+ });
+
+ configVisServer.listen(
+ {
+ host: globals.config.get('Butler-SOS.configVisualisation.host'),
+ port: globals.config.get('Butler-SOS.configVisualisation.port'),
+ },
+ (err, address) => {
+ if (err) {
+ globals.logger.error(`CONFIG VIS: Could not set up config visualisation server on ${address}`);
+ globals.logger.error(`CONFIG VIS: ${err.stack}`);
+ configVisServer.log.error(err);
+ process.exit(1);
+ }
+ globals.logger.info(`CONFIG VIS: Config visualisation server listening on ${address}`);
+
+ configVisServer.ready((err2) => {
+ if (err2) throw err;
+ });
+ },
+ );
+ } catch (err) {
+ globals.logger.error(`CONFIG VIS: Error setting up config visualisation server: ${err.message}`);
+ if (err.stack) {
+ globals.logger.error(`CONFIG VIS: ${err.stack}`);
+ }
+ throw err;
+ }
+}
+
diff --git a/src/plugins/README.md b/src/plugins/README.md
new file mode 100644
index 00000000..7d8136d6
--- /dev/null
+++ b/src/plugins/README.md
@@ -0,0 +1,16 @@
+# Plugins Folder
+
+Plugins define behavior that is common to all the routes in your
+application. Authentication, caching, templates, and all the other cross
+cutting concerns should be handled by plugins placed in this folder.
+
+Files in this folder are typically defined through the
+[`fastify-plugin`](/~https://github.com/fastify/fastify-plugin) module,
+making them non-encapsulated. They can define decorators and set hooks
+that will then be used in the rest of your application.
+
+Check out:
+
+- [The hitchhiker's guide to plugins](https://www.fastify.io/docs/latest/Plugins-Guide/)
+- [Fastify decorators](https://www.fastify.io/docs/latest/Decorators/).
+- [Fastify lifecycle](https://www.fastify.io/docs/latest/Lifecycle/).
diff --git a/src/plugins/sensible.js b/src/plugins/sensible.js
new file mode 100644
index 00000000..a9535b2f
--- /dev/null
+++ b/src/plugins/sensible.js
@@ -0,0 +1,14 @@
+import fp from 'fastify-plugin';
+
+/**
+ * This plugins adds some utilities to handle http errors
+ *
+ * @see /~https://github.com/fastify/fastify-sensible
+ */
+// eslint-disable-next-line no-unused-vars
+export default fp(async (fastify, _opts) => {
+ // eslint-disable-next-line global-require
+ await fastify.register(import('@fastify/sensible'), {
+ errorHandler: false,
+ });
+});
diff --git a/src/plugins/support.js b/src/plugins/support.js
new file mode 100644
index 00000000..373fca62
--- /dev/null
+++ b/src/plugins/support.js
@@ -0,0 +1,12 @@
+import fp from 'fastify-plugin';
+
+// the use of fastify-plugin is required to be able
+// to export the decorators to the outer scope
+
+// eslint-disable-next-line no-unused-vars
+export default fp(async (fastify, _opts) => {
+ // eslint-disable-next-line arrow-body-style
+ fastify.decorate('someSupport', () => {
+ return 'hugs';
+ });
+});
diff --git a/static/configvis/butler.png b/static/configvis/butler.png
new file mode 100644
index 00000000..5ecc6944
Binary files /dev/null and b/static/configvis/butler.png differ
diff --git a/static/configvis/download-solid.svg b/static/configvis/download-solid.svg
new file mode 100644
index 00000000..34fdf86d
--- /dev/null
+++ b/static/configvis/download-solid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/static/configvis/index.html b/static/configvis/index.html
new file mode 100644
index 00000000..7aba1a49
--- /dev/null
+++ b/static/configvis/index.html
@@ -0,0 +1,245 @@
+
+
+
+
+ Ctrl-Q
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
YAML configuration
+
{{butlerConfigYaml}}
+
+
+
+
JSON tree view
+
+ Your HTML.
+
+
+
+
+
+
diff --git a/static/configvis/jsontree.js b/static/configvis/jsontree.js
new file mode 100644
index 00000000..cc09af64
--- /dev/null
+++ b/static/configvis/jsontree.js
@@ -0,0 +1,927 @@
+'use strict';
+
+var Is;
+
+((e) => {
+ function t(e) {
+ return e !== null && e !== void 0 && e.toString() !== '';
+ }
+ e.defined = t;
+ function n(e) {
+ return t(e) && typeof e === 'object';
+ }
+ e.definedObject = n;
+ function r(e) {
+ return t(e) && typeof e === 'boolean';
+ }
+ e.definedBoolean = r;
+ function o(e) {
+ return t(e) && typeof e === 'string';
+ }
+ e.definedString = o;
+ function l(e) {
+ return t(e) && typeof e === 'function';
+ }
+ e.definedFunction = l;
+ function a(e) {
+ return t(e) && typeof e === 'number';
+ }
+ e.definedNumber = a;
+ function i(e) {
+ return n(e) && e instanceof Array;
+ }
+ e.definedArray = i;
+ function s(e) {
+ return n(e) && e instanceof Date;
+ }
+ e.definedDate = s;
+ function u(e) {
+ return t(e) && typeof e === 'number' && e % 1 !== 0;
+ }
+ e.definedDecimal = u;
+ function c(e, t = 1) {
+ return !i(e) || e.length < t;
+ }
+ e.invalidOptionArray = c;
+ function d(e) {
+ let t = e.length >= 2 && e.length <= 7;
+ if (t && e[0] === '#') {
+ t = isNaN(+e.substring(1, e.length - 1));
+ }
+ return t;
+ }
+ e.hexColor = d;
+})(Is || (Is = {}));
+
+var Default;
+
+((e) => {
+ function t(e, t) {
+ return typeof e === 'string' ? e : t;
+ }
+ e.getAnyString = t;
+ function n(e, t) {
+ return Is.definedString(e) ? e : t;
+ }
+ e.getString = n;
+ function r(e, t) {
+ return Is.definedBoolean(e) ? e : t;
+ }
+ e.getBoolean = r;
+ function o(e, t) {
+ return Is.definedNumber(e) ? e : t;
+ }
+ e.getNumber = o;
+ function l(e, t) {
+ return Is.definedFunction(e) ? e : t;
+ }
+ e.getFunction = l;
+ function a(e, t) {
+ return Is.definedArray(e) ? e : t;
+ }
+ e.getArray = a;
+ function i(e, t) {
+ return Is.definedObject(e) ? e : t;
+ }
+ e.getObject = i;
+ function s(e, t) {
+ let n = t;
+ if (Is.definedString(e)) {
+ const r = e.toString().split(' ');
+ if (r.length === 0) {
+ e = t;
+ } else {
+ n = r;
+ }
+ } else {
+ n = a(e, t);
+ }
+ return n;
+ }
+ e.getStringOrArray = s;
+ function u(e, t) {
+ var n;
+ const r = new RegExp(`^-?\\d+(?:.\\d{0,${t || -1}})?`);
+ return ((n = e.toString().match(r)) == null ? void 0 : n[0]) || '';
+ }
+ e.getFixedDecimalPlacesValue = u;
+ function c(e) {
+ let t;
+ const n = e.toString().split('(');
+ const r = n[0].split(' ');
+ if (r.length === 2) {
+ t = r[1];
+ } else {
+ t = r[0];
+ }
+ t += '()';
+ return t;
+ }
+ e.getFunctionName = c;
+})(Default || (Default = {}));
+
+var DomElement;
+
+((e) => {
+ function t(e, t, n = '', r = null) {
+ const o = t.toLowerCase();
+ const l = o === 'text';
+ let a = l ? document.createTextNode('') : document.createElement(o);
+ if (Is.defined(n)) {
+ a.className = n;
+ }
+ if (Is.defined(r)) {
+ e.insertBefore(a, r);
+ } else {
+ e.appendChild(a);
+ }
+ return a;
+ }
+ e.create = t;
+ function n(e, n, r, o, l = null) {
+ const a = t(e, n, r, l);
+ a.innerHTML = o;
+ return a;
+ }
+ e.createWithHTML = n;
+ function r(e, t) {
+ e.classList.add(t);
+ }
+ e.addClass = r;
+})(DomElement || (DomElement = {}));
+
+var Str;
+
+((e) => {
+ function t() {
+ const e = [];
+ for (let t = 0; t < 32; t++) {
+ if (t === 8 || t === 12 || t === 16 || t === 20) {
+ e.push('-');
+ }
+ const n = Math.floor(Math.random() * 16).toString(16);
+ e.push(n);
+ }
+ return e.join('');
+ }
+ e.newGuid = t;
+ function n(e, t = 1) {
+ const n = e.toString();
+ let r = n;
+ if (n.length < t) {
+ const e = t - n.length + 1;
+ r = Array(e).join('0') + n;
+ }
+ return r;
+ }
+ e.padNumber = n;
+})(Str || (Str = {}));
+
+var DateTime;
+
+((e) => {
+ function t(e) {
+ return e.getDay() - 1 < 0 ? 6 : e.getDay() - 1;
+ }
+ e.getWeekdayNumber = t;
+ function n(e, t) {
+ let n = e.text.thText;
+ if (t === 31 || t === 21 || t === 1) {
+ n = e.text.stText;
+ } else if (t === 22 || t === 2) {
+ n = e.text.ndText;
+ } else if (t === 23 || t === 3) {
+ n = e.text.rdText;
+ }
+ return n;
+ }
+ e.getDayOrdinal = n;
+ function r(e, r, o) {
+ let l = o;
+ const a = t(r);
+ l = l.replace('{hh}', Str.padNumber(r.getHours(), 2));
+ l = l.replace('{h}', r.getHours().toString());
+ l = l.replace('{MM}', Str.padNumber(r.getMinutes(), 2));
+ l = l.replace('{M}', r.getMinutes().toString());
+ l = l.replace('{ss}', Str.padNumber(r.getSeconds(), 2));
+ l = l.replace('{s}', r.getSeconds().toString());
+ l = l.replace('{dddd}', e.text.dayNames[a]);
+ l = l.replace('{ddd}', e.text.dayNamesAbbreviated[a]);
+ l = l.replace('{dd}', Str.padNumber(r.getDate()));
+ l = l.replace('{d}', r.getDate().toString());
+ l = l.replace('{o}', n(e, r.getDate()));
+ l = l.replace('{mmmm}', e.text.monthNames[r.getMonth()]);
+ l = l.replace('{mmm}', e.text.monthNamesAbbreviated[r.getMonth()]);
+ l = l.replace('{mm}', Str.padNumber(r.getMonth() + 1));
+ l = l.replace('{m}', (r.getMonth() + 1).toString());
+ l = l.replace('{yyyy}', r.getFullYear().toString());
+ l = l.replace('{yyy}', r.getFullYear().toString().substring(1));
+ l = l.replace('{yy}', r.getFullYear().toString().substring(2));
+ l = l.replace('{y}', Number.parseInt(r.getFullYear().toString().substring(2)).toString());
+ return l;
+ }
+ e.getCustomFormattedDateText = r;
+})(DateTime || (DateTime = {}));
+
+var Constants;
+
+((e) => {
+ e.JSONTREE_JS_ATTRIBUTE_NAME = 'data-jsontree-js';
+})(Constants || (Constants = {}));
+
+var Binding;
+
+((e) => {
+ let t;
+ ((t) => {
+ function n(t, n) {
+ const r = e.Options.get(t);
+ r._currentView = {};
+ r._currentView.element = n;
+ r._currentView.dataArrayCurrentIndex = 0;
+ return r;
+ }
+ t.getForNewInstance = n;
+ function r(e) {
+ let t = Default.getObject(e, {});
+ t.data = Default.getObject(t.data, null);
+ t.showCounts = Default.getBoolean(t.showCounts, true);
+ t.useZeroIndexingForArrays = Default.getBoolean(t.useZeroIndexingForArrays, true);
+ t.dateTimeFormat = Default.getString(t.dateTimeFormat, '{dd}{o} {mmmm} {yyyy} {hh}:{MM}:{ss}');
+ t.showArrowToggles = Default.getBoolean(t.showArrowToggles, true);
+ t.showStringQuotes = Default.getBoolean(t.showStringQuotes, true);
+ t.showAllAsClosed = Default.getBoolean(t.showAllAsClosed, false);
+ t.sortPropertyNames = Default.getBoolean(t.sortPropertyNames, true);
+ t.sortPropertyNamesInAlphabeticalOrder = Default.getBoolean(t.sortPropertyNamesInAlphabeticalOrder, true);
+ t.showCommas = Default.getBoolean(t.showCommas, false);
+ t.reverseArrayValues = Default.getBoolean(t.reverseArrayValues, false);
+ t.addArrayIndexPadding = Default.getBoolean(t.addArrayIndexPadding, false);
+ t.showValueColors = Default.getBoolean(t.showValueColors, true);
+ t.maximumDecimalPlaces = Default.getNumber(t.maximumDecimalPlaces, 2);
+ t.maximumStringLength = Default.getNumber(t.maximumStringLength, 0);
+ t.showStringHexColors = Default.getBoolean(t.showStringHexColors, false);
+ t.showArrayItemsAsSeparateObjects = Default.getBoolean(t.showArrayItemsAsSeparateObjects, false);
+ t.copyOnlyCurrentPage = Default.getBoolean(t.copyOnlyCurrentPage, false);
+ t = o(t);
+ t = l(t);
+ t = a(t);
+ return t;
+ }
+ t.get = r;
+ function o(e) {
+ e.title = Default.getObject(e.title, {});
+ e.title.text = Default.getString(e.title.text, 'JsonTree.js');
+ e.title.show = Default.getBoolean(e.title.show, true);
+ e.title.showTreeControls = Default.getBoolean(e.title.showTreeControls, true);
+ e.title.showCopyButton = Default.getBoolean(e.title.showCopyButton, true);
+ return e;
+ }
+ function l(e) {
+ e.ignore = Default.getObject(e.ignore, {});
+ e.ignore.nullValues = Default.getBoolean(e.ignore.nullValues, false);
+ e.ignore.functionValues = Default.getBoolean(e.ignore.functionValues, false);
+ e.ignore.unknownValues = Default.getBoolean(e.ignore.unknownValues, false);
+ e.ignore.booleanValues = Default.getBoolean(e.ignore.booleanValues, false);
+ e.ignore.decimalValues = Default.getBoolean(e.ignore.decimalValues, false);
+ e.ignore.numberValues = Default.getBoolean(e.ignore.numberValues, false);
+ e.ignore.stringValues = Default.getBoolean(e.ignore.stringValues, false);
+ e.ignore.dateValues = Default.getBoolean(e.ignore.dateValues, false);
+ e.ignore.objectValues = Default.getBoolean(e.ignore.objectValues, false);
+ e.ignore.arrayValues = Default.getBoolean(e.ignore.arrayValues, false);
+ return e;
+ }
+ function a(e) {
+ e.events = Default.getObject(e.events, {});
+ e.events.onBeforeRender = Default.getFunction(e.events.onBeforeRender, null);
+ e.events.onRenderComplete = Default.getFunction(e.events.onRenderComplete, null);
+ e.events.onValueClick = Default.getFunction(e.events.onValueClick, null);
+ e.events.onRefresh = Default.getFunction(e.events.onRefresh, null);
+ e.events.onCopyAll = Default.getFunction(e.events.onCopyAll, null);
+ e.events.onOpenAll = Default.getFunction(e.events.onOpenAll, null);
+ e.events.onCloseAll = Default.getFunction(e.events.onCloseAll, null);
+ e.events.onDestroy = Default.getFunction(e.events.onDestroy, null);
+ e.events.onBooleanRender = Default.getFunction(e.events.onBooleanRender, null);
+ e.events.onDecimalRender = Default.getFunction(e.events.onDecimalRender, null);
+ e.events.onNumberRender = Default.getFunction(e.events.onNumberRender, null);
+ e.events.onStringRender = Default.getFunction(e.events.onStringRender, null);
+ e.events.onDateRender = Default.getFunction(e.events.onDateRender, null);
+ e.events.onFunctionRender = Default.getFunction(e.events.onFunctionRender, null);
+ e.events.onNullRender = Default.getFunction(e.events.onNullRender, null);
+ e.events.onUnknownRender = Default.getFunction(e.events.onUnknownRender, null);
+ return e;
+ }
+ })((t = e.Options || (e.Options = {})));
+})(Binding || (Binding = {}));
+
+var Config;
+
+((e) => {
+ let t;
+ ((e) => {
+ function t(e = null) {
+ let t = Default.getObject(e, {});
+ t.safeMode = Default.getBoolean(t.safeMode, true);
+ t.domElementTypes = Default.getStringOrArray(t.domElementTypes, ['*']);
+ t = n(t);
+ return t;
+ }
+ e.get = t;
+ function n(e) {
+ e.text = Default.getObject(e.text, {});
+ e.text.objectText = Default.getAnyString(e.text.objectText, 'object');
+ e.text.arrayText = Default.getAnyString(e.text.arrayText, 'array');
+ e.text.closeAllButtonText = Default.getAnyString(e.text.closeAllButtonText, 'Close All');
+ e.text.openAllButtonText = Default.getAnyString(e.text.openAllButtonText, 'Open All');
+ e.text.copyAllButtonText = Default.getAnyString(e.text.copyAllButtonText, 'Copy All');
+ e.text.objectErrorText = Default.getAnyString(e.text.objectErrorText, 'Errors in object: {{error_1}}, {{error_2}}');
+ e.text.attributeNotValidErrorText = Default.getAnyString(
+ e.text.attributeNotValidErrorText,
+ "The attribute '{{attribute_name}}' is not a valid object.",
+ );
+ e.text.attributeNotSetErrorText = Default.getAnyString(
+ e.text.attributeNotSetErrorText,
+ "The attribute '{{attribute_name}}' has not been set correctly.",
+ );
+ e.text.stText = Default.getAnyString(e.text.stText, 'st');
+ e.text.ndText = Default.getAnyString(e.text.ndText, 'nd');
+ e.text.rdText = Default.getAnyString(e.text.rdText, 'rd');
+ e.text.thText = Default.getAnyString(e.text.thText, 'th');
+ e.text.ellipsisText = Default.getAnyString(e.text.ellipsisText, '...');
+ e.text.closeAllButtonSymbolText = Default.getAnyString(e.text.closeAllButtonSymbolText, '↑');
+ e.text.openAllButtonSymbolText = Default.getAnyString(e.text.openAllButtonSymbolText, '↓');
+ e.text.copyAllButtonSymbolText = Default.getAnyString(e.text.copyAllButtonSymbolText, '❐');
+ e.text.backButtonText = Default.getAnyString(e.text.backButtonText, 'Back');
+ e.text.nextButtonText = Default.getAnyString(e.text.nextButtonText, 'Next');
+ e.text.backButtonSymbolText = Default.getAnyString(e.text.backButtonSymbolText, '←');
+ e.text.nextButtonSymbolText = Default.getAnyString(e.text.nextButtonSymbolText, '→');
+ if (Is.invalidOptionArray(e.text.dayNames, 7)) {
+ e.text.dayNames = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
+ }
+ if (Is.invalidOptionArray(e.text.dayNamesAbbreviated, 7)) {
+ e.text.dayNamesAbbreviated = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
+ }
+ if (Is.invalidOptionArray(e.text.monthNames, 12)) {
+ e.text.monthNames = [
+ 'January',
+ 'February',
+ 'March',
+ 'April',
+ 'May',
+ 'June',
+ 'July',
+ 'August',
+ 'September',
+ 'October',
+ 'November',
+ 'December',
+ ];
+ }
+ if (Is.invalidOptionArray(e.text.monthNamesAbbreviated, 12)) {
+ e.text.monthNamesAbbreviated = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
+ }
+ return e;
+ }
+ })((t = e.Options || (e.Options = {})));
+})(Config || (Config = {}));
+
+var Trigger;
+
+((e) => {
+ function t(e, ...t) {
+ let n = null;
+ if (Is.definedFunction(e)) {
+ n = e.apply(null, [].slice.call(t, 0));
+ }
+ return n;
+ }
+ e.customEvent = t;
+})(Trigger || (Trigger = {}));
+
+(() => {
+ let _configuration = {};
+ let _elements_Data = {};
+ function render() {
+ const e = _configuration.domElementTypes;
+ const t = e.length;
+ for (let n = 0; n < t; n++) {
+ const t = document.getElementsByTagName(e[n]);
+ const r = [].slice.call(t);
+ const o = r.length;
+ for (let e = 0; e < o; e++) {
+ if (!renderElement(r[e])) {
+ break;
+ }
+ }
+ }
+ }
+ function renderElement(e) {
+ let t = true;
+ if (Is.defined(e) && e.hasAttribute(Constants.JSONTREE_JS_ATTRIBUTE_NAME)) {
+ const n = e.getAttribute(Constants.JSONTREE_JS_ATTRIBUTE_NAME);
+ if (Is.definedString(n)) {
+ const r = getObjectFromString(n);
+ if (r.parsed && Is.definedObject(r.object)) {
+ renderControl(Binding.Options.getForNewInstance(r.object, e));
+ } else {
+ if (!_configuration.safeMode) {
+ console.error(
+ _configuration.text.attributeNotValidErrorText.replace(
+ '{{attribute_name}}',
+ Constants.JSONTREE_JS_ATTRIBUTE_NAME,
+ ),
+ );
+ t = false;
+ }
+ }
+ } else {
+ if (!_configuration.safeMode) {
+ console.error(
+ _configuration.text.attributeNotSetErrorText.replace('{{attribute_name}}', Constants.JSONTREE_JS_ATTRIBUTE_NAME),
+ );
+ t = false;
+ }
+ }
+ }
+ return t;
+ }
+ function renderControl(e) {
+ Trigger.customEvent(e.events.onBeforeRender, e._currentView.element);
+ if (!Is.definedString(e._currentView.element.id)) {
+ e._currentView.element.id = Str.newGuid();
+ }
+ e._currentView.element.className = 'json-tree-js';
+ e._currentView.element.removeAttribute(Constants.JSONTREE_JS_ATTRIBUTE_NAME);
+ if (!_elements_Data.hasOwnProperty(e._currentView.element.id)) {
+ _elements_Data[e._currentView.element.id] = e;
+ }
+ renderControlContainer(e);
+ Trigger.customEvent(e.events.onRenderComplete, e._currentView.element);
+ }
+ function renderControlContainer(e) {
+ let t = _elements_Data[e._currentView.element.id].data;
+ e._currentView.element.innerHTML = '';
+ renderControlTitleBar(e, t);
+ if (e.showArrayItemsAsSeparateObjects) {
+ t = t[e._currentView.dataArrayCurrentIndex];
+ }
+ if (Is.definedObject(t) && !Is.definedArray(t)) {
+ renderObject(e._currentView.element, e, t, true);
+ } else if (Is.definedArray(t)) {
+ renderArray(e._currentView.element, e, t);
+ }
+ }
+ function renderControlTitleBar(e, t) {
+ if (e.title.show || e.title.showTreeControls || e.title.showCopyButton) {
+ const n = DomElement.create(e._currentView.element, 'div', 'title-bar');
+ const r = DomElement.create(n, 'div', 'controls');
+ if (e.title.show) {
+ DomElement.createWithHTML(n, 'div', 'title', e.title.text, r);
+ }
+ if (e.title.showCopyButton) {
+ const t = DomElement.createWithHTML(r, 'button', 'copy-all', _configuration.text.copyAllButtonSymbolText);
+ t.title = _configuration.text.copyAllButtonText;
+ t.onclick = () => {
+ let t = null;
+ if (e.copyOnlyCurrentPage && e.showArrayItemsAsSeparateObjects) {
+ t = JSON.stringify(_elements_Data[e._currentView.element.id].data[e._currentView.dataArrayCurrentIndex], null, 2);
+ } else {
+ t = JSON.stringify(_elements_Data[e._currentView.element.id].data, null, 2);
+ }
+ navigator.clipboard.writeText(t);
+ Trigger.customEvent(e.events.onCopyAll, t);
+ };
+ }
+ if (e.title.showTreeControls) {
+ const t = DomElement.createWithHTML(r, 'button', 'openAll', _configuration.text.openAllButtonSymbolText);
+ t.title = _configuration.text.openAllButtonText;
+ const n = DomElement.createWithHTML(r, 'button', 'closeAll', _configuration.text.closeAllButtonSymbolText);
+ n.title = _configuration.text.closeAllButtonText;
+ t.onclick = () => {
+ openAllNodes(e);
+ };
+ n.onclick = () => {
+ closeAllNodes(e);
+ };
+ }
+ if (e.showArrayItemsAsSeparateObjects && Is.definedArray(t) && t.length > 1) {
+ const n = DomElement.createWithHTML(r, 'button', 'back', _configuration.text.backButtonSymbolText);
+ n.title = _configuration.text.backButtonText;
+ if (e._currentView.dataArrayCurrentIndex > 0) {
+ n.onclick = () => {
+ e._currentView.dataArrayCurrentIndex--;
+ renderControlContainer(e);
+ };
+ } else {
+ n.disabled = true;
+ }
+ const o = DomElement.createWithHTML(r, 'button', 'next', _configuration.text.nextButtonSymbolText);
+ o.title = _configuration.text.nextButtonText;
+ if (e._currentView.dataArrayCurrentIndex < t.length - 1) {
+ o.onclick = () => {
+ e._currentView.dataArrayCurrentIndex++;
+ renderControlContainer(e);
+ };
+ } else {
+ o.disabled = true;
+ }
+ } else {
+ e.showArrayItemsAsSeparateObjects = false;
+ }
+ }
+ }
+ function openAllNodes(e) {
+ e.showAllAsClosed = false;
+ renderControlContainer(e);
+ Trigger.customEvent(e.events.onOpenAll, e._currentView.element);
+ }
+ function closeAllNodes(e) {
+ e.showAllAsClosed = true;
+ renderControlContainer(e);
+ Trigger.customEvent(e.events.onCloseAll, e._currentView.element);
+ }
+ function renderObject(e, t, n, r = false) {
+ const o = DomElement.create(e, 'div', 'object-type-title');
+ const l = DomElement.create(e, 'div', 'object-type-contents');
+ const a = t.showArrowToggles ? DomElement.create(o, 'div', 'down-arrow') : null;
+ const i = renderObjectValues(a, l, t, n);
+ const s = DomElement.createWithHTML(o, 'span', t.showValueColors ? 'object' : '', _configuration.text.objectText);
+ if (r && t.showArrayItemsAsSeparateObjects) {
+ let e = t.useZeroIndexingForArrays
+ ? t._currentView.dataArrayCurrentIndex.toString()
+ : (t._currentView.dataArrayCurrentIndex + 1).toString();
+ DomElement.createWithHTML(o, 'span', t.showValueColors ? 'object data-array-index' : 'data-array-index', `[${e}]:`, s);
+ }
+ if (t.showCounts && i > 0) {
+ DomElement.createWithHTML(o, 'span', t.showValueColors ? 'object count' : 'count', `{${i}}`);
+ }
+ }
+ function renderArray(e, t, n) {
+ const r = DomElement.create(e, 'div', 'object-type-title');
+ const o = DomElement.create(e, 'div', 'object-type-contents');
+ const l = t.showArrowToggles ? DomElement.create(r, 'div', 'down-arrow') : null;
+ DomElement.createWithHTML(r, 'span', t.showValueColors ? 'array' : '', _configuration.text.arrayText);
+ renderArrayValues(l, o, t, n);
+ if (t.showCounts) {
+ DomElement.createWithHTML(r, 'span', t.showValueColors ? 'array count' : 'count', `[${n.length}]`);
+ }
+ }
+ function renderObjectValues(e, t, n, r) {
+ let o = 0;
+ let l = [];
+ for (let e in r) {
+ if (r.hasOwnProperty(e)) {
+ l.push(e);
+ }
+ }
+ if (n.sortPropertyNames) {
+ l = l.sort();
+ if (!n.sortPropertyNamesInAlphabeticalOrder) {
+ l = l.reverse();
+ }
+ }
+ const a = l.length;
+ for (let e = 0; e < a; e++) {
+ const i = l[e];
+ if (r.hasOwnProperty(i)) {
+ renderValue(t, n, i, r[i], e === a - 1);
+ o++;
+ }
+ }
+ addArrowEvent(n, e, t);
+ return o;
+ }
+ function renderArrayValues(e, t, n, r) {
+ const o = r.length;
+ if (!n.reverseArrayValues) {
+ for (let e = 0; e < o; e++) {
+ renderValue(t, n, getIndexName(n, e, o), r[e], e === o - 1);
+ }
+ } else {
+ for (let e = o; e--; ) {
+ renderValue(t, n, getIndexName(n, e, o), r[e], e === 0);
+ }
+ }
+ addArrowEvent(n, e, t);
+ }
+ function renderValue(e, t, n, r, o) {
+ const l = DomElement.create(e, 'div', 'object-type-value');
+ const a = t.showArrowToggles ? DomElement.create(l, 'div', 'no-arrow') : null;
+ let i = null;
+ let s = null;
+ let u = false;
+ let c = null;
+ let d = true;
+ DomElement.createWithHTML(l, 'span', 'title', n);
+ DomElement.createWithHTML(l, 'span', 'split', ':');
+ if (!Is.defined(r)) {
+ if (!t.ignore.nullValues) {
+ i = t.showValueColors ? 'null' : '';
+ s = DomElement.createWithHTML(l, 'span', i, 'null');
+ d = false;
+ if (Is.definedFunction(t.events.onNullRender)) {
+ Trigger.customEvent(t.events.onNullRender, s);
+ }
+ createComma(t, l, o);
+ } else {
+ u = true;
+ }
+ } else if (Is.definedFunction(r)) {
+ if (!t.ignore.functionValues) {
+ i = t.showValueColors ? 'function' : '';
+ s = DomElement.createWithHTML(l, 'span', i, Default.getFunctionName(r));
+ c = 'function';
+ if (Is.definedFunction(t.events.onFunctionRender)) {
+ Trigger.customEvent(t.events.onFunctionRender, s);
+ }
+ createComma(t, l, o);
+ } else {
+ u = true;
+ }
+ } else if (Is.definedBoolean(r)) {
+ if (!t.ignore.booleanValues) {
+ i = t.showValueColors ? 'boolean' : '';
+ s = DomElement.createWithHTML(l, 'span', i, r);
+ c = 'boolean';
+ if (Is.definedFunction(t.events.onBooleanRender)) {
+ Trigger.customEvent(t.events.onBooleanRender, s);
+ }
+ createComma(t, l, o);
+ } else {
+ u = true;
+ }
+ } else if (Is.definedDecimal(r)) {
+ if (!t.ignore.decimalValues) {
+ const e = Default.getFixedDecimalPlacesValue(r, t.maximumDecimalPlaces);
+ i = t.showValueColors ? 'decimal' : '';
+ s = DomElement.createWithHTML(l, 'span', i, e);
+ c = 'decimal';
+ if (Is.definedFunction(t.events.onDecimalRender)) {
+ Trigger.customEvent(t.events.onDecimalRender, s);
+ }
+ createComma(t, l, o);
+ } else {
+ u = true;
+ }
+ } else if (Is.definedNumber(r)) {
+ if (!t.ignore.numberValues) {
+ i = t.showValueColors ? 'number' : '';
+ s = DomElement.createWithHTML(l, 'span', i, r);
+ c = 'number';
+ if (Is.definedFunction(t.events.onNumberRender)) {
+ Trigger.customEvent(t.events.onNumberRender, s);
+ }
+ createComma(t, l, o);
+ } else {
+ u = true;
+ }
+ } else if (Is.definedString(r)) {
+ if (!t.ignore.stringValues) {
+ let e = null;
+ if (t.showValueColors && t.showStringHexColors && Is.hexColor(r)) {
+ e = r;
+ } else {
+ if (t.maximumStringLength > 0 && r.length > t.maximumStringLength) {
+ r = r.substring(0, t.maximumStringLength) + _configuration.text.ellipsisText;
+ }
+ }
+ const n = t.showStringQuotes ? `"${r}"` : r;
+ i = t.showValueColors ? 'string' : '';
+ s = DomElement.createWithHTML(l, 'span', i, n);
+ c = 'string';
+ if (Is.definedString(e)) {
+ s.style.color = e;
+ }
+ if (Is.definedFunction(t.events.onStringRender)) {
+ Trigger.customEvent(t.events.onStringRender, s);
+ }
+ createComma(t, l, o);
+ } else {
+ u = true;
+ }
+ } else if (Is.definedDate(r)) {
+ if (!t.ignore.dateValues) {
+ i = t.showValueColors ? 'date' : '';
+ s = DomElement.createWithHTML(l, 'span', i, DateTime.getCustomFormattedDateText(_configuration, r, t.dateTimeFormat));
+ c = 'date';
+ if (Is.definedFunction(t.events.onDateRender)) {
+ Trigger.customEvent(t.events.onDateRender, s);
+ }
+ createComma(t, l, o);
+ } else {
+ u = true;
+ }
+ } else if (Is.definedObject(r) && !Is.definedArray(r)) {
+ if (!t.ignore.objectValues) {
+ const e = DomElement.create(l, 'span', t.showValueColors ? 'object' : '');
+ const n = DomElement.create(l, 'div', 'object-type-contents');
+ const i = renderObjectValues(a, n, t, r);
+ DomElement.createWithHTML(e, 'span', 'title', _configuration.text.objectText);
+ if (t.showCounts && i > 0) {
+ DomElement.createWithHTML(e, 'span', 'count', `{${i}}`);
+ }
+ createComma(t, e, o);
+ c = 'object';
+ } else {
+ u = true;
+ }
+ } else if (Is.definedArray(r)) {
+ if (!t.ignore.arrayValues) {
+ const e = DomElement.create(l, 'span', t.showValueColors ? 'array' : '');
+ const n = DomElement.create(l, 'div', 'object-type-contents');
+ DomElement.createWithHTML(e, 'span', 'title', _configuration.text.arrayText);
+ if (t.showCounts) {
+ DomElement.createWithHTML(e, 'span', 'count', `[${r.length}]`);
+ }
+ createComma(t, e, o);
+ renderArrayValues(a, n, t, r);
+ c = 'array';
+ } else {
+ u = true;
+ }
+ } else {
+ if (!t.ignore.unknownValues) {
+ i = t.showValueColors ? 'unknown' : '';
+ s = DomElement.createWithHTML(l, 'span', i, r.toString());
+ c = 'unknown';
+ if (Is.definedFunction(t.events.onUnknownRender)) {
+ Trigger.customEvent(t.events.onUnknownRender, s);
+ }
+ createComma(t, l, o);
+ } else {
+ u = true;
+ }
+ }
+ if (u) {
+ e.removeChild(l);
+ } else {
+ if (Is.defined(s)) {
+ addValueClickEvent(t, s, r, c, d);
+ }
+ }
+ }
+ function addValueClickEvent(e, t, n, r, o) {
+ if (o && Is.definedFunction(e.events.onValueClick)) {
+ t.onclick = () => {
+ Trigger.customEvent(e.events.onValueClick, n, r);
+ };
+ } else {
+ DomElement.addClass(t, 'no-hover');
+ }
+ }
+ function addArrowEvent(e, t, n) {
+ if (Is.defined(t)) {
+ t.onclick = () => {
+ if (t.className === 'down-arrow') {
+ n.style.display = 'none';
+ t.className = 'right-arrow';
+ } else {
+ n.style.display = 'block';
+ t.className = 'down-arrow';
+ }
+ };
+ if (e.showAllAsClosed) {
+ n.style.display = 'none';
+ t.className = 'right-arrow';
+ } else {
+ t.className = 'down-arrow';
+ }
+ }
+ }
+ function createComma(e, t, n) {
+ if (e.showCommas && !n) {
+ DomElement.createWithHTML(t, 'span', 'comma', ',');
+ }
+ }
+ function getIndexName(e, t, n) {
+ let r = e.useZeroIndexingForArrays ? t.toString() : (t + 1).toString();
+ if (!e.addArrayIndexPadding) {
+ r = Str.padNumber(parseInt(r), n.toString().length);
+ }
+ return `[${r}]`;
+ }
+ function getObjectFromString(objectString) {
+ const result = {
+ parsed: true,
+ object: null,
+ };
+ try {
+ if (Is.definedString(objectString)) {
+ result.object = JSON.parse(objectString);
+ }
+ } catch (e1) {
+ try {
+ result.object = eval(`(${objectString})`);
+ if (Is.definedFunction(result.object)) {
+ result.object = result.object();
+ }
+ } catch (e) {
+ if (!_configuration.safeMode) {
+ console.error(_configuration.text.objectErrorText.replace('{{error_1}}', e1.message).replace('{{error_2}}', e.message));
+ result.parsed = false;
+ }
+ result.object = null;
+ }
+ }
+ return result;
+ }
+ function destroyElement(e) {
+ e._currentView.element.innerHTML = '';
+ e._currentView.element.className = '';
+ Trigger.customEvent(e.events.onDestroy, e._currentView.element);
+ }
+ const _public = {
+ refresh: function (e) {
+ if (Is.definedString(e) && _elements_Data.hasOwnProperty(e)) {
+ const t = _elements_Data[e];
+ renderControlContainer(t);
+ Trigger.customEvent(t.events.onRefresh, t._currentView.element);
+ }
+ return _public;
+ },
+ refreshAll: function () {
+ for (let e in _elements_Data) {
+ if (_elements_Data.hasOwnProperty(e)) {
+ const t = _elements_Data[e];
+ renderControlContainer(t);
+ Trigger.customEvent(t.events.onRefresh, t._currentView.element);
+ }
+ }
+ return _public;
+ },
+ render: function (e, t) {
+ if (Is.definedObject(e) && Is.definedObject(t)) {
+ renderControl(Binding.Options.getForNewInstance(t, e));
+ }
+ return _public;
+ },
+ renderAll: function () {
+ render();
+ return _public;
+ },
+ openAll: function (e) {
+ if (Is.definedString(e) && _elements_Data.hasOwnProperty(e)) {
+ openAllNodes(_elements_Data[e]);
+ }
+ return _public;
+ },
+ closeAll: function (e) {
+ if (Is.definedString(e) && _elements_Data.hasOwnProperty(e)) {
+ closeAllNodes(_elements_Data[e]);
+ }
+ return _public;
+ },
+ destroy: function (e) {
+ if (Is.definedString(e) && _elements_Data.hasOwnProperty(e)) {
+ destroyElement(_elements_Data[e]);
+ delete _elements_Data[e];
+ }
+ return _public;
+ },
+ destroyAll: function () {
+ for (let e in _elements_Data) {
+ if (_elements_Data.hasOwnProperty(e)) {
+ destroyElement(_elements_Data[e]);
+ }
+ }
+ _elements_Data = {};
+ return _public;
+ },
+ setConfiguration: function (e) {
+ if (Is.definedObject(e)) {
+ let t = false;
+ const n = _configuration;
+ for (let r in e) {
+ if (e.hasOwnProperty(r) && _configuration.hasOwnProperty(r) && n[r] !== e[r]) {
+ n[r] = e[r];
+ t = true;
+ }
+ }
+ if (t) {
+ _configuration = Config.Options.get(n);
+ }
+ }
+ return _public;
+ },
+ getIds: function () {
+ const e = [];
+ for (let t in _elements_Data) {
+ if (_elements_Data.hasOwnProperty(t)) {
+ e.push(t);
+ }
+ }
+ return e;
+ },
+ getVersion: function () {
+ return '2.1.0';
+ },
+ };
+ (() => {
+ _configuration = Config.Options.get();
+ document.addEventListener('DOMContentLoaded', function () {
+ render();
+ });
+ if (!Is.defined(window.$jsontree)) {
+ window.$jsontree = _public;
+ }
+ })();
+})(); //# sourceMappingURL=jsontree.js.map
diff --git a/static/configvis/jsontree.js.css b/static/configvis/jsontree.js.css
new file mode 100644
index 00000000..f3d4d3d4
--- /dev/null
+++ b/static/configvis/jsontree.js.css
@@ -0,0 +1,330 @@
+/*
+ * JsonTree.js Library v2.1.0
+ *
+ * Copyright 2024 Bunoon
+ * Released under the MIT License
+ */
+:root {
+ --json-tree-js-default-font: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', 'Noto Sans', 'Liberation Sans', Arial,
+ sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
+ --json-tree-js-text-bold-weight: 400;
+ --json-tree-js-header-bold-weight: 900;
+ --json-tree-js-title-bold-weight: var(--json-tree-js-header-bold-weight);
+ --json-tree-js-text-bold-weight-active: var(--json-tree-js-header-bold-weight);
+ --json-tree-js-color-black: #3b3a3a;
+ --json-tree-js-color-white: #f5f5f5;
+ --json-tree-js-color-snow-white: #f5f5f5;
+ --json-tree-js-color-boolean: #ff0000;
+ --json-tree-js-color-decimal: #e3c868;
+ --json-tree-js-color-number: #666bf9;
+ --json-tree-js-color-string: #78b13f;
+ --json-tree-js-color-date: #a656f5;
+ --json-tree-js-color-array: #f28c28;
+ --json-tree-js-color-object: #c0c0c0;
+ --json-tree-js-color-null: #bbbbbb;
+ --json-tree-js-color-function: var(--json-tree-js-color-null);
+ --json-tree-js-color-unknown: var(--json-tree-js-color-null);
+ --json-tree-js-container-background-color: #22272e;
+ --json-tree-js-container-border-color: #454c56;
+ --json-tree-js-button-background-color: #2d333b;
+ --json-tree-js-button-border-color: var(--json-tree-js-container-border-color);
+ --json-tree-js-button-text-color: var(--json-tree-js-color-white);
+ --json-tree-js-button-background-color-hover: var(--json-tree-js-container-border-color);
+ --json-tree-js-button-text-color-hover: var(--json-tree-js-color-snow-white);
+ --json-tree-js-button-background-color-active: #616b79;
+ --json-tree-js-button-text-color-active: var(--json-tree-js-color-snow-white);
+ --json-tree-js-border-radius: 0.5rem;
+ --json-tree-js-border-style-scrollbar: inset 0 0 6px var(--json-tree-js-color-dark-gray);
+ --json-tree-js-border-size: 0.5px;
+ --json-tree-js-spacing: 10px;
+ --json-tree-js-spacing-font-size: 0.85rem;
+ --json-tree-js-transition: all 0.3s;
+}
+
+/*
+ -------------------------------------------------------------------------
+ JsonTree.js - Container
+ -------------------------------------------------------------------------
+*/
+div.json-tree-js {
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+ cursor: default;
+ box-sizing: border-box;
+ line-height: normal;
+ font-family: var(--json-tree-js-default-font) !important;
+ display: inline-block;
+ position: relative;
+ border-radius: var(--json-tree-js-border-radius);
+ background-color: var(--json-tree-js-container-background-color);
+ color: var(--json-tree-js-color-white);
+ border: var(--json-tree-js-border-size) solid var(--json-tree-js-container-border-color);
+ padding: var(--json-tree-js-spacing);
+ font-size: var(--json-tree-js-spacing-font-size);
+ font-weight: var(--json-tree-js-text-bold-weight);
+ width: auto;
+ overflow: hidden;
+ margin: 0 !important;
+}
+div.json-tree-js button {
+ font-family: var(--heat-js-default-font);
+}
+div.json-tree-js div.no-click {
+ pointer-events: none !important;
+}
+div.json-tree-js * {
+ box-sizing: border-box;
+ line-height: normal;
+}
+div.json-tree-js *::before,
+div.json-tree-js *::after {
+ box-sizing: border-box;
+ line-height: normal;
+}
+
+/*
+ -------------------------------------------------------------------------
+ JsonTree.js - Arrows
+ -------------------------------------------------------------------------
+*/
+div.json-tree-js div.no-arrow {
+ display: inline-block;
+ width: 12px;
+ height: 8px;
+ margin-right: calc(var(--json-tree-js-spacing));
+}
+div.json-tree-js div.down-arrow,
+div.json-tree-js div.right-arrow {
+ display: inline-block;
+ width: 0;
+ height: 0;
+ margin-right: calc(var(--json-tree-js-spacing));
+ cursor: pointer;
+ transition: var(--json-tree-js-transition);
+ transition-property: opacity;
+}
+div.json-tree-js div.down-arrow:hover,
+div.json-tree-js div.right-arrow:hover {
+ opacity: 0.7;
+}
+div.json-tree-js div.down-arrow {
+ border-left: 6px solid transparent;
+ border-right: 6px solid transparent;
+ border-top: 8px solid var(--json-tree-js-color-white);
+}
+div.json-tree-js div.right-arrow {
+ border-top: 5px solid transparent;
+ border-bottom: 5px solid transparent;
+ border-left: 12px solid var(--json-tree-js-color-white);
+}
+
+/*
+ -------------------------------------------------------------------------
+ JsonTree.js - Title Bar
+ -------------------------------------------------------------------------
+*/
+div.json-tree-js div.title-bar {
+ display: flex;
+ margin-bottom: var(--json-tree-js-spacing);
+}
+div.json-tree-js div.title-bar div.title {
+ text-align: left;
+ width: auto;
+ font-weight: var(--json-tree-js-title-bold-weight);
+ font-size: 1.2rem;
+}
+div.json-tree-js div.title-bar div.controls {
+ margin-left: calc(var(--json-tree-js-spacing) * 6);
+ flex-grow: 1;
+ text-align: right;
+}
+@media (min-width: 768px) {
+ div.json-tree-js div.title-bar div.controls {
+ margin-left: calc(var(--json-tree-js-spacing) * 12);
+ }
+}
+div.json-tree-js div.title-bar div.controls button {
+ background-color: var(--json-tree-js-button-background-color);
+ border: var(--json-tree-js-border-size) solid var(--json-tree-js-button-border-color);
+ color: var(--json-tree-js-button-text-color);
+ border-radius: var(--json-tree-js-border-radius);
+ padding-top: 5px;
+ padding-bottom: 5px;
+ padding-left: 9px;
+ padding-right: 9px;
+ outline: none;
+ transition: var(--json-tree-js-transition);
+}
+div.json-tree-js div.title-bar div.controls button:disabled {
+ color: var(--json-tree-js-button-border-color);
+}
+div.json-tree-js div.title-bar div.controls button:not(.active):not(:disabled):active {
+ background: var(--json-tree-js-button-background-color-active) !important;
+ color: var(--json-tree-js-button-text-color-active) !important;
+}
+div.json-tree-js div.title-bar div.controls button:not(.active):not(:disabled):hover {
+ cursor: pointer;
+ background: var(--json-tree-js-button-background-color-hover);
+ color: var(--json-tree-js-button-text-color-hover);
+}
+div.json-tree-js div.title-bar div.controls button {
+ margin-left: calc(var(--json-tree-js-spacing) / 2) !important;
+}
+div.json-tree-js div.title-bar div.controls button.back {
+ margin-left: calc(var(--json-tree-js-spacing) * 2) !important;
+}
+div.json-tree-js div.title-bar div.controls button.copy-all {
+ display: none;
+}
+@media (min-width: 768px) {
+ div.json-tree-js div.title-bar div.controls button.copy-all {
+ display: inline-block;
+ }
+}
+
+/*
+ -------------------------------------------------------------------------
+ JsonTree.js - Object Type Title
+ -------------------------------------------------------------------------
+*/
+div.json-tree-js div.object-type-title {
+ font-weight: var(--json-tree-js-header-bold-weight);
+ text-align: left !important;
+}
+div.json-tree-js div.object-type-title span.array {
+ color: var(--json-tree-js-color-array);
+}
+div.json-tree-js div.object-type-title span.object {
+ color: var(--json-tree-js-color-object);
+}
+div.json-tree-js div.object-type-title span.count {
+ margin-left: calc(var(--json-tree-js-spacing) / 2);
+ font-weight: var(--json-tree-js-text-bold-weight);
+}
+div.json-tree-js div.object-type-title span.data-array-index {
+ margin-right: calc(var(--json-tree-js-spacing) / 2);
+ font-weight: var(--json-tree-js-text-bold-weight);
+}
+
+/*
+ -------------------------------------------------------------------------
+ JsonTree.js - Object Type Contents
+ -------------------------------------------------------------------------
+*/
+div.json-tree-js div.object-type-contents {
+ margin-top: calc(var(--json-tree-js-spacing) / 2);
+}
+
+/*
+ -------------------------------------------------------------------------
+ JsonTree.js - Object Type Contents - Values
+ -------------------------------------------------------------------------
+*/
+div.json-tree-js div.object-type-contents {
+ margin-left: calc(var(--json-tree-js-spacing) * 2);
+ text-align: left !important;
+}
+div.json-tree-js div.object-type-contents div.object-type-value {
+ white-space: nowrap;
+ overflow: hidden;
+ margin-top: calc(var(--json-tree-js-spacing) / 2);
+ margin-bottom: calc(var(--json-tree-js-spacing) / 2);
+}
+div.json-tree-js div.object-type-contents div.object-type-value span.split {
+ margin-left: calc(var(--json-tree-js-spacing) / 2);
+ margin-right: calc(var(--json-tree-js-spacing) / 2);
+}
+div.json-tree-js div.object-type-contents div.object-type-value span.boolean,
+div.json-tree-js div.object-type-contents div.object-type-value span.decimal,
+div.json-tree-js div.object-type-contents div.object-type-value span.number,
+div.json-tree-js div.object-type-contents div.object-type-value span.string,
+div.json-tree-js div.object-type-contents div.object-type-value span.date,
+div.json-tree-js div.object-type-contents div.object-type-value span.null,
+div.json-tree-js div.object-type-contents div.object-type-value span.function,
+div.json-tree-js div.object-type-contents div.object-type-value span.unknown {
+ transition: var(--json-tree-js-transition);
+ transition-property: opacity;
+}
+div.json-tree-js div.object-type-contents div.object-type-value span.boolean:not(.no-hover):hover,
+div.json-tree-js div.object-type-contents div.object-type-value span.decimal:not(.no-hover):hover,
+div.json-tree-js div.object-type-contents div.object-type-value span.number:not(.no-hover):hover,
+div.json-tree-js div.object-type-contents div.object-type-value span.string:not(.no-hover):hover,
+div.json-tree-js div.object-type-contents div.object-type-value span.date:not(.no-hover):hover,
+div.json-tree-js div.object-type-contents div.object-type-value span.null:not(.no-hover):hover,
+div.json-tree-js div.object-type-contents div.object-type-value span.function:not(.no-hover):hover,
+div.json-tree-js div.object-type-contents div.object-type-value span.unknown:not(.no-hover):hover {
+ cursor: pointer;
+ opacity: 0.7;
+}
+div.json-tree-js div.object-type-contents div.object-type-value span.comma {
+ color: var(--json-tree-js-color-white);
+ font-weight: var(--json-tree-js-text-bold-weight);
+}
+div.json-tree-js div.object-type-contents div.object-type-value span.boolean {
+ color: var(--json-tree-js-color-boolean);
+}
+div.json-tree-js div.object-type-contents div.object-type-value span.decimal {
+ color: var(--json-tree-js-color-decimal);
+}
+div.json-tree-js div.object-type-contents div.object-type-value span.number {
+ color: var(--json-tree-js-color-number);
+}
+div.json-tree-js div.object-type-contents div.object-type-value span.string {
+ color: var(--json-tree-js-color-string);
+}
+div.json-tree-js div.object-type-contents div.object-type-value span.date {
+ color: var(--json-tree-js-color-date);
+}
+div.json-tree-js div.object-type-contents div.object-type-value span.array {
+ font-weight: var(--json-tree-js-header-bold-weight);
+ color: var(--json-tree-js-color-array);
+}
+div.json-tree-js div.object-type-contents div.object-type-value span.object {
+ font-weight: var(--json-tree-js-header-bold-weight);
+ color: var(--json-tree-js-color-object);
+}
+div.json-tree-js div.object-type-contents div.object-type-value span.null {
+ color: var(--json-tree-js-color-null);
+ font-style: italic;
+}
+div.json-tree-js div.object-type-contents div.object-type-value span.function {
+ color: var(--json-tree-js-color-function);
+ font-style: italic;
+}
+div.json-tree-js div.object-type-contents div.object-type-value span.unknown {
+ color: var(--json-tree-js-color-unknown);
+ font-style: italic;
+}
+div.json-tree-js div.object-type-contents div.object-type-value span.count {
+ margin-left: calc(var(--json-tree-js-spacing) / 2);
+ font-weight: var(--json-tree-js-text-bold-weight);
+}
+
+/*
+ -------------------------------------------------------------------------
+ JsonTree.js - Custom Scroll Bar
+ -------------------------------------------------------------------------
+*/
+.custom-scroll-bars::-webkit-scrollbar {
+ width: 12px;
+}
+.custom-scroll-bars::-webkit-scrollbar-track {
+ -webkit-box-shadow: var(--json-tree-js-border-style-scrollbar);
+ box-shadow: var(--json-tree-js-border-style-scrollbar);
+}
+.custom-scroll-bars::-webkit-scrollbar-thumb {
+ -webkit-box-shadow: var(--json-tree-js-border-style-scrollbar);
+ box-shadow: var(--json-tree-js-border-style-scrollbar);
+ background: var(--json-tree-js-color-white);
+}
+.custom-scroll-bars::-webkit-scrollbar-thumb:hover {
+ background-color: var(--json-tree-js-color-white);
+}
+.custom-scroll-bars::-webkit-scrollbar-thumb:active {
+ background-color: var(--json-tree-js-color-lighter-gray);
+}
+
+/*# sourceMappingURL=jsontree.js.css.map */
diff --git a/static/configvis/jsontree.js.css.map b/static/configvis/jsontree.js.css.map
new file mode 100644
index 00000000..782f92e0
--- /dev/null
+++ b/static/configvis/jsontree.js.css.map
@@ -0,0 +1 @@
+{"version":3,"sourceRoot":"","sources":["../src/jsontree.js.scss","../src/sass/_styles.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUA;EAEI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAcA;EACA;EACA;EACA;EAGA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EAGA;EACA;EAGA;;;AAIJ;AAAA;AAAA;AAAA;AAAA;AAMA;EC9EI;EACA;EACA;EACA;EACA;EACA;EAUA;EACA;EDiEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAGJ;ECxFA;EACA;;AD0FI;EC3FJ;EACA;;;ADkGJ;AAAA;AAAA;AAAA;AAAA;AAOI;EACI;EACA;EACA;EACA;;AAGJ;AAAA;EAEI;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;AAAA;EACI;;AAIR;EACI;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAKR;AAAA;AAAA;AAAA;AAAA;AAOI;EACI;EACA;;AAEA;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;AAEA;EALJ;IAMQ;;;ACjKZ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;EACA;;AD8IA;EACI;;AAGJ;EACI;;AAGJ;EACI;;AAEA;EAHJ;IAIQ;;;;AAQpB;AAAA;AAAA;AAAA;AAAA;AAOI;EACI;EACA;;AAEA;EACI;;AAGJ;EACI;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;;;AAKZ;AAAA;AAAA;AAAA;AAAA;AAOI;EACI;;;AAKR;AAAA;AAAA;AAAA;AAAA;AAOI;EACI;EACA;;AAEA;EACI;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAGJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAQI;EACA;;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACI;EACA;;AAIR;EACI;EACA;;AAGJ;EACI;;AAGJ;EACI;;AAGJ;EACI;;AAGJ;EACI;;AAGJ;EACI;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;;;AAOhB;AAAA;AAAA;AAAA;AAAA;AAOI;EACI;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;EACA;;AAEA;EACI;;AAGJ;EACI","file":"jsontree.js.css"}
\ No newline at end of file
diff --git a/static/configvis/jsontree.js.map b/static/configvis/jsontree.js.map
new file mode 100644
index 00000000..21e76574
--- /dev/null
+++ b/static/configvis/jsontree.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["/Users/williamtroup/Documents/GitHub%20Repos/JsonTree.js/dist/jsontree.js"],"names":["Is","Is2","defined","value","toString","definedObject","object","definedBoolean","definedString","definedFunction","definedNumber","definedArray","Array","definedDate","Date","definedDecimal","invalidOptionArray","array","minimumLength","length","hexColor","valid","isNaN","substring","Default","Default2","getAnyString","defaultValue","getString","getBoolean","getNumber","getFunction","getArray","getObject","getStringOrArray","result2","values","split","getFixedDecimalPlacesValue","decimalPlaces","_a","regExp","RegExp","match","getFunctionName","valueParts","valueNameParts","DomElement","DomElement2","create","container","type","className","beforeNode","nodeType","toLowerCase","isText","document","createTextNode","createElement","insertBefore","appendChild","createWithHTML","html","element","innerHTML","addClass","classList","add","Str","Str2","newGuid","charIndex","push","character","Math","floor","random","join","padNumber","number","numberString","numberResult","arrayLength","DateTime","DateTime2","getWeekdayNumber","date","getDay","getDayOrdinal","configuration","text","thText","stText","ndText","rdText","getCustomFormattedDateText","dateFormat","weekDayNumber","replace","getHours","getMinutes","getSeconds","dayNames","dayNamesAbbreviated","getDate","monthNames","getMonth","monthNamesAbbreviated","getFullYear","Number","parseInt","Constants","Constants2","JSONTREE_JS_ATTRIBUTE_NAME","Binding","Binding2","Options","Options2","getForNewInstance","data","bindingOptions","get","_currentView","dataArrayCurrentIndex","newOptions","options","showCounts","useZeroIndexingForArrays","dateTimeFormat","showArrowToggles","showStringQuotes","showAllAsClosed","sortPropertyNames","sortPropertyNamesInAlphabeticalOrder","showCommas","reverseArrayValues","addArrayIndexPadding","showValueColors","maximumDecimalPlaces","maximumStringLength","showStringHexColors","showArrayItemsAsSeparateObjects","copyOnlyCurrentPage","getTitle","getIgnore","getCustomTriggers","title","show","showTreeControls","showCopyButton","ignore","nullValues","functionValues","unknownValues","booleanValues","decimalValues","numberValues","stringValues","dateValues","objectValues","arrayValues","events","onBeforeRender","onRenderComplete","onValueClick","onRefresh","onCopyAll","onOpenAll","onCloseAll","onDestroy","onBooleanRender","onDecimalRender","onNumberRender","onStringRender","onDateRender","onFunctionRender","onNullRender","onUnknownRender","Config","Config2","newConfiguration","safeMode","domElementTypes","getText","objectText","arrayText","closeAllButtonText","openAllButtonText","copyAllButtonText","objectErrorText","attributeNotValidErrorText","attributeNotSetErrorText","ellipsisText","closeAllButtonSymbolText","openAllButtonSymbolText","copyAllButtonSymbolText","backButtonText","nextButtonText","backButtonSymbolText","nextButtonSymbolText","Trigger","Trigger2","customEvent","triggerFunction","args","apply","slice","call","_configuration","_elements_Data","render","tagTypes","tagTypesLength","tagTypeIndex","domElements","getElementsByTagName","elements","elementsLength","elementIndex","renderElement","hasAttribute","bindingOptionsData","getAttribute","getObjectFromString","parsed","renderControl","console","error","id","removeAttribute","hasOwnProperty","renderControlContainer","renderControlTitleBar","renderObject","renderArray","titleBar","controls","copy","onclick","copyData","JSON","stringify","navigator","clipboard","writeText","openAll","closeAll","openAllNodes","closeAllNodes","back","disabled","next","showPagingIndex","objectTypeTitle","objectTypeContents","arrow","propertyCount","renderObjectValues","titleText","dataArrayIndex","renderArrayValues","properties","key","sort","reverse","propertiesLength","propertyIndex","propertyName","renderValue","addArrowEvent","dataLength","dataIndex1","getIndexName","dataIndex2","name","isLastItem","objectTypeValue","valueClass","valueElement","ignored","addClickEvent","createComma","newValue","color","newStringValue","style","objectTitle","arrayTitle","arrayTypeContents","removeChild","addValueClickEvent","display","index","largestValue","objectString","result","parse","e1","eval","e2","message","destroyElement","_public","refresh","elementId","refreshAll","renderAll","destroy","destroyAll","setConfiguration","configurationHasChanged","newInternalConfiguration","getIds","getVersion","addEventListener","window","$jsontree"],"mappings":"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","sourcesContent":[null]}
\ No newline at end of file
diff --git a/static/configvis/prism.css b/static/configvis/prism.css
new file mode 100644
index 00000000..5d87cd19
--- /dev/null
+++ b/static/configvis/prism.css
@@ -0,0 +1,174 @@
+/* PrismJS 1.29.0
+https://prismjs.com/download.html#themes=prism-twilight&languages=markup+yaml&plugins=line-numbers */
+code[class*='language-'],
+pre[class*='language-'] {
+ color: #fff;
+ background: 0 0;
+ font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
+ font-size: 1em;
+ text-align: left;
+ text-shadow: 0 -0.1em 0.2em #000;
+ white-space: pre;
+ word-spacing: normal;
+ word-break: normal;
+ word-wrap: normal;
+ line-height: 1.5;
+ -moz-tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
+ -webkit-hyphens: none;
+ -moz-hyphens: none;
+ -ms-hyphens: none;
+ hyphens: none;
+}
+:not(pre) > code[class*='language-'],
+pre[class*='language-'] {
+ background: #141414;
+}
+pre[class*='language-'] {
+ border-radius: 0.5em;
+ border: 0.3em solid #545454;
+ box-shadow: 1px 1px 0.5em #000 inset;
+ margin: 0.5em 0;
+ overflow: auto;
+ padding: 1em;
+}
+pre[class*='language-']::-moz-selection {
+ background: #27292a;
+}
+pre[class*='language-']::selection {
+ background: #27292a;
+}
+code[class*='language-'] ::-moz-selection,
+code[class*='language-']::-moz-selection,
+pre[class*='language-'] ::-moz-selection,
+pre[class*='language-']::-moz-selection {
+ text-shadow: none;
+ background: hsla(0, 0%, 93%, 0.15);
+}
+code[class*='language-'] ::selection,
+code[class*='language-']::selection,
+pre[class*='language-'] ::selection,
+pre[class*='language-']::selection {
+ text-shadow: none;
+ background: hsla(0, 0%, 93%, 0.15);
+}
+:not(pre) > code[class*='language-'] {
+ border-radius: 0.3em;
+ border: 0.13em solid #545454;
+ box-shadow: 1px 1px 0.3em -0.1em #000 inset;
+ padding: 0.15em 0.2em 0.05em;
+ white-space: normal;
+}
+.token.cdata,
+.token.comment,
+.token.doctype,
+.token.prolog {
+ color: #777;
+}
+.token.punctuation {
+ opacity: 0.7;
+}
+.token.namespace {
+ opacity: 0.7;
+}
+.token.boolean,
+.token.deleted,
+.token.number,
+.token.tag {
+ color: #ce6849;
+}
+.token.builtin,
+.token.constant,
+.token.keyword,
+.token.property,
+.token.selector,
+.token.symbol {
+ color: #f9ed99;
+}
+.language-css .token.string,
+.style .token.string,
+.token.attr-name,
+.token.attr-value,
+.token.char,
+.token.entity,
+.token.inserted,
+.token.operator,
+.token.string,
+.token.url,
+.token.variable {
+ color: #909e6a;
+}
+.token.atrule {
+ color: #7385a5;
+}
+.token.important,
+.token.regex {
+ color: #e8c062;
+}
+.token.bold,
+.token.important {
+ font-weight: 700;
+}
+.token.italic {
+ font-style: italic;
+}
+.token.entity {
+ cursor: help;
+}
+.language-markup .token.attr-name,
+.language-markup .token.punctuation,
+.language-markup .token.tag {
+ color: #ac885c;
+}
+.token {
+ position: relative;
+ z-index: 1;
+}
+.line-highlight.line-highlight {
+ background: hsla(0, 0%, 33%, 0.25);
+ background: linear-gradient(to right, hsla(0, 0%, 33%, 0.1) 70%, hsla(0, 0%, 33%, 0));
+ border-bottom: 1px dashed #545454;
+ border-top: 1px dashed #545454;
+ margin-top: 0.75em;
+ z-index: 0;
+}
+.line-highlight.line-highlight:before,
+.line-highlight.line-highlight[data-end]:after {
+ background-color: #8693a6;
+ color: #f4f1ef;
+}
+pre[class*='language-'].line-numbers {
+ position: relative;
+ padding-left: 3.8em;
+ counter-reset: linenumber;
+}
+pre[class*='language-'].line-numbers > code {
+ position: relative;
+ white-space: inherit;
+}
+.line-numbers .line-numbers-rows {
+ position: absolute;
+ pointer-events: none;
+ top: 0;
+ font-size: 100%;
+ left: -3.8em;
+ width: 3em;
+ letter-spacing: -1px;
+ border-right: 1px solid #999;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+.line-numbers-rows > span {
+ display: block;
+ counter-increment: linenumber;
+}
+.line-numbers-rows > span:before {
+ content: counter(linenumber);
+ color: #999;
+ display: block;
+ padding-right: 0.8em;
+ text-align: right;
+}
diff --git a/static/configvis/prism.js b/static/configvis/prism.js
new file mode 100644
index 00000000..4b96b4a7
--- /dev/null
+++ b/static/configvis/prism.js
@@ -0,0 +1,625 @@
+/* PrismJS 1.29.0
+https://prismjs.com/download.html#themes=prism-twilight&languages=markup+yaml&plugins=line-numbers */
+var _self =
+ 'undefined' != typeof window ? window : 'undefined' != typeof WorkerGlobalScope && self instanceof WorkerGlobalScope ? self : {},
+ Prism = (function (e) {
+ var n = /(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,
+ t = 0,
+ r = {},
+ a = {
+ manual: e.Prism && e.Prism.manual,
+ disableWorkerMessageHandler: e.Prism && e.Prism.disableWorkerMessageHandler,
+ util: {
+ encode: function e(n) {
+ return n instanceof i
+ ? new i(n.type, e(n.content), n.alias)
+ : Array.isArray(n)
+ ? n.map(e)
+ : n
+ .replace(/&/g, '&')
+ .replace(/= g.reach);
+ A += w.value.length, w = w.next
+ ) {
+ var E = w.value;
+ if (n.length > e.length) return;
+ if (!(E instanceof i)) {
+ var P,
+ L = 1;
+ if (y) {
+ if (!(P = l(b, A, e, m)) || P.index >= e.length) break;
+ var S = P.index,
+ O = P.index + P[0].length,
+ j = A;
+ for (j += w.value.length; S >= j; ) j += (w = w.next).value.length;
+ if (((A = j -= w.value.length), w.value instanceof i)) continue;
+ for (var C = w; C !== n.tail && (j < O || 'string' == typeof C.value); C = C.next)
+ L++, (j += C.value.length);
+ L--, (E = e.slice(A, j)), (P.index -= A);
+ } else if (!(P = l(b, 0, E, m))) continue;
+ S = P.index;
+ var N = P[0],
+ _ = E.slice(0, S),
+ M = E.slice(S + N.length),
+ W = A + E.length;
+ g && W > g.reach && (g.reach = W);
+ var z = w.prev;
+ if (
+ (_ && ((z = u(n, z, _)), (A += _.length)),
+ c(n, z, L),
+ (w = u(n, z, new i(f, p ? a.tokenize(N, p) : N, k, N))),
+ M && u(n, w, M),
+ L > 1)
+ ) {
+ var I = { cause: f + ',' + d, reach: W };
+ o(e, n, t, w.prev, A, I), g && I.reach > g.reach && (g.reach = I.reach);
+ }
+ }
+ }
+ }
+ }
+ }
+ function s() {
+ var e = { value: null, prev: null, next: null },
+ n = { value: null, prev: e, next: null };
+ (e.next = n), (this.head = e), (this.tail = n), (this.length = 0);
+ }
+ function u(e, n, t) {
+ var r = n.next,
+ a = { value: t, prev: n, next: r };
+ return (n.next = a), (r.prev = a), e.length++, a;
+ }
+ function c(e, n, t) {
+ for (var r = n.next, a = 0; a < t && r !== e.tail; a++) r = r.next;
+ (n.next = r), (r.prev = n), (e.length -= a);
+ }
+ if (
+ ((e.Prism = a),
+ (i.stringify = function e(n, t) {
+ if ('string' == typeof n) return n;
+ if (Array.isArray(n)) {
+ var r = '';
+ return (
+ n.forEach(function (n) {
+ r += e(n, t);
+ }),
+ r
+ );
+ }
+ var i = { type: n.type, content: e(n.content, t), tag: 'span', classes: ['token', n.type], attributes: {}, language: t },
+ l = n.alias;
+ l && (Array.isArray(l) ? Array.prototype.push.apply(i.classes, l) : i.classes.push(l)), a.hooks.run('wrap', i);
+ var o = '';
+ for (var s in i.attributes) o += ' ' + s + '="' + (i.attributes[s] || '').replace(/"/g, '"') + '"';
+ return '<' + i.tag + ' class="' + i.classes.join(' ') + '"' + o + '>' + i.content + '' + i.tag + '>';
+ }),
+ !e.document)
+ )
+ return e.addEventListener
+ ? (a.disableWorkerMessageHandler ||
+ e.addEventListener(
+ 'message',
+ function (n) {
+ var t = JSON.parse(n.data),
+ r = t.language,
+ i = t.code,
+ l = t.immediateClose;
+ e.postMessage(a.highlight(i, a.languages[r], r)), l && e.close();
+ },
+ !1,
+ ),
+ a)
+ : a;
+ var g = a.util.currentScript();
+ function f() {
+ a.manual || a.highlightAll();
+ }
+ if ((g && ((a.filename = g.src), g.hasAttribute('data-manual') && (a.manual = !0)), !a.manual)) {
+ var h = document.readyState;
+ 'loading' === h || ('interactive' === h && g && g.defer)
+ ? document.addEventListener('DOMContentLoaded', f)
+ : window.requestAnimationFrame
+ ? window.requestAnimationFrame(f)
+ : window.setTimeout(f, 16);
+ }
+ return a;
+ })(_self);
+'undefined' != typeof module && module.exports && (module.exports = Prism), 'undefined' != typeof global && (global.Prism = Prism);
+(Prism.languages.markup = {
+ comment: { pattern: //, greedy: !0 },
+ prolog: { pattern: /<\?[\s\S]+?\?>/, greedy: !0 },
+ doctype: {
+ pattern: /"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,
+ greedy: !0,
+ inside: {
+ 'internal-subset': { pattern: /(^[^\[]*\[)[\s\S]+(?=\]>$)/, lookbehind: !0, greedy: !0, inside: null },
+ string: { pattern: /"[^"]*"|'[^']*'/, greedy: !0 },
+ punctuation: /^$|[[\]]/,
+ 'doctype-tag': /^DOCTYPE/i,
+ name: /[^\s<>'"]+/,
+ },
+ },
+ cdata: { pattern: //i, greedy: !0 },
+ tag: {
+ pattern: /<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,
+ greedy: !0,
+ inside: {
+ tag: { pattern: /^<\/?[^\s>\/]+/, inside: { punctuation: /^<\/?/, namespace: /^[^\s>\/:]+:/ } },
+ 'special-attr': [],
+ 'attr-value': {
+ pattern: /=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,
+ inside: {
+ punctuation: [
+ { pattern: /^=/, alias: 'attr-equals' },
+ { pattern: /^(\s*)["']|["']$/, lookbehind: !0 },
+ ],
+ },
+ },
+ punctuation: /\/?>/,
+ 'attr-name': { pattern: /[^\s>\/]+/, inside: { namespace: /^[^\s>\/:]+:/ } },
+ },
+ },
+ entity: [{ pattern: /&[\da-z]{1,8};/i, alias: 'named-entity' }, /?[\da-f]{1,8};/i],
+}),
+ (Prism.languages.markup.tag.inside['attr-value'].inside.entity = Prism.languages.markup.entity),
+ (Prism.languages.markup.doctype.inside['internal-subset'].inside = Prism.languages.markup),
+ Prism.hooks.add('wrap', function (a) {
+ 'entity' === a.type && (a.attributes.title = a.content.replace(/&/, '&'));
+ }),
+ Object.defineProperty(Prism.languages.markup.tag, 'addInlined', {
+ value: function (a, e) {
+ var s = {};
+ (s['language-' + e] = { pattern: /(^$)/i, lookbehind: !0, inside: Prism.languages[e] }),
+ (s.cdata = /^$/i);
+ var t = { 'included-cdata': { pattern: //i, inside: s } };
+ t['language-' + e] = { pattern: /[\s\S]+/, inside: Prism.languages[e] };
+ var n = {};
+ (n[a] = {
+ pattern: RegExp(
+ '(<__[^>]*>)(?:))*\\]\\]>|(?!)'.replace(
+ /__/g,
+ function () {
+ return a;
+ },
+ ),
+ 'i',
+ ),
+ lookbehind: !0,
+ greedy: !0,
+ inside: t,
+ }),
+ Prism.languages.insertBefore('markup', 'cdata', n);
+ },
+ }),
+ Object.defineProperty(Prism.languages.markup.tag, 'addAttribute', {
+ value: function (a, e) {
+ Prism.languages.markup.tag.inside['special-attr'].push({
+ pattern: RegExp('(^|["\'\\s])(?:' + a + ')\\s*=\\s*(?:"[^"]*"|\'[^\']*\'|[^\\s\'">=]+(?=[\\s>]))', 'i'),
+ lookbehind: !0,
+ inside: {
+ 'attr-name': /^[^\s=]+/,
+ 'attr-value': {
+ pattern: /=[\s\S]+/,
+ inside: {
+ value: {
+ pattern: /(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,
+ lookbehind: !0,
+ alias: [e, 'language-' + e],
+ inside: Prism.languages[e],
+ },
+ punctuation: [{ pattern: /^=/, alias: 'attr-equals' }, /"|'/],
+ },
+ },
+ },
+ });
+ },
+ }),
+ (Prism.languages.html = Prism.languages.markup),
+ (Prism.languages.mathml = Prism.languages.markup),
+ (Prism.languages.svg = Prism.languages.markup),
+ (Prism.languages.xml = Prism.languages.extend('markup', {})),
+ (Prism.languages.ssml = Prism.languages.xml),
+ (Prism.languages.atom = Prism.languages.xml),
+ (Prism.languages.rss = Prism.languages.xml);
+!(function (e) {
+ var n = /[*&][^\s[\]{},]+/,
+ r = /!(?:<[\w\-%#;/?:@&=+$,.!~*'()[\]]+>|(?:[a-zA-Z\d-]*!)?[\w\-%#;/?:@&=+$.~*'()]+)?/,
+ t = '(?:' + r.source + '(?:[ \t]+' + n.source + ')?|' + n.source + '(?:[ \t]+' + r.source + ')?)',
+ a =
+ '(?:[^\\s\\x00-\\x08\\x0e-\\x1f!"#%&\'*,\\-:>?@[\\]`{|}\\x7f-\\x84\\x86-\\x9f\\ud800-\\udfff\\ufffe\\uffff]|[?:-])(?:[ \t]*(?:(?![#:])|:))*'.replace(
+ //g,
+ function () {
+ return '[^\\s\\x00-\\x08\\x0e-\\x1f,[\\]{}\\x7f-\\x84\\x86-\\x9f\\ud800-\\udfff\\ufffe\\uffff]';
+ },
+ ),
+ d = '"(?:[^"\\\\\r\n]|\\\\.)*"|\'(?:[^\'\\\\\r\n]|\\\\.)*\'';
+ function o(e, n) {
+ n = (n || '').replace(/m/g, '') + 'm';
+ var r = '([:\\-,[{]\\s*(?:\\s<>[ \t]+)?)(?:<>)(?=[ \t]*(?:$|,|\\]|\\}|(?:[\r\n]\\s*)?#))'
+ .replace(/<>/g, function () {
+ return t;
+ })
+ .replace(/<>/g, function () {
+ return e;
+ });
+ return RegExp(r, n);
+ }
+ (e.languages.yaml = {
+ scalar: {
+ pattern: RegExp(
+ '([\\-:]\\s*(?:\\s<>[ \t]+)?[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)\\S[^\r\n]*(?:\\2[^\r\n]+)*)'.replace(
+ /<>/g,
+ function () {
+ return t;
+ },
+ ),
+ ),
+ lookbehind: !0,
+ alias: 'string',
+ },
+ comment: /#.*/,
+ key: {
+ pattern: RegExp(
+ '((?:^|[:\\-,[{\r\n?])[ \t]*(?:<>[ \t]+)?)<>(?=\\s*:\\s)'
+ .replace(/<>/g, function () {
+ return t;
+ })
+ .replace(/<>/g, function () {
+ return '(?:' + a + '|' + d + ')';
+ }),
+ ),
+ lookbehind: !0,
+ greedy: !0,
+ alias: 'atrule',
+ },
+ directive: { pattern: /(^[ \t]*)%.+/m, lookbehind: !0, alias: 'important' },
+ datetime: {
+ pattern: o(
+ '\\d{4}-\\d\\d?-\\d\\d?(?:[tT]|[ \t]+)\\d\\d?:\\d{2}:\\d{2}(?:\\.\\d*)?(?:[ \t]*(?:Z|[-+]\\d\\d?(?::\\d{2})?))?|\\d{4}-\\d{2}-\\d{2}|\\d\\d?:\\d{2}(?::\\d{2}(?:\\.\\d*)?)?',
+ ),
+ lookbehind: !0,
+ alias: 'number',
+ },
+ boolean: { pattern: o('false|true', 'i'), lookbehind: !0, alias: 'important' },
+ null: { pattern: o('null|~', 'i'), lookbehind: !0, alias: 'important' },
+ string: { pattern: o(d), lookbehind: !0, greedy: !0 },
+ number: {
+ pattern: o('[+-]?(?:0x[\\da-f]+|0o[0-7]+|(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:e[+-]?\\d+)?|\\.inf|\\.nan)', 'i'),
+ lookbehind: !0,
+ },
+ tag: r,
+ important: n,
+ punctuation: /---|[:[\]{}\-,|>?]|\.\.\./,
+ }),
+ (e.languages.yml = e.languages.yaml);
+})(Prism);
+!(function () {
+ if ('undefined' != typeof Prism && 'undefined' != typeof document) {
+ var e = 'line-numbers',
+ n = /\n(?!$)/g,
+ t = (Prism.plugins.lineNumbers = {
+ getLine: function (n, t) {
+ if ('PRE' === n.tagName && n.classList.contains(e)) {
+ var i = n.querySelector('.line-numbers-rows');
+ if (i) {
+ var r = parseInt(n.getAttribute('data-start'), 10) || 1,
+ s = r + (i.children.length - 1);
+ t < r && (t = r), t > s && (t = s);
+ var l = t - r;
+ return i.children[l];
+ }
+ }
+ },
+ resize: function (e) {
+ r([e]);
+ },
+ assumeViewportIndependence: !0,
+ }),
+ i = void 0;
+ window.addEventListener('resize', function () {
+ (t.assumeViewportIndependence && i === window.innerWidth) ||
+ ((i = window.innerWidth), r(Array.prototype.slice.call(document.querySelectorAll('pre.line-numbers'))));
+ }),
+ Prism.hooks.add('complete', function (t) {
+ if (t.code) {
+ var i = t.element,
+ s = i.parentNode;
+ if (s && /pre/i.test(s.nodeName) && !i.querySelector('.line-numbers-rows') && Prism.util.isActive(i, e)) {
+ i.classList.remove(e), s.classList.add(e);
+ var l,
+ o = t.code.match(n),
+ a = o ? o.length + 1 : 1,
+ u = new Array(a + 1).join('');
+ (l = document.createElement('span')).setAttribute('aria-hidden', 'true'),
+ (l.className = 'line-numbers-rows'),
+ (l.innerHTML = u),
+ s.hasAttribute('data-start') &&
+ (s.style.counterReset = 'linenumber ' + (parseInt(s.getAttribute('data-start'), 10) - 1)),
+ t.element.appendChild(l),
+ r([s]),
+ Prism.hooks.run('line-numbers', t);
+ }
+ }
+ }),
+ Prism.hooks.add('line-numbers', function (e) {
+ (e.plugins = e.plugins || {}), (e.plugins.lineNumbers = !0);
+ });
+ }
+ function r(e) {
+ if (
+ 0 !=
+ (e = e.filter(function (e) {
+ var n,
+ t = ((n = e), n ? (window.getComputedStyle ? getComputedStyle(n) : n.currentStyle || null) : null)['white-space'];
+ return 'pre-wrap' === t || 'pre-line' === t;
+ })).length
+ ) {
+ var t = e
+ .map(function (e) {
+ var t = e.querySelector('code'),
+ i = e.querySelector('.line-numbers-rows');
+ if (t && i) {
+ var r = e.querySelector('.line-numbers-sizer'),
+ s = t.textContent.split(n);
+ r || (((r = document.createElement('span')).className = 'line-numbers-sizer'), t.appendChild(r)),
+ (r.innerHTML = '0'),
+ (r.style.display = 'block');
+ var l = r.getBoundingClientRect().height;
+ return (r.innerHTML = ''), { element: e, lines: s, lineHeights: [], oneLinerHeight: l, sizer: r };
+ }
+ })
+ .filter(Boolean);
+ t.forEach(function (e) {
+ var n = e.sizer,
+ t = e.lines,
+ i = e.lineHeights,
+ r = e.oneLinerHeight;
+ (i[t.length - 1] = void 0),
+ t.forEach(function (e, t) {
+ if (e && e.length > 1) {
+ var s = n.appendChild(document.createElement('span'));
+ (s.style.display = 'block'), (s.textContent = e);
+ } else i[t] = r;
+ });
+ }),
+ t.forEach(function (e) {
+ for (var n = e.sizer, t = e.lineHeights, i = 0, r = 0; r < t.length; r++)
+ void 0 === t[r] && (t[r] = n.children[i++].getBoundingClientRect().height);
+ }),
+ t.forEach(function (e) {
+ var n = e.sizer,
+ t = e.element.querySelector('.line-numbers-rows');
+ (n.style.display = 'none'),
+ (n.innerHTML = ''),
+ e.lineHeights.forEach(function (e, n) {
+ t.children[n].style.height = e + 'px';
+ });
+ });
+ }
+ }
+})();
diff --git a/static/logo.svg b/static/logo.svg
new file mode 100644
index 00000000..2e809b42
--- /dev/null
+++ b/static/logo.svg
@@ -0,0 +1,7 @@
+
+
+