commit b852746352c31bbfed85d21f17cca03ce27f73a4 Author: Kevin Baensch Date: Thu Nov 3 11:05:44 2022 +0100 Init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ee0d57b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +dist/ +node_modules/ +.log/ +Config/hosts* diff --git a/Config/Caddyfile b/Config/Caddyfile new file mode 100644 index 0000000..dd8775d --- /dev/null +++ b/Config/Caddyfile @@ -0,0 +1,26 @@ +{ + "admin": {▾ + "disabled": false, + "listen": "", + "enforce_origin": false, + "origins": [""], + "config": { + "persist": false, + "load": {•••} + }, + "identity": {▾ + "identifiers": [""], + "issuers": [{•••}] + }, + "remote": { + "listen": "", + "access_control": [{ + "public_keys": [""], + "permissions": [{ + "paths": [""], + "methods": [""] + }] + }] + } + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..64eb5cb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,923 @@ +{ + "name": "reverseproxy", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "reverseproxy", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "daemon": "^1.1.0", + "node-docker-api": "^1.1.22" + }, + "devDependencies": { + "@types/node": "^18.8.4", + "typescript": "^4.8.3", + "typescript-language-server": "^1.2.0" + } + }, + "node_modules/@types/node": { + "version": "18.8.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.4.tgz", + "integrity": "sha512-WdlVphvfR/GJCLEMbNA8lJ0lhFNBj4SW3O+O5/cEGw9oYrv0al9zTwuQsq+myDUXgNx2jgBynoVgZ2MMJ6pbow==", + "dev": true + }, + "node_modules/commander": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz", + "integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "dev": true, + "dependencies": { + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/daemon": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/daemon/-/daemon-1.1.0.tgz", + "integrity": "sha512-1vX9YVcP21gt12nSD3SQRC/uPU7fyA6M8qyClTBIFuiRWoylFn57PwXhjBAqRl085bZAje7sILhZU48qcS9SWw==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/docker-modem": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-0.3.7.tgz", + "integrity": "sha512-4Xn4ZVtc/2DEFtxY04lOVeF7yvxwXGVo0sN8FKRBnLhBcwQ78Hb56j+Z5yAXXUhoweVhzGeBeGWahS+af0/mcg==", + "dependencies": { + "debug": "^2.6.0", + "JSONStream": "0.10.0", + "readable-stream": "~1.0.26-4", + "split-ca": "^1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", + "integrity": "sha512-fw7Q/8gFR8iSekUi9I+HqWIap6mywuoe7hQIg3buTVjuZgALKj4HAmm0X6f+TaL4c9NJbvyFQdaI2ppr5p6dnQ==", + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.10.0.tgz", + "integrity": "sha512-8XbSFFd43EG+1thjLNFIzCBlwXti0yKa7L+ak/f0T/pkC+31b7G41DXL/JzYpAoYWZ2eCPiu4IIqzijM8N0a/w==", + "dependencies": { + "jsonparse": "0.0.5", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "index.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/locate-path": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.1.1.tgz", + "integrity": "sha512-vJXaRMJgRVD3+cUZs3Mncj2mxpt5mP0EmNOsxRSZRMlbqjvxzDEOIUWXGmavo0ZC9+tNZCBLQ66reA11nbpHZg==", + "dev": true, + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/node-docker-api": { + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/node-docker-api/-/node-docker-api-1.1.22.tgz", + "integrity": "sha512-8xfOiuLDJQw+l58i66lUNQhRhS5fAExqQbLolmyqMucrsDON7k7eLMIHphcBwwB7utwCHCQkcp73gSAmzSiAiw==", + "dependencies": { + "docker-modem": "^0.3.1", + "memorystream": "^0.3.1" + } + }, + "node_modules/p-debounce": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-debounce/-/p-debounce-4.0.0.tgz", + "integrity": "sha512-4Ispi9I9qYGO4lueiLDhe4q4iK5ERK8reLsuzH6BPaXn53EGaua8H66PXIFGrW897hwjXp+pVLrm/DLxN0RF0A==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-4.0.0.tgz", + "integrity": "sha512-N4zdA4sfOe6yCv+ulPCmpnIBQ5I60xfhDr1otdBBhKte9QtEf3bhfrfkW7dTb+IQ0iEx4ZDzas0kc1o5rdWpYg==", + "dev": true, + "dependencies": { + "find-up": "^6.2.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/split-ca": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", + "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==" + }, + "node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, + "node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tempy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.0.0.tgz", + "integrity": "sha512-B2I9X7+o2wOaW4r/CWMkpOO9mdiTRCxXNgob6iGvPmfPWgH/KyUD6Uy5crtWBxIBe3YrNZKR2lSzv1JJKWD4vA==", + "dev": true, + "dependencies": { + "is-stream": "^3.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^2.12.2", + "unique-string": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, + "node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", + "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/typescript-language-server": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/typescript-language-server/-/typescript-language-server-1.2.0.tgz", + "integrity": "sha512-Ahxko4dKgOY/pGTMdzeVw/NTIZC4z33OqEs7xlR3VEV36IZX12OfJ8uwXSbmTJlCh1QQ12dpQgwUuNvSfbWB4w==", + "dev": true, + "dependencies": { + "commander": "^9.4.0", + "deepmerge": "^4.2.2", + "fs-extra": "^10.1.0", + "p-debounce": "^4.0.0", + "pkg-up": "^4.0.0", + "semver": "^7.3.7", + "tempy": "^3.0.0", + "vscode-languageserver": "^8.0.2", + "vscode-languageserver-protocol": "^3.17.2", + "vscode-languageserver-textdocument": "1.0.7", + "vscode-uri": "^3.0.3", + "which": "^2.0.2" + }, + "bin": { + "typescript-language-server": "lib/cli.js" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "dev": true, + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2.tgz", + "integrity": "sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-8.0.2.tgz", + "integrity": "sha512-bpEt2ggPxKzsAOZlXmCJ50bV7VrxwCS5BI4+egUmure/oI/t4OlFzi/YNtVvY24A2UDOZAgwFGgnZPwqSJubkA==", + "dev": true, + "dependencies": { + "vscode-languageserver-protocol": "3.17.2" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.2.tgz", + "integrity": "sha512-8kYisQ3z/SQ2kyjlNeQxbkkTNmVFoQCqkmGrzLH6A9ecPlgTbp3wDTnUNqaUxYr4vlAcloxx8zwy7G5WdguYNg==", + "dev": true, + "dependencies": { + "vscode-jsonrpc": "8.0.2", + "vscode-languageserver-types": "3.17.2" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.7.tgz", + "integrity": "sha512-bFJH7UQxlXT8kKeyiyu41r22jCZXG8kuuVVA33OEJn1diWOZK5n8zBSPZFHVBOu8kXZ6h0LIRhf5UnCo61J4Hg==", + "dev": true + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz", + "integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA==", + "dev": true + }, + "node_modules/vscode-uri": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.4.tgz", + "integrity": "sha512-aEmKD6H8Sg8gaQAUrnadG0BMeWXtiWhRsj1a94n2FYsMkDpgnK7BRVzZjOUYIvkv2B+bp5Bmt4ImZCpYbnJwkg==", + "dev": true + }, + "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" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@types/node": { + "version": "18.8.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.4.tgz", + "integrity": "sha512-WdlVphvfR/GJCLEMbNA8lJ0lhFNBj4SW3O+O5/cEGw9oYrv0al9zTwuQsq+myDUXgNx2jgBynoVgZ2MMJ6pbow==", + "dev": true + }, + "commander": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz", + "integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==", + "dev": true + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "dev": true, + "requires": { + "type-fest": "^1.0.1" + }, + "dependencies": { + "type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true + } + } + }, + "daemon": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/daemon/-/daemon-1.1.0.tgz", + "integrity": "sha512-1vX9YVcP21gt12nSD3SQRC/uPU7fyA6M8qyClTBIFuiRWoylFn57PwXhjBAqRl085bZAje7sILhZU48qcS9SWw==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "docker-modem": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-0.3.7.tgz", + "integrity": "sha512-4Xn4ZVtc/2DEFtxY04lOVeF7yvxwXGVo0sN8FKRBnLhBcwQ78Hb56j+Z5yAXXUhoweVhzGeBeGWahS+af0/mcg==", + "requires": { + "debug": "^2.6.0", + "JSONStream": "0.10.0", + "readable-stream": "~1.0.26-4", + "split-ca": "^1.0.0" + } + }, + "find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "requires": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + } + }, + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonparse": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", + "integrity": "sha512-fw7Q/8gFR8iSekUi9I+HqWIap6mywuoe7hQIg3buTVjuZgALKj4HAmm0X6f+TaL4c9NJbvyFQdaI2ppr5p6dnQ==" + }, + "JSONStream": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.10.0.tgz", + "integrity": "sha512-8XbSFFd43EG+1thjLNFIzCBlwXti0yKa7L+ak/f0T/pkC+31b7G41DXL/JzYpAoYWZ2eCPiu4IIqzijM8N0a/w==", + "requires": { + "jsonparse": "0.0.5", + "through": ">=2.2.7 <3" + } + }, + "locate-path": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.1.1.tgz", + "integrity": "sha512-vJXaRMJgRVD3+cUZs3Mncj2mxpt5mP0EmNOsxRSZRMlbqjvxzDEOIUWXGmavo0ZC9+tNZCBLQ66reA11nbpHZg==", + "dev": true, + "requires": { + "p-locate": "^6.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node-docker-api": { + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/node-docker-api/-/node-docker-api-1.1.22.tgz", + "integrity": "sha512-8xfOiuLDJQw+l58i66lUNQhRhS5fAExqQbLolmyqMucrsDON7k7eLMIHphcBwwB7utwCHCQkcp73gSAmzSiAiw==", + "requires": { + "docker-modem": "^0.3.1", + "memorystream": "^0.3.1" + } + }, + "p-debounce": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-debounce/-/p-debounce-4.0.0.tgz", + "integrity": "sha512-4Ispi9I9qYGO4lueiLDhe4q4iK5ERK8reLsuzH6BPaXn53EGaua8H66PXIFGrW897hwjXp+pVLrm/DLxN0RF0A==", + "dev": true + }, + "p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "requires": { + "yocto-queue": "^1.0.0" + } + }, + "p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "requires": { + "p-limit": "^4.0.0" + } + }, + "path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true + }, + "pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-4.0.0.tgz", + "integrity": "sha512-N4zdA4sfOe6yCv+ulPCmpnIBQ5I60xfhDr1otdBBhKte9QtEf3bhfrfkW7dTb+IQ0iEx4ZDzas0kc1o5rdWpYg==", + "dev": true, + "requires": { + "find-up": "^6.2.0" + } + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "split-ca": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", + "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==" + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, + "temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "dev": true + }, + "tempy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.0.0.tgz", + "integrity": "sha512-B2I9X7+o2wOaW4r/CWMkpOO9mdiTRCxXNgob6iGvPmfPWgH/KyUD6Uy5crtWBxIBe3YrNZKR2lSzv1JJKWD4vA==", + "dev": true, + "requires": { + "is-stream": "^3.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^2.12.2", + "unique-string": "^3.0.0" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, + "type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true + }, + "typescript": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", + "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", + "dev": true + }, + "typescript-language-server": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/typescript-language-server/-/typescript-language-server-1.2.0.tgz", + "integrity": "sha512-Ahxko4dKgOY/pGTMdzeVw/NTIZC4z33OqEs7xlR3VEV36IZX12OfJ8uwXSbmTJlCh1QQ12dpQgwUuNvSfbWB4w==", + "dev": true, + "requires": { + "commander": "^9.4.0", + "deepmerge": "^4.2.2", + "fs-extra": "^10.1.0", + "p-debounce": "^4.0.0", + "pkg-up": "^4.0.0", + "semver": "^7.3.7", + "tempy": "^3.0.0", + "vscode-languageserver": "^8.0.2", + "vscode-languageserver-protocol": "^3.17.2", + "vscode-languageserver-textdocument": "1.0.7", + "vscode-uri": "^3.0.3", + "which": "^2.0.2" + } + }, + "unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "dev": true, + "requires": { + "crypto-random-string": "^4.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + }, + "vscode-jsonrpc": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2.tgz", + "integrity": "sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ==", + "dev": true + }, + "vscode-languageserver": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-8.0.2.tgz", + "integrity": "sha512-bpEt2ggPxKzsAOZlXmCJ50bV7VrxwCS5BI4+egUmure/oI/t4OlFzi/YNtVvY24A2UDOZAgwFGgnZPwqSJubkA==", + "dev": true, + "requires": { + "vscode-languageserver-protocol": "3.17.2" + } + }, + "vscode-languageserver-protocol": { + "version": "3.17.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.2.tgz", + "integrity": "sha512-8kYisQ3z/SQ2kyjlNeQxbkkTNmVFoQCqkmGrzLH6A9ecPlgTbp3wDTnUNqaUxYr4vlAcloxx8zwy7G5WdguYNg==", + "dev": true, + "requires": { + "vscode-jsonrpc": "8.0.2", + "vscode-languageserver-types": "3.17.2" + } + }, + "vscode-languageserver-textdocument": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.7.tgz", + "integrity": "sha512-bFJH7UQxlXT8kKeyiyu41r22jCZXG8kuuVVA33OEJn1diWOZK5n8zBSPZFHVBOu8kXZ6h0LIRhf5UnCo61J4Hg==", + "dev": true + }, + "vscode-languageserver-types": { + "version": "3.17.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz", + "integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA==", + "dev": true + }, + "vscode-uri": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.4.tgz", + "integrity": "sha512-aEmKD6H8Sg8gaQAUrnadG0BMeWXtiWhRsj1a94n2FYsMkDpgnK7BRVzZjOUYIvkv2B+bp5Bmt4ImZCpYbnJwkg==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..787fac3 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "reverseproxy", + "version": "1.0.0", + "description": "", + "main": "dist/reverseProxy.js", + "type": "module", + "scripts": { + "build": "tsc", + "proxy": "node dist/reverseProxy.js", + "proxy-daemon": "node dist/reverseProxyDaemon" + }, + "author": "Baensch, Kevin", + "license": "MIT", + "devDependencies": { + "@types/node": "^18.8.4", + "typescript": "^4.8.3", + "typescript-language-server": "^1.2.0" + }, + "dependencies": { + "daemon": "^1.1.0", + "node-docker-api": "^1.1.22" + } +} diff --git a/src/caddy.ts b/src/caddy.ts new file mode 100644 index 0000000..0b3a409 --- /dev/null +++ b/src/caddy.ts @@ -0,0 +1,44 @@ +import { Caddy, ContainerData } from "./types"; + +const caddyConfig: Caddy = { + admin: { + disabled: false, + config: { + persist: false, + }, + }, + apps: { + http: { + http_port: 80, + https_port: 443, + servers: {}, + }, + }, +}; + +// shorthand for use in functions +const servers = caddyConfig.apps!.http!.servers; + +export function removeServer(id: string) { + if (id in servers) { + delete servers[id]; + } +} + +export function addServer(container: ContainerData) { + servers[container.Id] = { + listen: container.HostNames, + }; +} + +export async function updateCaddy(): Promise { + const request = await fetch("google.com"); + await request.json(); + return; +} + +function mkCaddyHost(container: ContainerData, network: string) { + return { + listen: [], + }; +} diff --git a/src/const.ts b/src/const.ts new file mode 100644 index 0000000..6ba80ee --- /dev/null +++ b/src/const.ts @@ -0,0 +1,8 @@ +import * as path from "node:path"; + +const baseConfPath = "./Config"; +export const caddyConfPath = path.join(baseConfPath, "Caddyfile"); +export const hostConfPath = path.join(baseConfPath, "hosts"); +export const hostConfBakExt = "orig"; +export const dockerCaddyName = "caddy2"; +export const dockerNetwork = "proxy"; diff --git a/src/daemon.d.ts b/src/daemon.d.ts new file mode 100644 index 0000000..62b2d10 --- /dev/null +++ b/src/daemon.d.ts @@ -0,0 +1,12 @@ +declare module "daemon" { + export function daemon( + script: string, + args: string[], + opt?: { + stdout?: string, + stderr?: string, + env?: Record, + cwd?: string + } + ); +} diff --git a/src/docker.ts b/src/docker.ts new file mode 100644 index 0000000..7599780 --- /dev/null +++ b/src/docker.ts @@ -0,0 +1,168 @@ +import { Docker } from "node-docker-api"; +import { dockerCaddyName, dockerNetwork, caddyConfPath } from "./const.js"; +import { + CaddyState, + ContainerData, + ContainerNetwork, + ContainerOverride, + ImageOverride, + NetworkData, + NetworkOverride, +} from "./types.js"; + +export let networks: Record = {}; +export let containers: Record = {}; + +export async function update() { + const dockerSock = new Docker({ socketPath: "/var/run/docker.sock" }); + await Promise.all([ + updateNetworkData(dockerSock), + updateContainerData(dockerSock), + ]); + const caddyState = await getCaddyState(dockerSock); + if (caddyState !== "running") await startCaddy(dockerSock, caddyState); +} + +async function updateNetworkData(socket: Docker): Promise { + networks = {}; + + for (const network of (await socket.network.list({ + all: true, + })) as unknown as NetworkOverride[]) { + const data = network.data; + // Note: There is no guarantee that Network names don't overlap. + // There are lots of potential issues connected to non unique names so we will throw an error if this is not the case + // See the 'CheckDuplicate' argument here: + // https://docs.docker.com/engine/api/v1.41/#tag/Network/operation/NetworkCreate + if (data.Name in networks) + throw new Error(`ERROR: Duplicate docker network name: ${data.Name}`); + networks[data.Name] = { + Id: data.Id, + Name: data.Name, + Driver: data.Driver, + }; + } + return; +} + +async function updateContainerData(socket: Docker): Promise { + containers = {}; + // The Docker-Node Library is somewhat old and has inacurate/missing typing + for (const container of (await socket.container.list({ + all: true, + })) as unknown as ContainerOverride[]) { + const data = container.data; + + const networks = Object.values(data.NetworkSettings.Networks).map( + (netData: ContainerNetwork) => { + return [ + netData.NetworkID, + { + NetworkID: netData.NetworkID, + IPAddress: netData.IPAddress, + }, + ] as [string, ContainerNetwork]; + } + ); + containers[data.Id] = { + Id: data.Id, + Ports: Object.values(data.Ports).map((p) => `${p.PrivatePort}/${p.Type}`), + Name: data.Names[0] ?? "Unnamed", + HostNames: data.Names, + State: data.State, + Networks: Object.fromEntries(networks), + Labels: data.Labels, + }; + } + return; +} + +async function getCaddyState(socket: Docker): Promise { + const container = getContainerByName(`/${dockerCaddyName}`); + if (container) { + // Check if container is part of the configured network + const networkCheck = networks[dockerNetwork].Id in container.Networks; + return networkCheck ? container.State : "wrongnetwork"; + } + const hasCaddy = ( + (await socket.image.list()) as unknown as ImageOverride[] + ).some((image) => image?.data?.RepoTags?.includes("caddy:latest")); + return hasCaddy ? "nocontainer" : "noimage"; +} + +function getContainerByName(containerName: string) { + for (const container of Object.values(containers)) { + if (container.Name === containerName) return container; + } + return undefined; +} + +function getNetworkByName(networkName: string) { + for (const network of Object.values(networks)) { + if (network.Name === networkName) return network; + } + return undefined; +} + +async function ensureNetwork(socket: Docker) { + if (!Object.values(networks).some((net) => net.Name === dockerNetwork)) { + await socket.network.create({ Name: dockerNetwork }); + await updateNetworkData(socket); + } +} + +async function addContainerToNetwork( + socket: Docker, + container: ContainerData, + network: NetworkData +) { + if (!(network.Id in container.Networks)) { + await socket.network.get(network.Id).connect({ Container: container.Id }); + await updateContainerData(socket); + } +} + +async function startCaddy(socket: Docker, caddyState: CaddyState) { + await ensureNetwork(socket); + + switch (caddyState) { + case "noimage": + await socket.image.create({}, { fromImage: "caddy", tag: "latest" }); + // Create Container in shared network + case "nocontainer": + // Docker Documentation can be found here: + // https://docs.docker.com/engine/api/v1.41/#tag/Container/operation/ContainerCreate + await socket.container.create({ + name: dockerCaddyName, + network: dockerNetwork, + Image: "caddy", + HostConfig: { + PortBindings: { + "80": [{ HostPort: "80" }], + "443/tcp": [{ HostIp: "0.0.0.0", HostPort: "443" }], + "443/udp": [{ HostIp: "0.0.0.0", HostPort: "443" }], + "2019/tcp": [{ HostIp: "0.0.0.0", HostPort: "2019" }], + }, + Binds: [`${caddyConfPath}:/etc/caddy/Caddyfile`], + }, + }); + await updateContainerData(socket); + case "wrongnetwork": + await addContainerToNetwork( + socket, + getContainerByName(`/${dockerCaddyName}`)!, + getNetworkByName(dockerNetwork)! + ); + case "exited": + const id = getContainerByName(`/${dockerCaddyName}`)?.Id!; + await socket.container.get(id)?.start(); + } +} + +export async function stopCaddy() { + // Create fresh docker connection (mitigate possible exceptions with the original connection and can be called separately) + const dockerSock = new Docker({ socketPath: "/var/run/docker.sock" }); + const container = getContainerByName(`/${dockerCaddyName}`); + if (container?.State === "running") + await dockerSock.container.get(container.Id).stop(); +} diff --git a/src/hosts.ts b/src/hosts.ts new file mode 100644 index 0000000..9ed5dd8 --- /dev/null +++ b/src/hosts.ts @@ -0,0 +1,120 @@ +import { + accessSync, + constants, + copyFileSync, + existsSync, + readFileSync, + writeFileSync, +} from "node:fs"; +import { hostConfPath, hostConfBakExt } from "./const.js"; +import { HostData, ContainerData } from "./types.js"; + +let hasChanged = false; +const hostData: HostData = {}; +let initialHostData: HostData = {}; + +function checkFile(path: string) { + if (!existsSync(path)) + throw new Error(`No such file or directory: ${hostConfPath}`); + // Throws error for missing file permissions + accessSync(path, constants.R_OK | constants.W_OK); +} + +export function writeConfig(): boolean { + // prevent unnecessary rewrites + if (!hasChanged) return false; + const stringData = ( + Object.entries(hostData) as Array<[string, string[]]> + ).reduce((prev, [curAddr, curHosts]) => { + return `${prev}${curAddr} ${curHosts.join(" ")}\n`; + }, ""); + writeFileSync(hostConfPath, stringData); + hasChanged = false; + return true; +} + +// Read initial data from host config backup +function readConfig() { + const textContent = readFileSync(`${hostConfPath}.${hostConfBakExt}`, { + encoding: "utf8", + }) as string; + // NOTE: maybe properly detect EOL-Style if it causes problems + // using Unix for now + for (const line of textContent.split("\n")) { + //skip empty lines and comments + if (!line || line.startsWith("#")) continue; + const { address, hosts } = line.match(/^(?
[^\ ]*) (?.*)$/) + ?.groups ?? { address: "172.0.0.1", hosts: "localhost" }; + if (!(address in hostData)) hostData[address] = hosts.split(" "); + else hostData[address] = hostData[address].concat(hosts.split(" ")); + } + initialHostData = structuredClone(hostData); +} + +export function init() { + // Boilerplate init code + // Check permissions for host and backup file + // conditionally create backup file + checkFile(hostConfPath); + try { + checkFile(`${hostConfPath}.${hostConfBakExt}`); + } catch (err) { + if ((err as Error).message.startsWith("No such file or directory:")) + copyFileSync(hostConfPath, `${hostConfPath}.${hostConfBakExt}`); + else throw err; + } + + // load data + readConfig(); +} + +function addHost(hosts: string | string[], address = "127.0.0.1") { + const hostList = typeof hosts === "string" ? hosts.split(" ") : hosts; + if (!hostData[address]?.some((addr) => hostList.includes(addr))) { + hostData[address] = (hostData[address] ?? []).concat(hostList); + hasChanged = true; + } +} + +function removeHost(hosts: string | string[], address = "127.0.0.1") { + if (address in hostData) { + const hostList = typeof hosts === "string" ? hosts.split(" ") : hosts; + hostData[address] = hostData[address].filter( + (host) => !hostList.includes(host) + ); + if (hostData[address].length === 0) delete hostData[address]; + hasChanged = true; + } +} + +export function update(dockerHosts: ContainerData[], networkID: string) { + // Create copy of host list to diff for disconnected containers at the end + const removedHosts = structuredClone(hostData); + + // Keep all original hosts in list + for (const address of Object.keys(initialHostData)) { + removedHosts[address] = removedHosts[address].filter( + (hostName) => !initialHostData[address].includes(hostName) + ); + } + + // Add new containers + for (const container of dockerHosts) { + if (container.State !== "running" || !(networkID in container.Networks)) + continue; + const indexAt = removedHosts["127.0.0.1"]?.findIndex( + (host) => host === container.Name + ); + if (indexAt !== -1) { + removedHosts["127.0.0.1"].splice(indexAt, 1); + continue; + } + addHost(container.Name); + } + + // Remove Containers + for (const address of Object.keys(removedHosts)) { + if (removedHosts[address]?.length > 0) + removeHost(removedHosts[address], address); + } +} diff --git a/src/reverseProxy.ts b/src/reverseProxy.ts new file mode 100644 index 0000000..049feff --- /dev/null +++ b/src/reverseProxy.ts @@ -0,0 +1,43 @@ +import process from "node:process"; +import * as docker from "./docker.js"; +import * as hosts from "./hosts.js"; +import { dockerNetwork } from "./const.js"; + +let running = true; +const stopRunning = () => { + running = false; +}; + +// Signal handlers to terminate min loop +process.on("SIGTERM", stopRunning); +process.on("SIGINT", stopRunning); + +hosts.init(); +docker.update().then(() => { + hosts.writeConfig(); + console.log("Startup Done"); + idleMain(); +}); + +async function idleMain() { + // Main Idle Loop + // Checks Docker for changes every ten seconds + // SIGTERM and SIGINT signals can be processed during the timeout so the process can exit gracefully + let idleCount = 0; + const proxyNetworkID = docker.networks[dockerNetwork].Id; + + while (running) { + if (idleCount === 0) { + await docker.update(); + hosts.update(Object.values(docker.containers), proxyNetworkID); + if (hosts.writeConfig()) { + //update caddy + } + } + await new Promise((res) => setTimeout(res, 1000)); + idleCount = (idleCount + 1) % 10; + } + + // Cleanup Code goes here (docker shutdown) + await docker.stopCaddy(); +} diff --git a/src/reverseProxyDaemon.ts b/src/reverseProxyDaemon.ts new file mode 100644 index 0000000..69b8256 --- /dev/null +++ b/src/reverseProxyDaemon.ts @@ -0,0 +1,3 @@ +import { daemon } from "daemon"; + +daemon("dist/app.js", [], { cwd: process.cwd() }); diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..31cdb57 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,219 @@ +export type NetType = "Bridge" | "Host" | "none" | "null"; + +export interface NetworkData { + Id: string; + Name: string; + Driver: NetType; +} + +export interface ContainerData { + Id: string; + Name: string; + State: "exited" | "running"; + Networks: Record; + HostNames?: string[]; + Ports: string[]; + Labels: { + HostName?: string; + PortMap?: string[]; + }; +} + +export type ContainerNetwork = { + NetworkID: string; + IPAddress: string; +}; + +export type ContainerOverride = { + data: { + Id: string; + Names: string[]; + State: "exited" | "running"; + NetworkSettings: { + Networks: Record; + }; + Labels: Record; + Ports: { + IP?: string; + PrivatePort: number; + PublicPort?: number; + Type: "tcp" | "udp"; + }[]; + }; +}; + +export type NetworkOverride = { + data: { + Name: string; + Id: string; + Driver: NetType; + Scope: string; + }; +}; + +export type ImageOverride = { + data: { + Containers: number; + Created: number; + Id: string; + Labels: null | Record; + ParentId: string; + RepoDigests: string[]; + RepoTags: string[]; + SharedSize: number; + Size: number; + VirtualSize: number; + }; +}; + +export type CaddyState = + | "running" + | "exited" + | "nocontainer" + | "noimage" + | "wrongnetwork"; + +export interface CaddyHttpServer { + listen?: string[]; + // "listener_wrappers"?: [{•••}], + read_timeout?: number; + read_header_timeout?: number; + write_timeout?: number; + idle_timeout?: number; + keepalive_interval?: number; + max_header_bytes?: number; + routes?: { + group?: string; + // "match"?: [{••• }], + // "handle"?: [{••• }], + terminal?: boolean; + }[]; + errors?: { + routes?: [ + { + group?: string; + // "match"?: [{••• }], + // "handle"?: [{••• }], + terminal?: boolean; + } + ]; + }; + tls_connection_policies?: [ + { + // "match"?: {••• }, + certificate_selection?: { + serial_number?: [{}]; + subject_organization?: string[]; + public_key_algorithm?: number; + any_tag?: string[]; + all_tags?: string[]; + }; + cipher_suites?: string[]; + curves?: string[]; + alpn?: string[]; + protocol_min?: string; + protocol_max?: string; + client_authentication?: { + trusted_ca_certs?: string[]; + trusted_ca_certs_pem_files?: string[]; + trusted_leaf_certs?: string; + // "verifiers"?: [{••• }], + mode?: string; + }; + default_sni?: string; + insecure_secrets_log?: string; + } + ]; + automatic_https?: { + disable?: boolean; + disable_redirects?: boolean; + disable_certificates?: boolean; + skip?: string[]; + skip_certificates?: string[]; + ignore_loaded_certificates?: boolean; + }; + strict_sni_host?: boolean; + logs?: { + default_logger_name?: string; + logger_names?: Record; + skip_hosts?: string[]; + skip_unmapped_hosts?: boolean; + should_log_credentials?: boolean; + }; + protocols?: string[]; + metrics?: {}; +} + +export interface CaddyHttp { + http_port: number; + https_port?: number; + grace_period?: number; + shutdown_delay?: number; + servers: Record; +} + +export interface CaddyFileSystem { + module: "file_system"; + root: string; +} + +export interface Caddy { + admin?: { + /** If true, the admin endpoint will be completely disabled. */ + disabled?: boolean; + /** The address to which the admin endpoint's listener should bind itself. */ + listen?: string; + enforce_origin?: boolean; + origins?: string[]; + config?: { + persist?: boolean; + // "load"?: {••• } + }; + identity?: { + identifiers?: string[]; + // "issuers"?: [{••• }] + }; + remote?: { + listen?: string; + access_control?: [ + { + public_keys?: string[]; + permissions?: [ + { + paths?: string[]; + methods?: string[]; + } + ]; + } + ]; + }; + }; + logging?: { + sink?: { + // writer?: { } + }; + logs?: Record< + string, + { + // "writer"?: { }, + // "encoder"?: { }, + level?: string; + sampling?: { + interval?: number; + first?: number; + thereafter?: number; + }; + include?: string[]; + exclude?: string[]; + } + >; + }; + storage?: { + file_system?: CaddyFileSystem; + }; + apps?: { + http?: CaddyHttp; + }; +} + +export type HostData = Record; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..cfcd0f0 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "esnext", + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + // "module": "nodenext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "outDir": "dist" + }, + "include": [ + "src" + ] +}