diff --git a/Makefile b/Makefile index ff5b9c4ad..34064e799 100644 --- a/Makefile +++ b/Makefile @@ -160,6 +160,10 @@ wormhole-deb: results/$(BASENAME).deb @echo "Paste the following command into the shell of your start-os server:" @wormhole send results/$(BASENAME).deb 2>&1 | awk -Winteractive '/wormhole receive/ { printf "sudo /usr/lib/startos/scripts/chroot-and-upgrade '"'"'cd $$(mktemp -d) && wormhole receive --accept-file %s && apt-get install -y --reinstall ./$(BASENAME).deb'"'"'\n", $$3 }' +wormhole-cli: core/target/$(ARCH)-unknown-linux-musl/release/start-cli + @echo "Paste the following command into the shell of your start-os server:" + @wormhole send results/$(BASENAME).deb 2>&1 | awk -Winteractive '/wormhole receive/ { printf "sudo /usr/lib/startos/scripts/chroot-and-upgrade '"'"'cd $$(mktemp -d) && wormhole receive --accept-file %s && apt-get install -y --reinstall ./$(BASENAME).deb'"'"'\n", $$3 }' + update: $(ALL_TARGETS) @if [ -z "$(REMOTE)" ]; then >&2 echo "Must specify REMOTE" && false; fi $(call ssh,'sudo /usr/lib/startos/scripts/chroot-and-upgrade --create') diff --git a/container-runtime/deb-install.sh b/container-runtime/deb-install.sh index b439c6308..697bfd10e 100644 --- a/container-runtime/deb-install.sh +++ b/container-runtime/deb-install.sh @@ -6,7 +6,7 @@ mkdir -p /run/systemd/resolve echo "nameserver 8.8.8.8" > /run/systemd/resolve/stub-resolv.conf apt-get update -apt-get install -y curl rsync +apt-get install -y curl rsync qemu-user-static curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash source ~/.bashrc diff --git a/container-runtime/package-lock.json b/container-runtime/package-lock.json index 838dcb769..9b211c077 100644 --- a/container-runtime/package-lock.json +++ b/container-runtime/package-lock.json @@ -13,6 +13,7 @@ "esbuild-plugin-resolve": "^2.0.0", "filebrowser": "^1.0.0", "isomorphic-fetch": "^3.0.0", + "lodash": "^4.17.21", "node-fetch": "^3.1.0", "ts-matches": "^5.5.1", "tslib": "^2.5.3", @@ -33,11 +34,13 @@ "license": "MIT", "dependencies": { "isomorphic-fetch": "^3.0.0", + "lodash": "^4.17.21", "ts-matches": "^5.4.1" }, "devDependencies": { "@iarna/toml": "^2.2.5", "@types/jest": "^29.4.0", + "@types/lodash": "^4.17.5", "jest": "^29.4.3", "prettier": "^3.2.5", "ts-jest": "^29.0.5", @@ -49,14 +52,12 @@ }, "node_modules/@iarna/toml": { "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", - "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" + "license": "ISC" }, "node_modules/@mole-inc/bin-wrapper": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@mole-inc/bin-wrapper/-/bin-wrapper-8.0.1.tgz", - "integrity": "sha512-sTGoeZnjI8N4KS+sW2AN95gDBErhAguvkw/tWdCjeM8bvxpz5lqrnd0vOJABA1A+Ic3zED7PYoLP/RANLgVotA==", "dev": true, + "license": "MIT", "dependencies": { "bin-check": "^4.1.0", "bin-version-check": "^5.0.0", @@ -73,9 +74,8 @@ }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -86,18 +86,16 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -108,9 +106,8 @@ }, "node_modules/@sindresorhus/is": { "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -124,9 +121,8 @@ }, "node_modules/@swc/cli": { "version": "0.1.65", - "resolved": "https://registry.npmjs.org/@swc/cli/-/cli-0.1.65.tgz", - "integrity": "sha512-4NcgsvJVHhA7trDnMmkGLLvWMHu2kSy+qHx6QwRhhJhdiYdNUrhdp+ERxen73sYtaeEOYeLJcWrQ60nzKi6rpg==", "dev": true, + "license": "MIT", "dependencies": { "@mole-inc/bin-wrapper": "^8.0.1", "commander": "^7.1.0", @@ -155,14 +151,13 @@ } }, "node_modules/@swc/core": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.4.1.tgz", - "integrity": "sha512-3y+Y8js+e7BbM16iND+6Rcs3jdiL28q3iVtYsCviYSSpP2uUVKkp5sJnCY4pg8AaVvyN7CGQHO7gLEZQ5ByozQ==", + "version": "1.5.28", "dev": true, "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { - "@swc/counter": "^0.1.2", - "@swc/types": "^0.1.5" + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.8" }, "engines": { "node": ">=10" @@ -172,19 +167,19 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.4.1", - "@swc/core-darwin-x64": "1.4.1", - "@swc/core-linux-arm-gnueabihf": "1.4.1", - "@swc/core-linux-arm64-gnu": "1.4.1", - "@swc/core-linux-arm64-musl": "1.4.1", - "@swc/core-linux-x64-gnu": "1.4.1", - "@swc/core-linux-x64-musl": "1.4.1", - "@swc/core-win32-arm64-msvc": "1.4.1", - "@swc/core-win32-ia32-msvc": "1.4.1", - "@swc/core-win32-x64-msvc": "1.4.1" + "@swc/core-darwin-arm64": "1.5.28", + "@swc/core-darwin-x64": "1.5.28", + "@swc/core-linux-arm-gnueabihf": "1.5.28", + "@swc/core-linux-arm64-gnu": "1.5.28", + "@swc/core-linux-arm64-musl": "1.5.28", + "@swc/core-linux-x64-gnu": "1.5.28", + "@swc/core-linux-x64-musl": "1.5.28", + "@swc/core-win32-arm64-msvc": "1.5.28", + "@swc/core-win32-ia32-msvc": "1.5.28", + "@swc/core-win32-x64-msvc": "1.5.28" }, "peerDependencies": { - "@swc/helpers": "^0.5.0" + "@swc/helpers": "*" }, "peerDependenciesMeta": { "@swc/helpers": { @@ -192,94 +187,13 @@ } } }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.4.1.tgz", - "integrity": "sha512-ePyfx0348UbR4DOAW24TedeJbafnzha8liXFGuQ4bdXtEVXhLfPngprrxKrAddCuv42F9aTxydlF6+adD3FBhA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.4.1.tgz", - "integrity": "sha512-eLf4JSe6VkCMdDowjM8XNC5rO+BrgfbluEzAVtKR8L2HacNYukieumN7EzpYCi0uF1BYwu1ku6tLyG2r0VcGxA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.4.1.tgz", - "integrity": "sha512-K8VtTLWMw+rkN/jDC9o/Q9SMmzdiHwYo2CfgkwVT29NsGccwmNhCQx6XoYiPKyKGIFKt4tdQnJHKUFzxUqQVtQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.4.1.tgz", - "integrity": "sha512-0e8p4g0Bfkt8lkiWgcdiENH3RzkcqKtpRXIVNGOmVc0OBkvc2tpm2WTx/eoCnes2HpTT4CTtR3Zljj4knQ4Fvw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.4.1.tgz", - "integrity": "sha512-b/vWGQo2n7lZVUnSQ7NBq3Qrj85GrAPPiRbpqaIGwOytiFSk8VULFihbEUwDe0rXgY4LDm8z8wkgADZcLnmdUA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.4.1.tgz", - "integrity": "sha512-AFMQlvkKEdNi1Vk2GFTxxJzbICttBsOQaXa98kFTeWTnFFIyiIj2w7Sk8XRTEJ/AjF8ia8JPKb1zddBWr9+bEQ==", + "version": "1.5.28", "cpu": [ "x64" ], "dev": true, + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" @@ -289,13 +203,12 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.4.1.tgz", - "integrity": "sha512-QX2MxIECX1gfvUVZY+jk528/oFkS9MAl76e3ZRvG2KC/aKlCQL0KSzcTSm13mOxkDKS30EaGRDRQWNukGpMeRg==", + "version": "1.5.28", "cpu": [ "x64" ], "dev": true, + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" @@ -304,71 +217,23 @@ "node": ">=10" } }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.4.1.tgz", - "integrity": "sha512-OklkJYXXI/tntD2zaY8i3iZldpyDw5q+NAP3k9OlQ7wXXf37djRsHLV0NW4+ZNHBjE9xp2RsXJ0jlOJhfgGoFA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.4.1.tgz", - "integrity": "sha512-MBuc3/QfKX9FnLOU7iGN+6yHRTQaPQ9WskiC8s8JFiKQ+7I2p25tay2RplR9dIEEGgVAu6L7auv96LbNTh+FaA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.4.1.tgz", - "integrity": "sha512-lu4h4wFBb/bOK6N2MuZwg7TrEpwYXgpQf5R7ObNSXL65BwZ9BG8XRzD+dLJmALu8l5N08rP/TrpoKRoGT4WSxw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, "node_modules/@swc/counter": { "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/@swc/types": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", - "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==", - "dev": true + "version": "0.1.8", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", "dev": true, + "license": "MIT", "dependencies": { "defer-to-connect": "^2.0.0" }, @@ -378,15 +243,13 @@ }, "node_modules/@tokenizer/token": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/cacheable-request": { "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", "dev": true, + "license": "MIT", "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "^3.1.4", @@ -396,41 +259,36 @@ }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/keyv": { "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/node": { - "version": "20.11.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", - "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", + "version": "20.14.2", "dev": true, + "license": "MIT", "dependencies": { "undici-types": "~5.26.4" } }, "node_modules/@types/responselike": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", - "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/accepts": { "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" @@ -441,8 +299,6 @@ }, "node_modules/arch": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", "dev": true, "funding": [ { @@ -457,24 +313,22 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/array-flatten": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + "license": "MIT" }, "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 + "dev": true, + "license": "MIT" }, "node_modules/bin-check": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bin-check/-/bin-check-4.1.0.tgz", - "integrity": "sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==", "dev": true, + "license": "MIT", "dependencies": { "execa": "^0.7.0", "executable": "^4.1.0" @@ -485,9 +339,8 @@ }, "node_modules/bin-version": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/bin-version/-/bin-version-6.0.0.tgz", - "integrity": "sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw==", "dev": true, + "license": "MIT", "dependencies": { "execa": "^5.0.0", "find-versions": "^5.0.0" @@ -501,9 +354,8 @@ }, "node_modules/bin-version-check": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bin-version-check/-/bin-version-check-5.1.0.tgz", - "integrity": "sha512-bYsvMqJ8yNGILLz1KP9zKLzQ6YpljV3ln1gqhuLkUtyfGi3qXKGuK2p+U4NAvjVFzDFiBBtOpCOSFNuYYEGZ5g==", "dev": true, + "license": "MIT", "dependencies": { "bin-version": "^6.0.0", "semver": "^7.5.3", @@ -518,9 +370,8 @@ }, "node_modules/bin-version/node_modules/cross-spawn": { "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -532,9 +383,8 @@ }, "node_modules/bin-version/node_modules/execa": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -555,9 +405,8 @@ }, "node_modules/bin-version/node_modules/get-stream": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -567,9 +416,8 @@ }, "node_modules/bin-version/node_modules/is-stream": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -579,9 +427,8 @@ }, "node_modules/bin-version/node_modules/npm-run-path": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -591,18 +438,16 @@ }, "node_modules/bin-version/node_modules/path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/bin-version/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, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -612,18 +457,16 @@ }, "node_modules/bin-version/node_modules/shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/bin-version/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, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -635,12 +478,11 @@ } }, "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "version": "1.20.2", + "license": "MIT", "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -648,7 +490,7 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -659,20 +501,18 @@ }, "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==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", "dev": true, + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -680,26 +520,23 @@ }, "node_modules/bytes": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/cacheable-lookup": { "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.6.0" } }, "node_modules/cacheable-request": { "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", "dev": true, + "license": "MIT", "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", @@ -715,9 +552,8 @@ }, "node_modules/cacheable-request/node_modules/get-stream": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, + "license": "MIT", "dependencies": { "pump": "^3.0.0" }, @@ -730,8 +566,7 @@ }, "node_modules/call-bind": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -748,9 +583,8 @@ }, "node_modules/clone-response": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dev": true, + "license": "MIT", "dependencies": { "mimic-response": "^1.0.0" }, @@ -760,17 +594,15 @@ }, "node_modules/commander": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10" } }, "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" }, @@ -780,30 +612,26 @@ }, "node_modules/content-type": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.6.0", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/cookie-signature": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + "license": "MIT" }, "node_modules/cross-spawn": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", "dev": true, + "license": "MIT", "dependencies": { "lru-cache": "^4.0.1", "shebang-command": "^1.2.0", @@ -812,25 +640,22 @@ }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", "engines": { "node": ">= 12" } }, "node_modules/debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } }, "node_modules/decompress-response": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, + "license": "MIT", "dependencies": { "mimic-response": "^3.1.0" }, @@ -843,9 +668,8 @@ }, "node_modules/decompress-response/node_modules/mimic-response": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -855,17 +679,15 @@ }, "node_modules/defer-to-connect": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/define-data-property": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -880,16 +702,14 @@ }, "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/destroy": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" @@ -897,30 +717,26 @@ }, "node_modules/ee-first": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + "license": "MIT" }, "node_modules/encodeurl": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/end-of-stream": { "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, + "license": "MIT", "dependencies": { "once": "^1.4.0" } }, "node_modules/es-define-property": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.4" }, @@ -930,27 +746,23 @@ }, "node_modules/es-errors": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/esbuild-plugin-resolve": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/esbuild-plugin-resolve/-/esbuild-plugin-resolve-2.0.0.tgz", - "integrity": "sha512-eJy9B8yDW5X/J48eWtR1uVmv+DKfHvYYnrrcqQoe/nUkVHVOTZlJnSevkYyGOz6hI90t036Y5QIPDrGzmppxfg==" + "license": "MIT" }, "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": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -960,17 +772,15 @@ }, "node_modules/etag": { "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/execa": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==", "dev": true, + "license": "MIT", "dependencies": { "cross-spawn": "^5.0.1", "get-stream": "^3.0.0", @@ -986,9 +796,8 @@ }, "node_modules/executable": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", "dev": true, + "license": "MIT", "dependencies": { "pify": "^2.2.0" }, @@ -997,16 +806,15 @@ } }, "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "version": "4.19.2", + "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -1039,9 +847,8 @@ }, "node_modules/ext-list": { "version": "2.2.2", - "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", - "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", "dev": true, + "license": "MIT", "dependencies": { "mime-db": "^1.28.0" }, @@ -1051,9 +858,8 @@ }, "node_modules/ext-name": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", - "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", "dev": true, + "license": "MIT", "dependencies": { "ext-list": "^2.0.0", "sort-keys-length": "^1.0.0" @@ -1064,9 +870,8 @@ }, "node_modules/fast-glob": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -1080,17 +885,14 @@ }, "node_modules/fastq": { "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, "node_modules/fetch-blob": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", "funding": [ { "type": "github", @@ -1101,6 +903,7 @@ "url": "https://paypal.me/jimmywarting" } ], + "license": "MIT", "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" @@ -1111,9 +914,8 @@ }, "node_modules/file-type": { "version": "17.1.6", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-17.1.6.tgz", - "integrity": "sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==", "dev": true, + "license": "MIT", "dependencies": { "readable-web-to-node-stream": "^3.0.2", "strtok3": "^7.0.0-alpha.9", @@ -1128,8 +930,7 @@ }, "node_modules/filebrowser": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/filebrowser/-/filebrowser-1.0.0.tgz", - "integrity": "sha512-RRONYpCDzbmWPhBX43T4dE+ptqLznJ7lKfbMaZLChB2i2ZIdFXoqT9qZTi70Dpq6fnJHuvcdeiRqMIPZKhVgTQ==", + "license": "ISC", "dependencies": { "commander": "^2.9.0", "content-disposition": "^0.5.1", @@ -1138,14 +939,12 @@ }, "node_modules/filebrowser/node_modules/commander": { "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "license": "MIT" }, "node_modules/filename-reserved-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz", - "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -1155,9 +954,8 @@ }, "node_modules/filenamify": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-5.1.1.tgz", - "integrity": "sha512-M45CbrJLGACfrPOkrTp3j2EcO9OBkKUYME0eiqOCa7i2poaklU0jhlIaMlr8ijLorT0uLAzrn3qXOp5684CkfA==", "dev": true, + "license": "MIT", "dependencies": { "filename-reserved-regex": "^3.0.0", "strip-outer": "^2.0.0", @@ -1171,10 +969,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -1184,8 +981,7 @@ }, "node_modules/finalhandler": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "license": "MIT", "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", @@ -1201,9 +997,8 @@ }, "node_modules/find-versions": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-5.1.0.tgz", - "integrity": "sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==", "dev": true, + "license": "MIT", "dependencies": { "semver-regex": "^4.0.5" }, @@ -1216,8 +1011,7 @@ }, "node_modules/formdata-polyfill": { "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", "dependencies": { "fetch-blob": "^3.1.2" }, @@ -1227,32 +1021,28 @@ }, "node_modules/forwarded": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/fresh": { "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/function-bind": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/get-intrinsic": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", @@ -1269,18 +1059,16 @@ }, "node_modules/get-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/glob-parent": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -1290,8 +1078,7 @@ }, "node_modules/gopd": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "license": "MIT", "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -1301,9 +1088,8 @@ }, "node_modules/got": { "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", "dev": true, + "license": "MIT", "dependencies": { "@sindresorhus/is": "^4.0.0", "@szmarczak/http-timer": "^4.0.5", @@ -1326,8 +1112,7 @@ }, "node_modules/has-property-descriptors": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" }, @@ -1336,9 +1121,8 @@ } }, "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "version": "1.0.3", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -1348,8 +1132,7 @@ }, "node_modules/has-symbols": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -1358,9 +1141,8 @@ } }, "node_modules/hasown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "version": "2.0.2", + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -1370,14 +1152,12 @@ }, "node_modules/http-cache-semantics": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "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", @@ -1391,9 +1171,8 @@ }, "node_modules/http2-wrapper": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", "dev": true, + "license": "MIT", "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.0.0" @@ -1404,17 +1183,15 @@ }, "node_modules/human-signals": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=10.17.0" } }, "node_modules/iconv-lite": { "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -1424,8 +1201,6 @@ }, "node_modules/ieee754": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true, "funding": [ { @@ -1440,35 +1215,32 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "license": "ISC" }, "node_modules/ipaddr.js": { "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", "engines": { "node": ">= 0.10" } }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -1478,41 +1250,36 @@ }, "node_modules/is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, "node_modules/is-plain-obj": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-stream": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/isomorphic-fetch": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", - "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", + "license": "MIT", "dependencies": { "node-fetch": "^2.6.1", "whatwg-fetch": "^3.4.1" @@ -1520,8 +1287,7 @@ }, "node_modules/isomorphic-fetch/node_modules/node-fetch": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -1539,33 +1305,34 @@ }, "node_modules/json-buffer": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/keyv": { "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, + "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "node_modules/lowercase-keys": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/lru-cache": { "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "dev": true, + "license": "ISC", "dependencies": { "pseudomap": "^1.0.2", "yallist": "^2.1.2" @@ -1573,47 +1340,41 @@ }, "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/merge-descriptors": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "license": "MIT" }, "node_modules/merge-stream": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/merge2": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/methods": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.7", "dev": true, + "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -1622,8 +1383,7 @@ }, "node_modules/mime": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -1633,16 +1393,14 @@ }, "node_modules/mime-db": { "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -1652,27 +1410,24 @@ }, "node_modules/mimic-fn": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/mimic-response": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -1685,21 +1440,17 @@ }, "node_modules/ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "license": "MIT" }, "node_modules/negotiator": { "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/node-domexception": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", "funding": [ { "type": "github", @@ -1710,14 +1461,14 @@ "url": "https://paypal.me/jimmywarting" } ], + "license": "MIT", "engines": { "node": ">=10.5.0" } }, "node_modules/node-fetch": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", @@ -1733,9 +1484,8 @@ }, "node_modules/normalize-url": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -1745,9 +1495,8 @@ }, "node_modules/npm-run-path": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^2.0.0" }, @@ -1757,16 +1506,14 @@ }, "node_modules/object-inspect": { "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/on-finished": { "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -1776,18 +1523,16 @@ }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, + "license": "ISC", "dependencies": { "wrappy": "1" } }, "node_modules/onetime": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -1800,9 +1545,8 @@ }, "node_modules/os-filter-obj": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz", - "integrity": "sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==", "dev": true, + "license": "MIT", "dependencies": { "arch": "^2.1.0" }, @@ -1812,49 +1556,43 @@ }, "node_modules/p-cancelable": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/p-finally": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/parseurl": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/path-key": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/path-to-regexp": { "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "license": "MIT" }, "node_modules/peek-readable": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", - "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -1865,9 +1603,8 @@ }, "node_modules/picomatch": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -1877,18 +1614,16 @@ }, "node_modules/pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/prettier": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", - "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "version": "3.3.2", "dev": true, + "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -1901,8 +1636,7 @@ }, "node_modules/proxy-addr": { "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -1913,15 +1647,13 @@ }, "node_modules/pseudomap": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/pump": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, + "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -1929,8 +1661,7 @@ }, "node_modules/qs": { "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.0.4" }, @@ -1943,120 +1674,6 @@ }, "node_modules/queue-microtask": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readable-web-to-node-stream": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", - "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", - "dev": true, - "dependencies": { - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true - }, - "node_modules/responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", - "dev": true, - "dependencies": { - "lowercase-keys": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { @@ -2072,14 +1689,95 @@ "url": "https://feross.org/support" } ], - "dependencies": { - "queue-microtask": "^1.2.2" + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "node_modules/range-parser": { + "version": "1.2.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readable-web-to-node-stream": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/responselike": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "dev": true, "funding": [ { "type": "github", @@ -2093,21 +1791,38 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, "node_modules/safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "license": "MIT" }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.2", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -2117,9 +1832,8 @@ }, "node_modules/semver-regex": { "version": "4.0.5", - "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", - "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -2129,9 +1843,8 @@ }, "node_modules/semver-truncate": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/semver-truncate/-/semver-truncate-3.0.0.tgz", - "integrity": "sha512-LJWA9kSvMolR51oDE6PN3kALBNaUdkxzAGcexw8gjMA8xr5zUqK0JiR3CgARSqanYF3Z1YHvsErb1KDgh+v7Rg==", "dev": true, + "license": "MIT", "dependencies": { "semver": "^7.3.5" }, @@ -2142,28 +1855,9 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/semver/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/semver/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/send": { "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "license": "MIT", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -2185,13 +1879,11 @@ }, "node_modules/send/node_modules/ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "license": "MIT" }, "node_modules/serve-static": { "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "license": "MIT", "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", @@ -2203,16 +1895,15 @@ } }, "node_modules/set-function-length": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", - "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "version": "1.2.2", + "license": "MIT", "dependencies": { - "define-data-property": "^1.1.2", + "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -2220,14 +1911,12 @@ }, "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": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^1.0.0" }, @@ -2237,19 +1926,17 @@ }, "node_modules/shebang-regex": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/side-channel": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", - "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", + "version": "1.0.6", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.7", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.4", "object-inspect": "^1.13.1" @@ -2263,24 +1950,21 @@ }, "node_modules/signal-exit": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/slash": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/sort-keys": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", "dev": true, + "license": "MIT", "dependencies": { "is-plain-obj": "^1.0.0" }, @@ -2290,9 +1974,8 @@ }, "node_modules/sort-keys-length": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", - "integrity": "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==", "dev": true, + "license": "MIT", "dependencies": { "sort-keys": "^1.0.0" }, @@ -2302,53 +1985,47 @@ }, "node_modules/source-map": { "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">= 8" } }, "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", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" } }, "node_modules/strip-eof": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/strip-final-newline": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/strip-outer": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-2.0.0.tgz", - "integrity": "sha512-A21Xsm1XzUkK0qK1ZrytDUvqsQWict2Cykhvi0fBQntGG5JSprESasEyV1EZ/4CiR5WB5KjzLTrP/bO37B0wPg==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -2358,9 +2035,8 @@ }, "node_modules/strtok3": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", - "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", "dev": true, + "license": "MIT", "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^5.0.0" @@ -2375,9 +2051,8 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -2387,17 +2062,15 @@ }, "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/token-types": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", - "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", "dev": true, + "license": "MIT", "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" @@ -2412,14 +2085,12 @@ }, "node_modules/tr46": { "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "license": "MIT" }, "node_modules/trim-repeated": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-2.0.0.tgz", - "integrity": "sha512-QUHBFTJGdOwmp0tbOG505xAgOp/YliZP/6UgafFXYZ26WT1bvQmSMJUvkeVSASuJJHbqsFbynTvkd5W8RBTipg==", "dev": true, + "license": "MIT", "dependencies": { "escape-string-regexp": "^5.0.0" }, @@ -2429,18 +2100,15 @@ }, "node_modules/ts-matches": { "version": "5.5.1", - "resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-5.5.1.tgz", - "integrity": "sha512-UFYaKgfqlg9FROK7bdpYqFwG1CJvP4kOJdjXuWoqxo9jCmANoDw1GxkSCpJgoTeIiSTaTH5Qr1klSspb8c+ydg==" + "license": "MIT" }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "version": "2.6.3", + "license": "0BSD" }, "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" @@ -2450,10 +2118,9 @@ } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.4.5", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2464,62 +2131,53 @@ }, "node_modules/undici-types": { "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/unpipe": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/utils-merge": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", "engines": { "node": ">= 0.4.0" } }, "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/web-streams-polyfill": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz", - "integrity": "sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ==", + "version": "3.3.3", + "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/webidl-conversions": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "license": "BSD-2-Clause" }, "node_modules/whatwg-fetch": { "version": "3.6.20", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", - "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" + "license": "MIT" }, "node_modules/whatwg-url": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -2527,9 +2185,8 @@ }, "node_modules/which": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -2539,20 +2196,20 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/yallist": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/yaml": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", - "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "version": "2.4.5", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, "engines": { "node": ">= 14" } @@ -2560,14 +2217,10 @@ }, "dependencies": { "@iarna/toml": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", - "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" + "version": "2.2.5" }, "@mole-inc/bin-wrapper": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@mole-inc/bin-wrapper/-/bin-wrapper-8.0.1.tgz", - "integrity": "sha512-sTGoeZnjI8N4KS+sW2AN95gDBErhAguvkw/tWdCjeM8bvxpz5lqrnd0vOJABA1A+Ic3zED7PYoLP/RANLgVotA==", "dev": true, "requires": { "bin-check": "^4.1.0", @@ -2582,8 +2235,6 @@ }, "@nodelib/fs.scandir": { "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "requires": { "@nodelib/fs.stat": "2.0.5", @@ -2592,14 +2243,10 @@ }, "@nodelib/fs.stat": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true }, "@nodelib/fs.walk": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "requires": { "@nodelib/fs.scandir": "2.1.5", @@ -2608,8 +2255,6 @@ }, "@sindresorhus/is": { "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true }, "@start9labs/start-sdk": { @@ -2617,8 +2262,10 @@ "requires": { "@iarna/toml": "^2.2.5", "@types/jest": "^29.4.0", + "@types/lodash": "^4.17.5", "isomorphic-fetch": "^3.0.0", "jest": "^29.4.3", + "lodash": "^4.17.21", "prettier": "^3.2.5", "ts-jest": "^29.0.5", "ts-matches": "^5.4.1", @@ -2630,8 +2277,6 @@ }, "@swc/cli": { "version": "0.1.65", - "resolved": "https://registry.npmjs.org/@swc/cli/-/cli-0.1.65.tgz", - "integrity": "sha512-4NcgsvJVHhA7trDnMmkGLLvWMHu2kSy+qHx6QwRhhJhdiYdNUrhdp+ERxen73sYtaeEOYeLJcWrQ60nzKi6rpg==", "dev": true, "requires": { "@mole-inc/bin-wrapper": "^8.0.1", @@ -2644,111 +2289,46 @@ } }, "@swc/core": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.4.1.tgz", - "integrity": "sha512-3y+Y8js+e7BbM16iND+6Rcs3jdiL28q3iVtYsCviYSSpP2uUVKkp5sJnCY4pg8AaVvyN7CGQHO7gLEZQ5ByozQ==", + "version": "1.5.28", "dev": true, "requires": { - "@swc/core-darwin-arm64": "1.4.1", - "@swc/core-darwin-x64": "1.4.1", - "@swc/core-linux-arm-gnueabihf": "1.4.1", - "@swc/core-linux-arm64-gnu": "1.4.1", - "@swc/core-linux-arm64-musl": "1.4.1", - "@swc/core-linux-x64-gnu": "1.4.1", - "@swc/core-linux-x64-musl": "1.4.1", - "@swc/core-win32-arm64-msvc": "1.4.1", - "@swc/core-win32-ia32-msvc": "1.4.1", - "@swc/core-win32-x64-msvc": "1.4.1", - "@swc/counter": "^0.1.2", - "@swc/types": "^0.1.5" + "@swc/core-darwin-arm64": "1.5.28", + "@swc/core-darwin-x64": "1.5.28", + "@swc/core-linux-arm-gnueabihf": "1.5.28", + "@swc/core-linux-arm64-gnu": "1.5.28", + "@swc/core-linux-arm64-musl": "1.5.28", + "@swc/core-linux-x64-gnu": "1.5.28", + "@swc/core-linux-x64-musl": "1.5.28", + "@swc/core-win32-arm64-msvc": "1.5.28", + "@swc/core-win32-ia32-msvc": "1.5.28", + "@swc/core-win32-x64-msvc": "1.5.28", + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.8" } }, - "@swc/core-darwin-arm64": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.4.1.tgz", - "integrity": "sha512-ePyfx0348UbR4DOAW24TedeJbafnzha8liXFGuQ4bdXtEVXhLfPngprrxKrAddCuv42F9aTxydlF6+adD3FBhA==", - "dev": true, - "optional": true - }, - "@swc/core-darwin-x64": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.4.1.tgz", - "integrity": "sha512-eLf4JSe6VkCMdDowjM8XNC5rO+BrgfbluEzAVtKR8L2HacNYukieumN7EzpYCi0uF1BYwu1ku6tLyG2r0VcGxA==", - "dev": true, - "optional": true - }, - "@swc/core-linux-arm-gnueabihf": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.4.1.tgz", - "integrity": "sha512-K8VtTLWMw+rkN/jDC9o/Q9SMmzdiHwYo2CfgkwVT29NsGccwmNhCQx6XoYiPKyKGIFKt4tdQnJHKUFzxUqQVtQ==", - "dev": true, - "optional": true - }, - "@swc/core-linux-arm64-gnu": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.4.1.tgz", - "integrity": "sha512-0e8p4g0Bfkt8lkiWgcdiENH3RzkcqKtpRXIVNGOmVc0OBkvc2tpm2WTx/eoCnes2HpTT4CTtR3Zljj4knQ4Fvw==", - "dev": true, - "optional": true - }, - "@swc/core-linux-arm64-musl": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.4.1.tgz", - "integrity": "sha512-b/vWGQo2n7lZVUnSQ7NBq3Qrj85GrAPPiRbpqaIGwOytiFSk8VULFihbEUwDe0rXgY4LDm8z8wkgADZcLnmdUA==", - "dev": true, - "optional": true - }, "@swc/core-linux-x64-gnu": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.4.1.tgz", - "integrity": "sha512-AFMQlvkKEdNi1Vk2GFTxxJzbICttBsOQaXa98kFTeWTnFFIyiIj2w7Sk8XRTEJ/AjF8ia8JPKb1zddBWr9+bEQ==", + "version": "1.5.28", "dev": true, "optional": true }, "@swc/core-linux-x64-musl": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.4.1.tgz", - "integrity": "sha512-QX2MxIECX1gfvUVZY+jk528/oFkS9MAl76e3ZRvG2KC/aKlCQL0KSzcTSm13mOxkDKS30EaGRDRQWNukGpMeRg==", - "dev": true, - "optional": true - }, - "@swc/core-win32-arm64-msvc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.4.1.tgz", - "integrity": "sha512-OklkJYXXI/tntD2zaY8i3iZldpyDw5q+NAP3k9OlQ7wXXf37djRsHLV0NW4+ZNHBjE9xp2RsXJ0jlOJhfgGoFA==", - "dev": true, - "optional": true - }, - "@swc/core-win32-ia32-msvc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.4.1.tgz", - "integrity": "sha512-MBuc3/QfKX9FnLOU7iGN+6yHRTQaPQ9WskiC8s8JFiKQ+7I2p25tay2RplR9dIEEGgVAu6L7auv96LbNTh+FaA==", - "dev": true, - "optional": true - }, - "@swc/core-win32-x64-msvc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.4.1.tgz", - "integrity": "sha512-lu4h4wFBb/bOK6N2MuZwg7TrEpwYXgpQf5R7ObNSXL65BwZ9BG8XRzD+dLJmALu8l5N08rP/TrpoKRoGT4WSxw==", + "version": "1.5.28", "dev": true, "optional": true }, "@swc/counter": { "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", "dev": true }, "@swc/types": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", - "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==", - "dev": true + "version": "0.1.8", + "dev": true, + "requires": { + "@swc/counter": "^0.1.3" + } }, "@szmarczak/http-timer": { "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", "dev": true, "requires": { "defer-to-connect": "^2.0.0" @@ -2756,14 +2336,10 @@ }, "@tokenizer/token": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", "dev": true }, "@types/cacheable-request": { "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", "dev": true, "requires": { "@types/http-cache-semantics": "*", @@ -2774,23 +2350,17 @@ }, "@types/http-cache-semantics": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", "dev": true }, "@types/keyv": { "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", "dev": true, "requires": { "@types/node": "*" } }, "@types/node": { - "version": "20.11.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", - "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", + "version": "20.14.2", "dev": true, "requires": { "undici-types": "~5.26.4" @@ -2798,8 +2368,6 @@ }, "@types/responselike": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", - "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", "dev": true, "requires": { "@types/node": "*" @@ -2807,8 +2375,6 @@ }, "accepts": { "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "requires": { "mime-types": "~2.1.34", "negotiator": "0.6.3" @@ -2816,25 +2382,17 @@ }, "arch": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", "dev": true }, "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + "version": "1.1.1" }, "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 }, "bin-check": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bin-check/-/bin-check-4.1.0.tgz", - "integrity": "sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==", "dev": true, "requires": { "execa": "^0.7.0", @@ -2843,8 +2401,6 @@ }, "bin-version": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/bin-version/-/bin-version-6.0.0.tgz", - "integrity": "sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw==", "dev": true, "requires": { "execa": "^5.0.0", @@ -2853,8 +2409,6 @@ "dependencies": { "cross-spawn": { "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -2864,8 +2418,6 @@ }, "execa": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "requires": { "cross-spawn": "^7.0.3", @@ -2881,20 +2433,14 @@ }, "get-stream": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true }, "is-stream": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, "npm-run-path": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "requires": { "path-key": "^3.0.0" @@ -2902,14 +2448,10 @@ }, "path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "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, "requires": { "shebang-regex": "^3.0.0" @@ -2917,14 +2459,10 @@ }, "shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "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" @@ -2934,8 +2472,6 @@ }, "bin-version-check": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bin-version-check/-/bin-version-check-5.1.0.tgz", - "integrity": "sha512-bYsvMqJ8yNGILLz1KP9zKLzQ6YpljV3ln1gqhuLkUtyfGi3qXKGuK2p+U4NAvjVFzDFiBBtOpCOSFNuYYEGZ5g==", "dev": true, "requires": { "bin-version": "^6.0.0", @@ -2944,12 +2480,10 @@ } }, "body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "version": "1.20.2", "requires": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -2957,44 +2491,34 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" } }, "brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "requires": { "balanced-match": "^1.0.0" } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + "version": "3.1.2" }, "cacheable-lookup": { "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", "dev": true }, "cacheable-request": { "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", "dev": true, "requires": { "clone-response": "^1.0.2", @@ -3008,8 +2532,6 @@ "dependencies": { "get-stream": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "requires": { "pump": "^3.0.0" @@ -3019,8 +2541,6 @@ }, "call-bind": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "requires": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -3031,8 +2551,6 @@ }, "clone-response": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dev": true, "requires": { "mimic-response": "^1.0.0" @@ -3040,37 +2558,25 @@ }, "commander": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true }, "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==", "requires": { "safe-buffer": "5.2.1" } }, "content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" + "version": "1.0.5" }, "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + "version": "0.6.0" }, "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + "version": "1.0.6" }, "cross-spawn": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", "dev": true, "requires": { "lru-cache": "^4.0.1", @@ -3079,22 +2585,16 @@ } }, "data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==" + "version": "4.0.1" }, "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" } }, "decompress-response": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, "requires": { "mimic-response": "^3.1.0" @@ -3102,22 +2602,16 @@ "dependencies": { "mimic-response": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "dev": true } } }, "defer-to-connect": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true }, "define-data-property": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "requires": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -3125,29 +2619,19 @@ } }, "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + "version": "2.0.0" }, "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + "version": "1.2.0" }, "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + "version": "1.1.1" }, "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + "version": "1.0.2" }, "end-of-stream": { "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, "requires": { "once": "^1.4.0" @@ -3155,42 +2639,28 @@ }, "es-define-property": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "requires": { "get-intrinsic": "^1.2.4" } }, "es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + "version": "1.3.0" }, "esbuild-plugin-resolve": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/esbuild-plugin-resolve/-/esbuild-plugin-resolve-2.0.0.tgz", - "integrity": "sha512-eJy9B8yDW5X/J48eWtR1uVmv+DKfHvYYnrrcqQoe/nUkVHVOTZlJnSevkYyGOz6hI90t036Y5QIPDrGzmppxfg==" + "version": "2.0.0" }, "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==" + "version": "1.0.3" }, "escape-string-regexp": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "dev": true }, "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + "version": "1.8.1" }, "execa": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==", "dev": true, "requires": { "cross-spawn": "^5.0.1", @@ -3204,24 +2674,20 @@ }, "executable": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", "dev": true, "requires": { "pify": "^2.2.0" } }, "express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "version": "4.19.2", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -3251,8 +2717,6 @@ }, "ext-list": { "version": "2.2.2", - "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", - "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", "dev": true, "requires": { "mime-db": "^1.28.0" @@ -3260,8 +2724,6 @@ }, "ext-name": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", - "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", "dev": true, "requires": { "ext-list": "^2.0.0", @@ -3270,8 +2732,6 @@ }, "fast-glob": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -3283,8 +2743,6 @@ }, "fastq": { "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -3292,8 +2750,6 @@ }, "fetch-blob": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", "requires": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" @@ -3301,8 +2757,6 @@ }, "file-type": { "version": "17.1.6", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-17.1.6.tgz", - "integrity": "sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==", "dev": true, "requires": { "readable-web-to-node-stream": "^3.0.2", @@ -3312,8 +2766,6 @@ }, "filebrowser": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/filebrowser/-/filebrowser-1.0.0.tgz", - "integrity": "sha512-RRONYpCDzbmWPhBX43T4dE+ptqLznJ7lKfbMaZLChB2i2ZIdFXoqT9qZTi70Dpq6fnJHuvcdeiRqMIPZKhVgTQ==", "requires": { "commander": "^2.9.0", "content-disposition": "^0.5.1", @@ -3321,22 +2773,16 @@ }, "dependencies": { "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "version": "2.20.3" } } }, "filename-reserved-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz", - "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==", "dev": true }, "filenamify": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-5.1.1.tgz", - "integrity": "sha512-M45CbrJLGACfrPOkrTp3j2EcO9OBkKUYME0eiqOCa7i2poaklU0jhlIaMlr8ijLorT0uLAzrn3qXOp5684CkfA==", "dev": true, "requires": { "filename-reserved-regex": "^3.0.0", @@ -3345,9 +2791,7 @@ } }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -3355,8 +2799,6 @@ }, "finalhandler": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "requires": { "debug": "2.6.9", "encodeurl": "~1.0.2", @@ -3369,8 +2811,6 @@ }, "find-versions": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-5.1.0.tgz", - "integrity": "sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==", "dev": true, "requires": { "semver-regex": "^4.0.5" @@ -3378,31 +2818,21 @@ }, "formdata-polyfill": { "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", "requires": { "fetch-blob": "^3.1.2" } }, "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + "version": "0.2.0" }, "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + "version": "0.5.2" }, "function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + "version": "1.1.2" }, "get-intrinsic": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "requires": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", @@ -3413,14 +2843,10 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", "dev": true }, "glob-parent": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -3428,16 +2854,12 @@ }, "gopd": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "requires": { "get-intrinsic": "^1.1.3" } }, "got": { "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", "dev": true, "requires": { "@sindresorhus/is": "^4.0.0", @@ -3455,40 +2877,28 @@ }, "has-property-descriptors": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "requires": { "es-define-property": "^1.0.0" } }, "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" + "version": "1.0.3" }, "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + "version": "1.0.3" }, "hasown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "version": "2.0.2", "requires": { "function-bind": "^1.1.2" } }, "http-cache-semantics": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, "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==", "requires": { "depd": "2.0.0", "inherits": "2.0.4", @@ -3499,8 +2909,6 @@ }, "http2-wrapper": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", "dev": true, "requires": { "quick-lru": "^5.1.1", @@ -3509,44 +2917,30 @@ }, "human-signals": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true }, "iconv-lite": { "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "requires": { "safer-buffer": ">= 2.1.2 < 3" } }, "ieee754": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true }, "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "version": "2.0.4" }, "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + "version": "1.9.1" }, "is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -3554,32 +2948,22 @@ }, "is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, "is-plain-obj": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", "dev": true }, "is-stream": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", "dev": true }, "isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "isomorphic-fetch": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", - "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", "requires": { "node-fetch": "^2.6.1", "whatwg-fetch": "^3.4.1" @@ -3587,8 +2971,6 @@ "dependencies": { "node-fetch": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "requires": { "whatwg-url": "^5.0.0" } @@ -3597,29 +2979,26 @@ }, "json-buffer": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, "keyv": { "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "requires": { "json-buffer": "3.0.1" } }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "lowercase-keys": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "dev": true }, "lru-cache": { "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "dev": true, "requires": { "pseudomap": "^1.0.2", @@ -3627,100 +3006,68 @@ } }, "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==" + "version": "0.3.0" }, "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "version": "1.0.1" }, "merge-stream": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, "merge2": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + "version": "1.1.2" }, "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.7", "dev": true, "requires": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + "version": "1.6.0" }, "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + "version": "1.52.0" }, "mime-types": { "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { "mime-db": "1.52.0" } }, "mimic-fn": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, "mimic-response": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", "dev": true }, "minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", "dev": true, "requires": { "brace-expansion": "^2.0.1" } }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "version": "2.0.0" }, "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + "version": "0.6.3" }, "node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" + "version": "1.0.0" }, "node-fetch": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", "requires": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", @@ -3729,36 +3076,26 @@ }, "normalize-url": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", "dev": true }, "npm-run-path": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", "dev": true, "requires": { "path-key": "^2.0.0" } }, "object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==" + "version": "1.13.1" }, "on-finished": { "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "requires": { "ee-first": "1.1.1" } }, "once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "requires": { "wrappy": "1" @@ -3766,8 +3103,6 @@ }, "onetime": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "requires": { "mimic-fn": "^2.1.0" @@ -3775,8 +3110,6 @@ }, "os-filter-obj": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz", - "integrity": "sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==", "dev": true, "requires": { "arch": "^2.1.0" @@ -3784,60 +3117,40 @@ }, "p-cancelable": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", "dev": true }, "p-finally": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", "dev": true }, "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + "version": "1.3.3" }, "path-key": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true }, "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "version": "0.1.7" }, "peek-readable": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", - "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==", "dev": true }, "picomatch": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true }, "prettier": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", - "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "version": "3.3.2", "dev": true }, "proxy-addr": { "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "requires": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -3845,14 +3158,10 @@ }, "pseudomap": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", "dev": true }, "pump": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -3861,33 +3170,23 @@ }, "qs": { "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "requires": { "side-channel": "^1.0.4" } }, "queue-microtask": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, "quick-lru": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true }, "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + "version": "1.2.1" }, "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", "requires": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -3897,8 +3196,6 @@ }, "readable-stream": { "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "requires": { "inherits": "^2.0.3", @@ -3908,8 +3205,6 @@ }, "readable-web-to-node-stream": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", - "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", "dev": true, "requires": { "readable-stream": "^3.6.0" @@ -3917,14 +3212,10 @@ }, "resolve-alpn": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", "dev": true }, "responselike": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", "dev": true, "requires": { "lowercase-keys": "^2.0.0" @@ -3932,65 +3223,31 @@ }, "reusify": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, "run-parallel": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "requires": { "queue-microtask": "^1.2.2" } }, "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "version": "5.2.1" }, "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "version": "2.1.2" }, "semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - }, - "dependencies": { - "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" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } + "version": "7.6.2", + "dev": true }, "semver-regex": { "version": "4.0.5", - "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", - "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", "dev": true }, "semver-truncate": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/semver-truncate/-/semver-truncate-3.0.0.tgz", - "integrity": "sha512-LJWA9kSvMolR51oDE6PN3kALBNaUdkxzAGcexw8gjMA8xr5zUqK0JiR3CgARSqanYF3Z1YHvsErb1KDgh+v7Rg==", "dev": true, "requires": { "semver": "^7.3.5" @@ -3998,8 +3255,6 @@ }, "send": { "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "requires": { "debug": "2.6.9", "depd": "2.0.0", @@ -4017,16 +3272,12 @@ }, "dependencies": { "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "version": "2.1.3" } } }, "serve-static": { "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", @@ -4035,27 +3286,21 @@ } }, "set-function-length": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", - "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "version": "1.2.2", "requires": { - "define-data-property": "^1.1.2", + "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" + "has-property-descriptors": "^1.0.2" } }, "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "version": "1.2.0" }, "shebang-command": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, "requires": { "shebang-regex": "^1.0.0" @@ -4063,16 +3308,12 @@ }, "shebang-regex": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true }, "side-channel": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", - "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", + "version": "1.0.6", "requires": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.7", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.4", "object-inspect": "^1.13.1" @@ -4080,20 +3321,14 @@ }, "signal-exit": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "slash": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, "sort-keys": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", "dev": true, "requires": { "is-plain-obj": "^1.0.0" @@ -4101,8 +3336,6 @@ }, "sort-keys-length": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", - "integrity": "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==", "dev": true, "requires": { "sort-keys": "^1.0.0" @@ -4110,19 +3343,13 @@ }, "source-map": { "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true }, "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + "version": "2.0.1" }, "string_decoder": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "requires": { "safe-buffer": "~5.2.0" @@ -4130,26 +3357,18 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", "dev": true }, "strip-final-newline": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true }, "strip-outer": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-2.0.0.tgz", - "integrity": "sha512-A21Xsm1XzUkK0qK1ZrytDUvqsQWict2Cykhvi0fBQntGG5JSprESasEyV1EZ/4CiR5WB5KjzLTrP/bO37B0wPg==", "dev": true }, "strtok3": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", - "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", "dev": true, "requires": { "@tokenizer/token": "^0.3.0", @@ -4158,22 +3377,16 @@ }, "to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { "is-number": "^7.0.0" } }, "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + "version": "1.0.1" }, "token-types": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", - "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", "dev": true, "requires": { "@tokenizer/token": "^0.3.0", @@ -4181,90 +3394,60 @@ } }, "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "version": "0.0.3" }, "trim-repeated": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-2.0.0.tgz", - "integrity": "sha512-QUHBFTJGdOwmp0tbOG505xAgOp/YliZP/6UgafFXYZ26WT1bvQmSMJUvkeVSASuJJHbqsFbynTvkd5W8RBTipg==", "dev": true, "requires": { "escape-string-regexp": "^5.0.0" } }, "ts-matches": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-5.5.1.tgz", - "integrity": "sha512-UFYaKgfqlg9FROK7bdpYqFwG1CJvP4kOJdjXuWoqxo9jCmANoDw1GxkSCpJgoTeIiSTaTH5Qr1klSspb8c+ydg==" + "version": "5.5.1" }, "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "version": "2.6.3" }, "type-is": { "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "requires": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.4.5", "dev": true }, "undici-types": { "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + "version": "1.0.0" }, "util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + "version": "1.0.1" }, "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + "version": "1.1.2" }, "web-streams-polyfill": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz", - "integrity": "sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ==" + "version": "3.3.3" }, "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "version": "3.0.1" }, "whatwg-fetch": { - "version": "3.6.20", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", - "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" + "version": "3.6.20" }, "whatwg-url": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "requires": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -4272,8 +3455,6 @@ }, "which": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -4281,20 +3462,14 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "yallist": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", "dev": true }, "yaml": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", - "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==" + "version": "2.4.5" } } } diff --git a/container-runtime/package.json b/container-runtime/package.json index e2c56afff..357c606fc 100644 --- a/container-runtime/package.json +++ b/container-runtime/package.json @@ -21,6 +21,7 @@ "esbuild-plugin-resolve": "^2.0.0", "filebrowser": "^1.0.0", "isomorphic-fetch": "^3.0.0", + "lodash": "^4.17.21", "node-fetch": "^3.1.0", "ts-matches": "^5.5.1", "tslib": "^2.5.3", diff --git a/container-runtime/src/Adapters/HostSystemStartOs.ts b/container-runtime/src/Adapters/HostSystemStartOs.ts index ba2076f52..93905825a 100644 --- a/container-runtime/src/Adapters/HostSystemStartOs.ts +++ b/container-runtime/src/Adapters/HostSystemStartOs.ts @@ -32,6 +32,8 @@ type RpcError = typeof matchRpcError._TYPE const SOCKET_PATH = "/media/startos/rpc/host.sock" const MAIN = "/main" as const export class HostSystemStartOs implements Effects { + procedureId: string | null = null + static of(callbackHolder: CallbackHolder) { return new HostSystemStartOs(callbackHolder) } @@ -40,7 +42,7 @@ export class HostSystemStartOs implements Effects { id = 0 rpcRound( method: K, - params: unknown, + params: Record, ) { const id = this.id++ const client = net.createConnection({ path: SOCKET_PATH }, () => { @@ -48,7 +50,7 @@ export class HostSystemStartOs implements Effects { JSON.stringify({ id, method, - params, + params: { ...params, procedureId: this.procedureId }, }) + "\n", ) }) @@ -102,14 +104,14 @@ export class HostSystemStartOs implements Effects { }) as ReturnType } clearBindings(...[]: Parameters) { - return this.rpcRound("clearBindings", null) as ReturnType< + return this.rpcRound("clearBindings", {}) as ReturnType< T.Effects["clearBindings"] > } clearServiceInterfaces( ...[]: Parameters ) { - return this.rpcRound("clearServiceInterfaces", null) as ReturnType< + return this.rpcRound("clearServiceInterfaces", {}) as ReturnType< T.Effects["clearServiceInterfaces"] > } @@ -145,18 +147,20 @@ export class HostSystemStartOs implements Effects { T.Effects["exportServiceInterface"] > } - exposeForDependents(...[options]: any) { - return this.rpcRound("exposeForDependents", null) as ReturnType< + exposeForDependents( + ...[options]: Parameters + ) { + return this.rpcRound("exposeForDependents", options) as ReturnType< T.Effects["exposeForDependents"] > } getConfigured(...[]: Parameters) { - return this.rpcRound("getConfigured", null) as ReturnType< + return this.rpcRound("getConfigured", {}) as ReturnType< T.Effects["getConfigured"] > } getContainerIp(...[]: Parameters) { - return this.rpcRound("getContainerIp", null) as ReturnType< + return this.rpcRound("getContainerIp", {}) as ReturnType< T.Effects["getContainerIp"] > } @@ -229,7 +233,7 @@ export class HostSystemStartOs implements Effects { > } restart(...[]: Parameters) { - return this.rpcRound("restart", null) + return this.rpcRound("restart", {}) as ReturnType } running(...[packageId]: Parameters) { return this.rpcRound("running", { packageId }) as ReturnType< @@ -262,7 +266,7 @@ export class HostSystemStartOs implements Effects { > } getDependencies(): ReturnType { - return this.rpcRound("getDependencies", null) as ReturnType< + return this.rpcRound("getDependencies", {}) as ReturnType< T.Effects["getDependencies"] > } @@ -279,7 +283,7 @@ export class HostSystemStartOs implements Effects { } shutdown(...[]: Parameters) { - return this.rpcRound("shutdown", null) + return this.rpcRound("shutdown", {}) as ReturnType } stopped(...[packageId]: Parameters) { return this.rpcRound("stopped", { packageId }) as ReturnType< diff --git a/container-runtime/src/Adapters/RpcListener.ts b/container-runtime/src/Adapters/RpcListener.ts index faff253fe..5391d943e 100644 --- a/container-runtime/src/Adapters/RpcListener.ts +++ b/container-runtime/src/Adapters/RpcListener.ts @@ -58,6 +58,7 @@ const runType = object({ method: literal("execute"), params: object( { + id: string, procedure: string, input: any, timeout: number, @@ -70,6 +71,7 @@ const sandboxRunType = object({ method: literal("sandbox"), params: object( { + id: string, procedure: string, input: any, timeout: number, @@ -195,6 +197,7 @@ export class RpcListener { const procedure = jsonPath.unsafeCast(params.procedure) return system .execute(this.effects, { + id: params.id, procedure, input: params.input, timeout: params.timeout, diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts index 0bdb0d769..731b38903 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts @@ -49,7 +49,7 @@ function todo(): never { const execFile = promisify(childProcess.execFile) const MANIFEST_LOCATION = "/usr/lib/startos/package/embassyManifest.json" -const EMBASSY_JS_LOCATION = "/usr/lib/startos/package/embassy.js" +export const EMBASSY_JS_LOCATION = "/usr/lib/startos/package/embassy.js" const EMBASSY_POINTER_PATH_PREFIX = "/embassyConfig" const matchSetResult = object( @@ -199,11 +199,14 @@ export class SystemForEmbassy implements System { async execute( effects: HostSystemStartOs, options: { + id: string procedure: JsonPath input: unknown timeout?: number | undefined }, ): Promise { + effects = Object.create(effects) + effects.procedureId = options.id return this._execute(effects, options) .then((x) => matches(x) @@ -724,7 +727,7 @@ export class SystemForEmbassy implements System { private async properties( effects: HostSystemStartOs, timeoutMs: number | null, - ): Promise> { + ): Promise> { // TODO BLU-J set the properties ever so often const setConfigValue = this.manifest.properties if (!setConfigValue) throw new Error("There is no properties") diff --git a/container-runtime/src/Adapters/Systems/SystemForStartOs.ts b/container-runtime/src/Adapters/Systems/SystemForStartOs.ts index 7549bf0f2..bf27f222d 100644 --- a/container-runtime/src/Adapters/Systems/SystemForStartOs.ts +++ b/container-runtime/src/Adapters/Systems/SystemForStartOs.ts @@ -1,20 +1,23 @@ import { ExecuteResult, System } from "../../Interfaces/System" import { unNestPath } from "../../Models/JsonPath" -import { string } from "ts-matches" +import matches, { any, number, object, string, tuple } from "ts-matches" import { HostSystemStartOs } from "../HostSystemStartOs" import { Effects } from "../../Models/Effects" -import { RpcResult } from "../RpcListener" +import { RpcResult, matchRpcResult } from "../RpcListener" import { duration } from "../../Models/Duration" -const LOCATION = "/usr/lib/startos/package/startos" +import { T } from "@start9labs/start-sdk" +import { MainEffects } from "@start9labs/start-sdk/cjs/lib/StartSdk" +export const STARTOS_JS_LOCATION = "/usr/lib/startos/package/index.js" export class SystemForStartOs implements System { private onTerm: (() => Promise) | undefined static of() { - return new SystemForStartOs() + return new SystemForStartOs(require(STARTOS_JS_LOCATION)) } - constructor() {} + constructor(readonly abi: T.ABI) {} async execute( effects: HostSystemStartOs, options: { + id: string procedure: | "/init" | "/uninit" @@ -33,7 +36,61 @@ export class SystemForStartOs implements System { timeout?: number | undefined }, ): Promise { - return { result: await this._execute(effects, options) } + effects = Object.create(effects) + effects.procedureId = options.id + return this._execute(effects, options) + .then((x) => + matches(x) + .when( + object({ + result: any, + }), + (x) => x, + ) + .when( + object({ + error: string, + }), + (x) => ({ + error: { + code: 0, + message: x.error, + }, + }), + ) + .when( + object({ + "error-code": tuple(number, string), + }), + ({ "error-code": [code, message] }) => ({ + error: { + code, + message, + }, + }), + ) + .defaultTo({ result: x }), + ) + .catch((error: unknown) => { + if (error instanceof Error) + return { + error: { + code: 0, + message: error.name, + data: { + details: error.message, + debug: `${error?.cause ?? "[noCause]"}:${error?.stack ?? "[noStack]"}`, + }, + }, + } + if (matchRpcResult.test(error)) return error + return { + error: { + code: 0, + message: String(error), + }, + } + }) } async _execute( effects: Effects, @@ -58,26 +115,27 @@ export class SystemForStartOs implements System { ): Promise { switch (options.procedure) { case "/init": { - const path = `${LOCATION}/procedures/init` - const procedure: any = await import(path).catch(() => require(path)) - const previousVersion = string.optional().unsafeCast(options) - return procedure.init({ effects, previousVersion }) + const previousVersion = + string.optional().unsafeCast(options.input) || null + return this.abi.init({ effects, previousVersion }) } case "/uninit": { - const path = `${LOCATION}/procedures/init` - const procedure: any = await import(path).catch(() => require(path)) - const nextVersion = string.optional().unsafeCast(options) - return procedure.uninit({ effects, nextVersion }) + const nextVersion = string.optional().unsafeCast(options.input) || null + return this.abi.uninit({ effects, nextVersion }) } case "/main/start": { - const path = `${LOCATION}/procedures/main` - const procedure: any = await import(path).catch(() => require(path)) const started = async (onTerm: () => Promise) => { await effects.setMainStatus({ status: "running" }) if (this.onTerm) await this.onTerm() this.onTerm = onTerm } - return procedure.main({ effects, started }) + const daemons = await ( + await this.abi.main({ + effects: { ...effects, _type: "main" }, + started, + }) + ).build() + this.onTerm = daemons.term } case "/main/stop": { await effects.setMainStatus({ status: "stopped" }) @@ -86,67 +144,50 @@ export class SystemForStartOs implements System { return duration(30, "s") } case "/config/set": { - const path = `${LOCATION}/procedures/config` - const procedure: any = await import(path).catch(() => require(path)) - const input = options.input - return procedure.setConfig({ effects, input }) + const input = options.input as any // TODO + return this.abi.setConfig({ effects, input }) } case "/config/get": { - const path = `${LOCATION}/procedures/config` - const procedure: any = await import(path).catch(() => require(path)) - return procedure.getConfig({ effects }) + return this.abi.getConfig({ effects }) } case "/backup/create": case "/backup/restore": throw new Error("this should be called with the init/unit") case "/actions/metadata": { - const path = `${LOCATION}/procedures/actions` - const procedure: any = await import(path).catch(() => require(path)) - return procedure.actionsMetadata({ effects }) + return this.abi.actionsMetadata({ effects }) } default: const procedures = unNestPath(options.procedure) const id = procedures[2] switch (true) { case procedures[1] === "actions" && procedures[3] === "get": { - const path = `${LOCATION}/procedures/actions` - const action: any = (await import(path).catch(() => require(path))) - .actions[id] + const action = (await this.abi.actions({ effects }))[id] if (!action) throw new Error(`Action ${id} not found`) - return action.get({ effects }) + return action.getConfig({ effects }) } case procedures[1] === "actions" && procedures[3] === "run": { - const path = `${LOCATION}/procedures/actions` - const action: any = (await import(path).catch(() => require(path))) - .actions[id] + const action = (await this.abi.actions({ effects }))[id] if (!action) throw new Error(`Action ${id} not found`) - const input = options.input - return action.run({ effects, input }) + return action.run({ effects, input: options.input as any }) // TODO } case procedures[1] === "dependencies" && procedures[3] === "query": { - const path = `${LOCATION}/procedures/dependencies` - const dependencyConfig: any = ( - await import(path).catch(() => require(path)) - ).dependencyConfig[id] + const dependencyConfig = this.abi.dependencyConfig[id] if (!dependencyConfig) throw new Error(`dependencyConfig ${id} not found`) const localConfig = options.input - return dependencyConfig.query({ effects, localConfig }) + return dependencyConfig.query({ effects }) } case procedures[1] === "dependencies" && procedures[3] === "update": { - const path = `${LOCATION}/procedures/dependencies` - const dependencyConfig: any = ( - await import(path).catch(() => require(path)) - ).dependencyConfig[id] + const dependencyConfig = this.abi.dependencyConfig[id] if (!dependencyConfig) throw new Error(`dependencyConfig ${id} not found`) - return dependencyConfig.update(options.input) + return dependencyConfig.update(options.input as any) // TODO } } } - throw new Error("Method not implemented.") + throw new Error(`Method ${options.procedure} not implemented.`) } - exit(effects: Effects): Promise { - throw new Error("Method not implemented.") + async exit(effects: Effects): Promise { + return void null } } diff --git a/container-runtime/src/Adapters/Systems/index.ts b/container-runtime/src/Adapters/Systems/index.ts index eadc67318..a44ad533e 100644 --- a/container-runtime/src/Adapters/Systems/index.ts +++ b/container-runtime/src/Adapters/Systems/index.ts @@ -1,6 +1,22 @@ +import * as fs from "node:fs/promises" import { System } from "../../Interfaces/System" -import { SystemForEmbassy } from "./SystemForEmbassy" -import { SystemForStartOs } from "./SystemForStartOs" +import { EMBASSY_JS_LOCATION, SystemForEmbassy } from "./SystemForEmbassy" +import { STARTOS_JS_LOCATION, SystemForStartOs } from "./SystemForStartOs" export async function getSystem(): Promise { - return SystemForEmbassy.of() + if ( + await fs.access(STARTOS_JS_LOCATION).then( + () => true, + () => false, + ) + ) { + return SystemForStartOs.of() + } else if ( + await fs.access(EMBASSY_JS_LOCATION).then( + () => true, + () => false, + ) + ) { + return SystemForEmbassy.of() + } + throw new Error(`${STARTOS_JS_LOCATION} not found`) } diff --git a/container-runtime/src/Interfaces/System.ts b/container-runtime/src/Interfaces/System.ts index 86b2aa492..85ba0fb0f 100644 --- a/container-runtime/src/Interfaces/System.ts +++ b/container-runtime/src/Interfaces/System.ts @@ -14,6 +14,7 @@ export interface System { execute( effects: T.Effects, options: { + id: string procedure: JsonPath input: unknown timeout?: number diff --git a/core/Cargo.lock b/core/Cargo.lock index 4ae02e98e..cf10152c2 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -387,6 +387,22 @@ dependencies = [ "tower-service", ] +[[package]] +name = "backhand" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f2fc1bc7bb7fd449e02000cc1592cc63dcdcd61710f8b9efe32bab2d1784603" +dependencies = [ + "deku", + "flate2", + "rustc-hash", + "thiserror", + "tracing", + "xz2", + "zstd", + "zstd-safe", +] + [[package]] name = "backtrace" version = "0.3.71" @@ -588,6 +604,11 @@ name = "cc" version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" +dependencies = [ + "jobserver", + "libc", + "once_cell", +] [[package]] name = "cfg-if" @@ -1058,12 +1079,6 @@ dependencies = [ "cipher 0.3.0", ] -[[package]] -name = "current_platform" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a74858bcfe44b22016cb49337d7b6f04618c58e5dbfdef61b06b8c434324a0bc" - [[package]] name = "curve25519-dalek" version = "3.2.0" @@ -1146,6 +1161,31 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +[[package]] +name = "deku" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709ade444d53896e60f6265660eb50480dd08b77bfc822e5dcc233b88b0b2fba" +dependencies = [ + "bitvec", + "deku_derive", + "no_std_io", + "rustversion", +] + +[[package]] +name = "deku_derive" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7534973f93f9de83203e41c8ddd32d230599fa73fa889f3deb1580ccd186913" +dependencies = [ + "darling", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.60", +] + [[package]] name = "der" version = "0.7.9" @@ -2405,6 +2445,15 @@ dependencies = [ "jaq-parse", ] +[[package]] +name = "jobserver" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +dependencies = [ + "libc", +] + [[package]] name = "josekit" version = "0.8.6" @@ -2561,6 +2610,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "libyml" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e281a65eeba3d4503a2839252f86374528f9ceafe6fed97c1d3b52e1fb625c1" + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -2583,6 +2638,17 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "lzma-sys" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "matchers" version = "0.1.0" @@ -2788,6 +2854,15 @@ dependencies = [ "libc", ] +[[package]] +name = "no_std_io" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fa5f306a6f2c01b4fd172f29bb46195b1764061bf926c75e96ff55df3178208" +dependencies = [ + "memchr", +] + [[package]] name = "nom" version = "7.1.3" @@ -3800,6 +3875,12 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc_version" version = "0.4.0" @@ -3895,9 +3976,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "rusty-fork" @@ -3929,9 +4010,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -4015,9 +4096,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.200" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] @@ -4041,9 +4122,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.200" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", @@ -4052,9 +4133,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "indexmap 2.2.6", "itoa", @@ -4124,16 +4205,20 @@ dependencies = [ ] [[package]] -name = "serde_yaml" -version = "0.9.34+deprecated" +name = "serde_yml" +version = "0.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +checksum = "78ce6afeda22f0b55dde2c34897bce76a629587348480384231205c14b59a01f" dependencies = [ "indexmap 2.2.6", "itoa", + "libyml", + "log", + "memchr", "ryu", "serde", - "unsafe-libyaml", + "serde_json", + "tempfile", ] [[package]] @@ -4600,6 +4685,7 @@ dependencies = [ "async-trait", "axum 0.7.5", "axum-server", + "backhand", "base32", "base64 0.21.7", "base64ct", @@ -4614,7 +4700,6 @@ dependencies = [ "console-subscriber", "cookie 0.18.1", "cookie_store", - "current_platform", "der", "digest 0.10.7", "divrem", @@ -4681,7 +4766,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "serde_with", - "serde_yaml", + "serde_yml", "sha2 0.10.8", "shell-words", "simple-logging", @@ -5537,12 +5622,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" -[[package]] -name = "unsafe-libyaml" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" - [[package]] name = "untrusted" version = "0.9.0" @@ -5993,6 +6072,15 @@ dependencies = [ "rustix", ] +[[package]] +name = "xz2" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +dependencies = [ + "lzma-sys", +] + [[package]] name = "yajrc" version = "0.1.3" @@ -6056,3 +6144,31 @@ dependencies = [ "quote", "syn 2.0.60", ] + +[[package]] +name = "zstd" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.10+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/core/startos/Cargo.toml b/core/startos/Cargo.toml index bd064a167..3ad4f9eeb 100644 --- a/core/startos/Cargo.toml +++ b/core/startos/Cargo.toml @@ -59,6 +59,7 @@ async-stream = "0.3.5" async-trait = "0.1.74" axum = { version = "0.7.3", features = ["ws"] } axum-server = "0.6.0" +backhand = "0.18.0" base32 = "0.4.0" base64 = "0.21.4" base64ct = "1.6.0" @@ -72,7 +73,6 @@ console = "0.15.7" console-subscriber = { version = "0.2", optional = true } cookie = "0.18.0" cookie_store = "0.20.0" -current_platform = "0.2.0" der = { version = "0.7.9", features = ["derive", "pem"] } digest = "0.10.7" divrem = "1.0.0" @@ -154,7 +154,7 @@ serde_json = "1.0" serde_toml = { package = "toml", version = "0.8.2" } serde_urlencoded = "0.7" serde_with = { version = "3.4.0", features = ["macros", "json"] } -serde_yaml = "0.9.25" +serde_yaml = { package = "serde_yml", version = "0.0.10" } sha2 = "0.10.2" shell-words = "1" simple-logging = "2.0.2" diff --git a/core/startos/src/action.rs b/core/startos/src/action.rs index 87ff317f8..e93af4a4d 100644 --- a/core/startos/src/action.rs +++ b/core/startos/src/action.rs @@ -8,6 +8,7 @@ use ts_rs::TS; use crate::config::Config; use crate::context::RpcContext; use crate::prelude::*; +use crate::rpc_continuations::Guid; use crate::util::serde::{display_serializable, StdinDeserializable, WithIoFormat}; #[derive(Debug, Serialize, Deserialize)] @@ -77,6 +78,7 @@ pub async fn action( .as_ref() .or_not_found(lazy_format!("Manager for {}", package_id))? .action( + Guid::new(), action_id, input.map(|c| to_value(&c)).transpose()?.unwrap_or_default(), ) diff --git a/core/startos/src/auth.rs b/core/startos/src/auth.rs index 4838f2ea2..d33320b78 100644 --- a/core/startos/src/auth.rs +++ b/core/startos/src/auth.rs @@ -178,6 +178,7 @@ pub fn check_password_against_db(db: &DatabaseModel, password: &str) -> Result<( #[derive(Deserialize, Serialize, Parser, TS)] #[serde(rename_all = "camelCase")] #[command(rename_all = "kebab-case")] +#[ts(export)] pub struct LoginParams { password: Option, #[ts(skip)] diff --git a/core/startos/src/backup/restore.rs b/core/startos/src/backup/restore.rs index 774b1f1cf..4753a4290 100644 --- a/core/startos/src/backup/restore.rs +++ b/core/startos/src/backup/restore.rs @@ -149,7 +149,6 @@ async fn restore_packages( S9pk::open( backup_dir.path().join(&id).with_extension("s9pk"), Some(&id), - true, ) .await?, Some(backup_dir), diff --git a/core/startos/src/config/mod.rs b/core/startos/src/config/mod.rs index e61517794..01309a16f 100644 --- a/core/startos/src/config/mod.rs +++ b/core/startos/src/config/mod.rs @@ -16,6 +16,7 @@ use ts_rs::TS; use crate::context::{CliContext, RpcContext}; use crate::prelude::*; +use crate::rpc_continuations::Guid; use crate::util::serde::{HandlerExtSerde, StdinDeserializable}; #[derive(Clone, Debug, Default, Serialize, Deserialize)] @@ -156,7 +157,7 @@ pub async fn get(ctx: RpcContext, _: Empty, id: PackageId) -> Result, - #[arg(long = "ethernet-interface")] + #[arg(long)] pub ethernet_interface: Option, #[arg(skip)] pub os_partitions: Option, - #[arg(long = "bind-rpc")] + #[arg(long)] pub bind_rpc: Option, - #[arg(long = "tor-control")] + #[arg(long)] pub tor_control: Option, - #[arg(long = "tor-socks")] + #[arg(long)] pub tor_socks: Option, - #[arg(long = "dns-bind")] + #[arg(long)] pub dns_bind: Option>, - #[arg(long = "revision-cache-size")] + #[arg(long)] pub revision_cache_size: Option, - #[arg(short = 'd', long = "datadir")] + #[arg(short, long)] pub datadir: Option, - #[arg(long = "disable-encryption")] + #[arg(long)] pub disable_encryption: Option, + #[arg(long)] + pub multi_arch_s9pks: Option, } impl ContextConfig for ServerConfig { fn next(&mut self) -> Option { @@ -131,6 +133,7 @@ impl ContextConfig for ServerConfig { .or(other.revision_cache_size); self.datadir = self.datadir.take().or(other.datadir); self.disable_encryption = self.disable_encryption.take().or(other.disable_encryption); + self.multi_arch_s9pks = self.multi_arch_s9pks.take().or(other.multi_arch_s9pks); } } diff --git a/core/startos/src/context/rpc.rs b/core/startos/src/context/rpc.rs index cf2d28085..a3e77a62c 100644 --- a/core/startos/src/context/rpc.rs +++ b/core/startos/src/context/rpc.rs @@ -43,6 +43,7 @@ pub struct RpcContextSeed { pub db: TypedPatchDb, pub account: RwLock, pub net_controller: Arc, + pub s9pk_arch: Option<&'static str>, pub services: ServiceMap, pub metrics_cache: RwLock>, pub shutdown: broadcast::Sender>, @@ -152,6 +153,11 @@ impl RpcContext { db, account: RwLock::new(account), net_controller, + s9pk_arch: if config.multi_arch_s9pks.unwrap_or(false) { + None + } else { + Some(crate::ARCH) + }, services, metrics_cache, shutdown, diff --git a/core/startos/src/control.rs b/core/startos/src/control.rs index d4a595a61..e831e07d6 100644 --- a/core/startos/src/control.rs +++ b/core/startos/src/control.rs @@ -7,6 +7,7 @@ use ts_rs::TS; use crate::context::RpcContext; use crate::prelude::*; +use crate::rpc_continuations::Guid; use crate::Error; #[derive(Deserialize, Serialize, Parser, TS)] @@ -23,7 +24,7 @@ pub async fn start(ctx: RpcContext, ControlParams { id }: ControlParams) -> Resu .await .as_ref() .or_not_found(lazy_format!("Manager for {id}"))? - .start() + .start(Guid::new()) .await?; Ok(()) @@ -36,7 +37,7 @@ pub async fn stop(ctx: RpcContext, ControlParams { id }: ControlParams) -> Resul .await .as_ref() .ok_or_else(|| Error::new(eyre!("Manager not found"), crate::ErrorKind::InvalidRequest))? - .stop() + .stop(Guid::new()) .await?; Ok(()) @@ -48,7 +49,7 @@ pub async fn restart(ctx: RpcContext, ControlParams { id }: ControlParams) -> Re .await .as_ref() .ok_or_else(|| Error::new(eyre!("Manager not found"), crate::ErrorKind::InvalidRequest))? - .restart() + .restart(Guid::new()) .await?; Ok(()) diff --git a/core/startos/src/dependencies.rs b/core/startos/src/dependencies.rs index a746a42c9..f6ccc53ad 100644 --- a/core/startos/src/dependencies.rs +++ b/core/startos/src/dependencies.rs @@ -13,6 +13,7 @@ use crate::config::{Config, ConfigSpec, ConfigureContext}; use crate::context::RpcContext; use crate::db::model::package::CurrentDependencies; use crate::prelude::*; +use crate::rpc_continuations::Guid; use crate::Error; pub fn dependency() -> ParentHandler { @@ -86,7 +87,7 @@ pub async fn configure_impl( ErrorKind::Unknown, ) })? - .configure(configure_context) + .configure(Guid::new(), configure_context) .await?; Ok(()) } @@ -103,14 +104,15 @@ pub async fn configure_logic( ctx: RpcContext, (dependent_id, dependency_id): (PackageId, PackageId), ) -> Result { + let procedure_id = Guid::new(); let dependency_guard = ctx.services.get(&dependency_id).await; let dependency = dependency_guard.as_ref().or_not_found(&dependency_id)?; let dependent_guard = ctx.services.get(&dependent_id).await; let dependent = dependent_guard.as_ref().or_not_found(&dependent_id)?; - let config_res = dependency.get_config().await?; + let config_res = dependency.get_config(procedure_id.clone()).await?; let diff = Value::Object( dependent - .dependency_config(dependency_id, config_res.config.clone()) + .dependency_config(procedure_id, dependency_id, config_res.config.clone()) .await? .unwrap_or_default(), ); @@ -129,6 +131,7 @@ pub async fn compute_dependency_config_errs( id: &PackageId, current_dependencies: &mut CurrentDependencies, ) -> Result<(), Error> { + let procedure_id = Guid::new(); let service_guard = ctx.services.get(id).await; let service = service_guard.as_ref().or_not_found(id)?; for (dep_id, dep_info) in current_dependencies.0.iter_mut() { @@ -137,10 +140,10 @@ pub async fn compute_dependency_config_errs( continue; }; - let dep_config = dependency.get_config().await?.config; + let dep_config = dependency.get_config(procedure_id.clone()).await?.config; dep_info.config_satisfied = service - .dependency_config(dep_id.clone(), dep_config) + .dependency_config(procedure_id.clone(), dep_id.clone(), dep_config) .await? .is_none(); } diff --git a/core/startos/src/disk/mount/backup.rs b/core/startos/src/disk/mount/backup.rs index 5dbd80db3..ad9f5090b 100644 --- a/core/startos/src/disk/mount/backup.rs +++ b/core/startos/src/disk/mount/backup.rs @@ -178,7 +178,6 @@ impl BackupMountGuard { Ok(()) } } -#[async_trait::async_trait] impl GenericMountGuard for BackupMountGuard { fn path(&self) -> &Path { if let Some(guard) = &self.encrypted_guard { diff --git a/core/startos/src/disk/mount/filesystem/overlayfs.rs b/core/startos/src/disk/mount/filesystem/overlayfs.rs index f96de7b11..5e40a21a1 100644 --- a/core/startos/src/disk/mount/filesystem/overlayfs.rs +++ b/core/startos/src/disk/mount/filesystem/overlayfs.rs @@ -6,8 +6,8 @@ use digest::generic_array::GenericArray; use digest::{Digest, OutputSizeUser}; use sha2::Sha256; -use crate::disk::mount::filesystem::{FileSystem, ReadOnly, ReadWrite}; -use crate::disk::mount::guard::{GenericMountGuard, MountGuard, TmpMountGuard}; +use crate::disk::mount::filesystem::{FileSystem, ReadWrite}; +use crate::disk::mount::guard::{GenericMountGuard, MountGuard}; use crate::prelude::*; use crate::util::io::TmpDir; @@ -94,17 +94,13 @@ impl< } #[derive(Debug)] -pub struct OverlayGuard { - lower: Option, +pub struct OverlayGuard { + lower: Option, upper: Option, inner_guard: MountGuard, } -impl OverlayGuard { - pub async fn mount( - base: &impl FileSystem, - mountpoint: impl AsRef, - ) -> Result { - let lower = TmpMountGuard::mount(base, ReadOnly).await?; +impl OverlayGuard { + pub async fn mount(lower: G, mountpoint: impl AsRef) -> Result { let upper = TmpDir::new().await?; let inner_guard = MountGuard::mount( &OverlayFs::new( @@ -140,16 +136,15 @@ impl OverlayGuard { } } } -#[async_trait::async_trait] -impl GenericMountGuard for OverlayGuard { +impl GenericMountGuard for OverlayGuard { fn path(&self) -> &Path { self.inner_guard.path() } - async fn unmount(mut self) -> Result<(), Error> { + async fn unmount(self) -> Result<(), Error> { self.unmount(false).await } } -impl Drop for OverlayGuard { +impl Drop for OverlayGuard { fn drop(&mut self) { let lower = self.lower.take(); let upper = self.upper.take(); diff --git a/core/startos/src/disk/mount/guard.rs b/core/startos/src/disk/mount/guard.rs index d08b04881..4686a10b8 100644 --- a/core/startos/src/disk/mount/guard.rs +++ b/core/startos/src/disk/mount/guard.rs @@ -2,6 +2,7 @@ use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use std::sync::{Arc, Weak}; +use futures::Future; use lazy_static::lazy_static; use models::ResultExt; use tokio::sync::Mutex; @@ -14,23 +15,20 @@ use crate::Error; pub const TMP_MOUNTPOINT: &'static str = "/media/startos/tmp"; -#[async_trait::async_trait] pub trait GenericMountGuard: std::fmt::Debug + Send + Sync + 'static { fn path(&self) -> &Path; - async fn unmount(mut self) -> Result<(), Error>; + fn unmount(self) -> impl Future> + Send; } -#[async_trait::async_trait] impl GenericMountGuard for Never { fn path(&self) -> &Path { match *self {} } - async fn unmount(mut self) -> Result<(), Error> { + async fn unmount(self) -> Result<(), Error> { match self {} } } -#[async_trait::async_trait] impl GenericMountGuard for Arc where T: GenericMountGuard, @@ -38,7 +36,7 @@ where fn path(&self) -> &Path { (&**self).path() } - async fn unmount(mut self) -> Result<(), Error> { + async fn unmount(self) -> Result<(), Error> { if let Ok(guard) = Arc::try_unwrap(self) { guard.unmount().await?; } @@ -102,12 +100,11 @@ impl Drop for MountGuard { } } } -#[async_trait::async_trait] impl GenericMountGuard for MountGuard { fn path(&self) -> &Path { &self.mountpoint } - async fn unmount(mut self) -> Result<(), Error> { + async fn unmount(self) -> Result<(), Error> { MountGuard::unmount(self, false).await } } @@ -165,12 +162,11 @@ impl TmpMountGuard { std::mem::replace(self, unmounted) } } -#[async_trait::async_trait] impl GenericMountGuard for TmpMountGuard { fn path(&self) -> &Path { self.guard.path() } - async fn unmount(mut self) -> Result<(), Error> { + async fn unmount(self) -> Result<(), Error> { self.guard.unmount().await } } @@ -187,12 +183,11 @@ impl SubPath { Self { guard, path } } } -#[async_trait::async_trait] impl GenericMountGuard for SubPath { fn path(&self) -> &Path { self.path.as_path() } - async fn unmount(mut self) -> Result<(), Error> { + async fn unmount(self) -> Result<(), Error> { self.guard.unmount().await } } diff --git a/core/startos/src/init.rs b/core/startos/src/init.rs index 694d4e3a3..97c674ac5 100644 --- a/core/startos/src/init.rs +++ b/core/startos/src/init.rs @@ -242,7 +242,7 @@ pub async fn init(cfg: &ServerConfig) -> Result { let should_rebuild = tokio::fs::metadata(SYSTEM_REBUILD_PATH).await.is_ok() || &*server_info.version < &emver::Version::new(0, 3, 2, 0) - || (*ARCH == "x86_64" && &*server_info.version < &emver::Version::new(0, 3, 4, 0)); + || (ARCH == "x86_64" && &*server_info.version < &emver::Version::new(0, 3, 4, 0)); let log_dir = cfg.datadir().join("main/logs"); if tokio::fs::metadata(&log_dir).await.is_err() { diff --git a/core/startos/src/install/mod.rs b/core/startos/src/install/mod.rs index c4e4452b7..bb7d1a02e 100644 --- a/core/startos/src/install/mod.rs +++ b/core/startos/src/install/mod.rs @@ -152,7 +152,6 @@ pub async fn install( .await?, ), None, // TODO - true, ) .await?; @@ -262,7 +261,6 @@ pub async fn sideload(ctx: RpcContext) -> Result { if let Err(e) = async { let s9pk = S9pk::deserialize( &file, None, // TODO - true, ) .await?; let _ = id_send.send(s9pk.as_manifest().id.clone()); diff --git a/core/startos/src/lib.rs b/core/startos/src/lib.rs index d60a2db24..0e125af98 100644 --- a/core/startos/src/lib.rs +++ b/core/startos/src/lib.rs @@ -4,12 +4,8 @@ pub const CAP_1_KiB: usize = 1024; pub const CAP_1_MiB: usize = CAP_1_KiB * CAP_1_KiB; pub const CAP_10_MiB: usize = 10 * CAP_1_MiB; pub const HOST_IP: [u8; 4] = [172, 18, 0, 1]; -pub const TARGET: &str = current_platform::CURRENT_PLATFORM; +pub use std::env::consts::ARCH; lazy_static::lazy_static! { - pub static ref ARCH: &'static str = { - let (arch, _) = TARGET.split_once("-").unwrap(); - arch - }; pub static ref PLATFORM: String = { if let Ok(platform) = std::fs::read_to_string("/usr/lib/startos/PLATFORM.txt") { platform diff --git a/core/startos/src/lxc/mod.rs b/core/startos/src/lxc/mod.rs index f64ecebe7..8d37120ba 100644 --- a/core/startos/src/lxc/mod.rs +++ b/core/startos/src/lxc/mod.rs @@ -29,7 +29,7 @@ use crate::disk::mount::filesystem::bind::Bind; use crate::disk::mount::filesystem::block_dev::BlockDev; use crate::disk::mount::filesystem::idmapped::IdMapped; use crate::disk::mount::filesystem::overlayfs::OverlayGuard; -use crate::disk::mount::filesystem::{MountType, ReadWrite}; +use crate::disk::mount::filesystem::{MountType, ReadOnly, ReadWrite}; use crate::disk::mount::guard::{GenericMountGuard, MountGuard, TmpMountGuard}; use crate::disk::mount::util::unmount; use crate::prelude::*; @@ -153,7 +153,7 @@ impl LxcManager { pub struct LxcContainer { manager: Weak, - rootfs: OverlayGuard, + rootfs: OverlayGuard, pub guid: Arc, rpc_bind: TmpMountGuard, log_mount: Option, @@ -184,12 +184,16 @@ impl LxcContainer { .invoke(ErrorKind::Filesystem) .await?; let rootfs = OverlayGuard::mount( - &IdMapped::new( - BlockDev::new("/usr/lib/startos/container-runtime/rootfs.squashfs"), - 0, - 100000, - 65536, - ), + TmpMountGuard::mount( + &IdMapped::new( + BlockDev::new("/usr/lib/startos/container-runtime/rootfs.squashfs"), + 0, + 100000, + 65536, + ), + ReadOnly, + ) + .await?, &rootfs_dir, ) .await?; diff --git a/core/startos/src/os_install/gpt.rs b/core/startos/src/os_install/gpt.rs index 4139b4cf2..01703083b 100644 --- a/core/startos/src/os_install/gpt.rs +++ b/core/startos/src/os_install/gpt.rs @@ -87,7 +87,7 @@ pub async fn partition(disk: &DiskInfo, overwrite: bool) -> Result gpt::partition_types::LINUX_ROOT_X64, "aarch64" => gpt::partition_types::LINUX_ROOT_ARM_64, _ => gpt::partition_types::LINUX_FS, diff --git a/core/startos/src/os_install/mod.rs b/core/startos/src/os_install/mod.rs index c3931b236..4e5c7ed15 100644 --- a/core/startos/src/os_install/mod.rs +++ b/core/startos/src/os_install/mod.rs @@ -366,7 +366,7 @@ pub async fn execute( if tokio::fs::metadata("/sys/firmware/efi").await.is_err() { install.arg("--target=i386-pc"); } else { - match *ARCH { + match ARCH { "x86_64" => install.arg("--target=x86_64-efi"), "aarch64" => install.arg("--target=arm64-efi"), _ => &mut install, diff --git a/core/startos/src/registry/device_info.rs b/core/startos/src/registry/device_info.rs index 7da5bd8b9..51d6ac46b 100644 --- a/core/startos/src/registry/device_info.rs +++ b/core/startos/src/registry/device_info.rs @@ -134,7 +134,7 @@ pub struct HardwareInfo { impl From<&RpcContext> for HardwareInfo { fn from(value: &RpcContext) -> Self { Self { - arch: InternedString::intern(&**crate::ARCH), + arch: InternedString::intern(crate::ARCH), ram: value.hardware.ram, devices: value .hardware diff --git a/core/startos/src/registry/package/add.rs b/core/startos/src/registry/package/add.rs index 6a1050b99..d28aeaaa4 100644 --- a/core/startos/src/registry/package/add.rs +++ b/core/startos/src/registry/package/add.rs @@ -53,7 +53,6 @@ pub async fn add_package( let s9pk = S9pk::deserialize( &Arc::new(HttpSource::new(ctx.client.clone(), url.clone()).await?), Some(&commitment), - false, ) .await?; @@ -109,7 +108,7 @@ pub async fn cli_add_package( .. }: HandlerArgs, ) -> Result<(), Error> { - let s9pk = S9pk::open(&file, None, false).await?; + let s9pk = S9pk::open(&file, None).await?; let mut progress = FullProgressTracker::new(); let progress_handle = progress.handle(); @@ -143,7 +142,6 @@ pub async fn cli_add_package( let mut src = S9pk::deserialize( &Arc::new(HttpSource::new(ctx.client.clone(), url.clone()).await?), Some(&commitment), - false, ) .await?; src.serialize(&mut TrackingIO::new(0, tokio::io::sink()), true) diff --git a/core/startos/src/rpc_continuations.rs b/core/startos/src/rpc_continuations.rs index ce8bf43fd..e6b823ef9 100644 --- a/core/startos/src/rpc_continuations.rs +++ b/core/startos/src/rpc_continuations.rs @@ -39,6 +39,11 @@ impl Guid { Some(Guid(InternedString::intern(r))) } } +impl Default for Guid { + fn default() -> Self { + Self::new() + } +} impl AsRef for Guid { fn as_ref(&self) -> &str { self.0.as_ref() diff --git a/core/startos/src/s9pk/merkle_archive/directory_contents.rs b/core/startos/src/s9pk/merkle_archive/directory_contents.rs index 77c3582d9..c5e5c4a7d 100644 --- a/core/startos/src/s9pk/merkle_archive/directory_contents.rs +++ b/core/startos/src/s9pk/merkle_archive/directory_contents.rs @@ -211,7 +211,10 @@ impl DirectoryContents { if !filter(path) { if v.hash.is_none() { return Err(Error::new( - eyre!("cannot filter out unhashed file, run `update_hashes` first"), + eyre!( + "cannot filter out unhashed file {}, run `update_hashes` first", + path.display() + ), ErrorKind::InvalidRequest, )); } diff --git a/core/startos/src/s9pk/merkle_archive/expected.rs b/core/startos/src/s9pk/merkle_archive/expected.rs new file mode 100644 index 000000000..a0f095b7a --- /dev/null +++ b/core/startos/src/s9pk/merkle_archive/expected.rs @@ -0,0 +1,103 @@ + +use std::ffi::OsStr; +use std::path::Path; + +use crate::prelude::*; +use crate::s9pk::merkle_archive::directory_contents::DirectoryContents; +use crate::s9pk::merkle_archive::source::FileSource; +use crate::s9pk::merkle_archive::Entry; + +/// An object for tracking the files expected to be in an s9pk +pub struct Expected<'a, T> { + keep: DirectoryContents<()>, + dir: &'a DirectoryContents, +} +impl<'a, T> Expected<'a, T> { + pub fn new(dir: &'a DirectoryContents,) -> Self { + Self { + keep: DirectoryContents::new(), + dir + } + } +} +impl<'a, T: Clone> Expected<'a, T> { + pub fn check_file(&mut self, path: impl AsRef) -> Result<(), Error> { + if self + .dir + .get_path(path.as_ref()) + .and_then(|e| e.as_file()) + .is_some() + { + self.keep.insert_path(path, Entry::file(()))?; + Ok(()) + } else { + Err(Error::new( + eyre!("file {} missing from archive", path.as_ref().display()), + ErrorKind::ParseS9pk, + )) + } + } + pub fn check_stem( + &mut self, + path: impl AsRef, + mut valid_extension: impl FnMut(Option<&OsStr>) -> bool, + ) -> Result<(), Error> { + let (dir, stem) = if let Some(parent) = path.as_ref().parent().filter(|p| *p != Path::new("")) { + ( + self.dir + .get_path(parent) + .and_then(|e| e.as_directory()) + .ok_or_else(|| { + Error::new( + eyre!("directory {} missing from archive", parent.display()), + ErrorKind::ParseS9pk, + ) + })?, + path.as_ref().strip_prefix(parent).unwrap(), + ) + } else { + (self.dir, path.as_ref()) + }; + let name = dir + .with_stem(&stem.as_os_str().to_string_lossy()) + .filter(|(_, e)| e.as_file().is_some()) + .try_fold( + Err(Error::new( + eyre!( + "file {} with valid extension missing from archive", + path.as_ref().display() + ), + ErrorKind::ParseS9pk, + )), + |acc, (name, _)| + if valid_extension(Path::new(&*name).extension()) { + match acc { + Ok(_) => Err(Error::new( + eyre!( + "more than one file matching {} with valid extension in archive", + path.as_ref().display() + ), + ErrorKind::ParseS9pk, + )), + Err(_) => Ok(Ok(name)) + } + } else { + Ok(acc) + } + )??; + self.keep + .insert_path(path.as_ref().with_file_name(name), Entry::file(()))?; + Ok(()) + } + pub fn into_filter(self) -> Filter { + Filter(self.keep) + } +} + +pub struct Filter(DirectoryContents<()>); +impl Filter { + pub fn keep_checked(&self, dir: &mut DirectoryContents) -> Result<(), Error> { + dir.filter(|path| self.0.get_path(path).is_some()) + } +} + diff --git a/core/startos/src/s9pk/merkle_archive/mod.rs b/core/startos/src/s9pk/merkle_archive/mod.rs index 1c0d6b786..00ead65c5 100644 --- a/core/startos/src/s9pk/merkle_archive/mod.rs +++ b/core/startos/src/s9pk/merkle_archive/mod.rs @@ -19,6 +19,7 @@ use crate::util::serde::Base64; use crate::CAP_1_MiB; pub mod directory_contents; +pub mod expected; pub mod file_contents; pub mod hash; pub mod sink; @@ -217,6 +218,9 @@ impl Entry { pub fn file(source: S) -> Self { Self::new(EntryContents::File(FileContents::new(source))) } + pub fn directory(directory: DirectoryContents) -> Self { + Self::new(EntryContents::Directory(directory)) + } pub fn hash(&self) -> Option<(Hash, u64)> { self.hash } diff --git a/core/startos/src/s9pk/merkle_archive/source/mod.rs b/core/startos/src/s9pk/merkle_archive/source/mod.rs index f6922d109..0a00b18dd 100644 --- a/core/startos/src/s9pk/merkle_archive/source/mod.rs +++ b/core/startos/src/s9pk/merkle_archive/source/mod.rs @@ -280,3 +280,8 @@ impl FileSource for Section { self.source.copy_to(self.position, self.size, w).await } } + +pub type DynRead = Box; +pub fn into_dyn_read(r: R) -> DynRead { + Box::new(r) +} diff --git a/core/startos/src/s9pk/merkle_archive/source/multi_cursor_file.rs b/core/startos/src/s9pk/merkle_archive/source/multi_cursor_file.rs index 9cc162f0e..92eb40f9d 100644 --- a/core/startos/src/s9pk/merkle_archive/source/multi_cursor_file.rs +++ b/core/startos/src/s9pk/merkle_archive/source/multi_cursor_file.rs @@ -97,7 +97,7 @@ impl ArchiveSource for MultiCursorFile { .ok() .map(|m| m.len()) } - async fn fetch_all(&self) -> Result { + async fn fetch_all(&self) -> Result { use tokio::io::AsyncSeekExt; let mut file = self.cursor().await?; diff --git a/core/startos/src/s9pk/rpc.rs b/core/startos/src/s9pk/rpc.rs index 89cfc9b5a..fac9e6724 100644 --- a/core/startos/src/s9pk/rpc.rs +++ b/core/startos/src/s9pk/rpc.rs @@ -1,32 +1,26 @@ -use std::collections::BTreeSet; -use std::path::{Path, PathBuf}; -use std::sync::Arc; +use std::path::PathBuf; use clap::Parser; -use itertools::Itertools; use models::ImageId; use rpc_toolkit::{from_fn_async, Empty, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; use tokio::fs::File; -use tokio::process::Command; use ts_rs::TS; use crate::context::CliContext; use crate::prelude::*; use crate::s9pk::manifest::Manifest; -use crate::s9pk::merkle_archive::source::DynFileSource; -use crate::s9pk::merkle_archive::Entry; -use crate::s9pk::v2::compat::CONTAINER_TOOL; +use crate::s9pk::v2::pack::ImageConfig; use crate::s9pk::v2::SIG_CONTEXT; use crate::s9pk::S9pk; use crate::util::io::TmpDir; use crate::util::serde::{apply_expr, HandlerExtSerde}; -use crate::util::Invoke; pub const SKIP_ENV: &[&str] = &["TERM", "container", "HOME", "HOSTNAME"]; pub fn s9pk() -> ParentHandler { ParentHandler::new() + .subcommand("pack", from_fn_async(super::v2::pack::pack).no_display()) .subcommand("edit", edit()) .subcommand("inspect", inspect()) } @@ -77,117 +71,21 @@ fn inspect() -> ParentHandler { #[derive(Deserialize, Serialize, Parser, TS)] struct AddImageParams { id: ImageId, - image: String, - arches: Option>, + #[command(flatten)] + config: ImageConfig, } async fn add_image( ctx: CliContext, - AddImageParams { id, image, arches }: AddImageParams, + AddImageParams { id, config }: AddImageParams, S9pkPath { s9pk: s9pk_path }: S9pkPath, ) -> Result<(), Error> { - let mut s9pk = S9pk::from_file(super::load(&ctx, &s9pk_path).await?, false) + let mut s9pk = S9pk::from_file(super::load(&ctx, &s9pk_path).await?) .await? .into_dyn(); - let arches: BTreeSet<_> = arches - .unwrap_or_else(|| vec!["x86_64".to_owned(), "aarch64".to_owned()]) - .into_iter() - .collect(); + s9pk.as_manifest_mut().images.insert(id, config); let tmpdir = TmpDir::new().await?; - for arch in arches { - let sqfs_path = tmpdir.join(format!("image.{arch}.squashfs")); - let docker_platform = if arch == "x86_64" { - "--platform=linux/amd64".to_owned() - } else if arch == "aarch64" { - "--platform=linux/arm64".to_owned() - } else { - format!("--platform=linux/{arch}") - }; - let env = String::from_utf8( - Command::new(CONTAINER_TOOL) - .arg("run") - .arg("--rm") - .arg(&docker_platform) - .arg("--entrypoint") - .arg("env") - .arg(&image) - .invoke(ErrorKind::Docker) - .await?, - )? - .lines() - .filter(|l| { - l.trim() - .split_once("=") - .map_or(false, |(v, _)| !SKIP_ENV.contains(&v)) - }) - .join("\n") - + "\n"; - let workdir = Path::new( - String::from_utf8( - Command::new(CONTAINER_TOOL) - .arg("run") - .arg(&docker_platform) - .arg("--rm") - .arg("--entrypoint") - .arg("pwd") - .arg(&image) - .invoke(ErrorKind::Docker) - .await?, - )? - .trim(), - ) - .to_owned(); - let container_id = String::from_utf8( - Command::new(CONTAINER_TOOL) - .arg("create") - .arg(&docker_platform) - .arg(&image) - .invoke(ErrorKind::Docker) - .await?, - )?; - Command::new("bash") - .arg("-c") - .arg(format!( - "{CONTAINER_TOOL} export {container_id} | mksquashfs - {sqfs} -tar", - container_id = container_id.trim(), - sqfs = sqfs_path.display() - )) - .invoke(ErrorKind::Docker) - .await?; - Command::new(CONTAINER_TOOL) - .arg("rm") - .arg(container_id.trim()) - .invoke(ErrorKind::Docker) - .await?; - let archive = s9pk.as_archive_mut(); - archive.set_signer(ctx.developer_key()?.clone(), SIG_CONTEXT); - archive.contents_mut().insert_path( - Path::new("images") - .join(&arch) - .join(&id) - .with_extension("squashfs"), - Entry::file(DynFileSource::new(sqfs_path)), - )?; - archive.contents_mut().insert_path( - Path::new("images") - .join(&arch) - .join(&id) - .with_extension("env"), - Entry::file(DynFileSource::new(Arc::<[u8]>::from(Vec::from(env)))), - )?; - archive.contents_mut().insert_path( - Path::new("images") - .join(&arch) - .join(&id) - .with_extension("json"), - Entry::file(DynFileSource::new(Arc::<[u8]>::from( - serde_json::to_vec(&serde_json::json!({ - "workdir": workdir - })) - .with_kind(ErrorKind::Serialization)?, - ))), - )?; - } - s9pk.as_manifest_mut().images.insert(id); + s9pk.load_images(&tmpdir).await?; + s9pk.validate_and_filter(None)?; let tmp_path = s9pk_path.with_extension("s9pk.tmp"); let mut tmp_file = File::create(&tmp_path).await?; s9pk.serialize(&mut tmp_file, true).await?; @@ -206,7 +104,7 @@ async fn edit_manifest( EditManifestParams { expression }: EditManifestParams, S9pkPath { s9pk: s9pk_path }: S9pkPath, ) -> Result { - let mut s9pk = S9pk::from_file(super::load(&ctx, &s9pk_path).await?, false).await?; + let mut s9pk = S9pk::from_file(super::load(&ctx, &s9pk_path).await?).await?; let old = serde_json::to_value(s9pk.as_manifest()).with_kind(ErrorKind::Serialization)?; *s9pk.as_manifest_mut() = serde_json::from_value(apply_expr(old.into(), &expression)?.into()) .with_kind(ErrorKind::Serialization)?; @@ -227,7 +125,7 @@ async fn file_tree( _: Empty, S9pkPath { s9pk }: S9pkPath, ) -> Result, Error> { - let s9pk = S9pk::from_file(super::load(&ctx, &s9pk).await?, false).await?; + let s9pk = S9pk::from_file(super::load(&ctx, &s9pk).await?).await?; Ok(s9pk.as_archive().contents().file_paths("")) } @@ -244,7 +142,7 @@ async fn cat( ) -> Result<(), Error> { use crate::s9pk::merkle_archive::source::FileSource; - let s9pk = S9pk::from_file(super::load(&ctx, &s9pk).await?, false).await?; + let s9pk = S9pk::from_file(super::load(&ctx, &s9pk).await?).await?; tokio::io::copy( &mut s9pk .as_archive() @@ -266,6 +164,6 @@ async fn inspect_manifest( _: Empty, S9pkPath { s9pk }: S9pkPath, ) -> Result { - let s9pk = S9pk::from_file(super::load(&ctx, &s9pk).await?, false).await?; + let s9pk = S9pk::from_file(super::load(&ctx, &s9pk).await?).await?; Ok(s9pk.as_manifest().clone()) } diff --git a/core/startos/src/s9pk/v2/compat.rs b/core/startos/src/s9pk/v2/compat.rs index 5d4ad2f44..835c86b87 100644 --- a/core/startos/src/s9pk/v2/compat.rs +++ b/core/startos/src/s9pk/v2/compat.rs @@ -1,6 +1,5 @@ -use std::collections::{BTreeMap, BTreeSet}; -use std::io::Cursor; -use std::path::{Path, PathBuf}; +use std::collections::BTreeMap; +use std::path::Path; use std::sync::Arc; use itertools::Itertools; @@ -14,49 +13,18 @@ use crate::prelude::*; use crate::s9pk::manifest::Manifest; use crate::s9pk::merkle_archive::directory_contents::DirectoryContents; use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile; -use crate::s9pk::merkle_archive::source::{FileSource, Section}; +use crate::s9pk::merkle_archive::source::Section; use crate::s9pk::merkle_archive::{Entry, MerkleArchive}; use crate::s9pk::rpc::SKIP_ENV; use crate::s9pk::v1::manifest::{Manifest as ManifestV1, PackageProcedure}; use crate::s9pk::v1::reader::S9pkReader; +use crate::s9pk::v2::pack::{PackSource, CONTAINER_TOOL}; use crate::s9pk::v2::{S9pk, SIG_CONTEXT}; use crate::util::io::TmpDir; use crate::util::Invoke; pub const MAGIC_AND_VERSION: &[u8] = &[0x3b, 0x3b, 0x01]; -#[cfg(not(feature = "docker"))] -pub const CONTAINER_TOOL: &str = "podman"; - -#[cfg(feature = "docker")] -pub const CONTAINER_TOOL: &str = "docker"; - -type DynRead = Box; -fn into_dyn_read(r: R) -> DynRead { - Box::new(r) -} - -#[derive(Clone)] -enum CompatSource { - Buffered(Arc<[u8]>), - File(PathBuf), -} -impl FileSource for CompatSource { - type Reader = Box; - async fn size(&self) -> Result { - match self { - Self::Buffered(a) => Ok(a.len() as u64), - Self::File(f) => Ok(tokio::fs::metadata(f).await?.len()), - } - } - async fn reader(&self) -> Result { - match self { - Self::Buffered(a) => Ok(into_dyn_read(Cursor::new(a.clone()))), - Self::File(f) => Ok(into_dyn_read(File::open(f).await?)), - } - } -} - impl S9pk> { #[instrument(skip_all)] pub async fn from_v1( @@ -66,7 +34,7 @@ impl S9pk> { ) -> Result { let scratch_dir = TmpDir::new().await?; - let mut archive = DirectoryContents::::new(); + let mut archive = DirectoryContents::::new(); // manifest.json let manifest_raw = reader.manifest().await?; @@ -88,21 +56,21 @@ impl S9pk> { let license: Arc<[u8]> = reader.license().await?.to_vec().await?.into(); archive.insert_path( "LICENSE.md", - Entry::file(CompatSource::Buffered(license.into())), + Entry::file(PackSource::Buffered(license.into())), )?; // instructions.md let instructions: Arc<[u8]> = reader.instructions().await?.to_vec().await?.into(); archive.insert_path( "instructions.md", - Entry::file(CompatSource::Buffered(instructions.into())), + Entry::file(PackSource::Buffered(instructions.into())), )?; // icon.md let icon: Arc<[u8]> = reader.icon().await?.to_vec().await?.into(); archive.insert_path( format!("icon.{}", manifest.assets.icon_type()), - Entry::file(CompatSource::Buffered(icon.into())), + Entry::file(PackSource::Buffered(icon.into())), )?; // images @@ -122,7 +90,9 @@ impl S9pk> { .invoke(ErrorKind::Docker) .await?; for (image, system) in &images { - new_manifest.images.insert(image.clone()); + let mut image_config = new_manifest.images.remove(image).unwrap_or_default(); + image_config.arch.insert(arch.as_str().into()); + new_manifest.images.insert(image.clone(), image_config); let sqfs_path = images_dir.join(image).with_extension("squashfs"); let image_name = if *system { format!("start9/{}:latest", image) @@ -190,21 +160,21 @@ impl S9pk> { .join(&arch) .join(&image) .with_extension("squashfs"), - Entry::file(CompatSource::File(sqfs_path)), + Entry::file(PackSource::File(sqfs_path)), )?; archive.insert_path( Path::new("images") .join(&arch) .join(&image) .with_extension("env"), - Entry::file(CompatSource::Buffered(Vec::from(env).into())), + Entry::file(PackSource::Buffered(Vec::from(env).into())), )?; archive.insert_path( Path::new("images") .join(&arch) .join(&image) .with_extension("json"), - Entry::file(CompatSource::Buffered( + Entry::file(PackSource::Buffered( serde_json::to_vec(&serde_json::json!({ "workdir": workdir })) @@ -240,7 +210,7 @@ impl S9pk> { .await?; archive.insert_path( Path::new("assets").join(&asset_id), - Entry::file(CompatSource::File(sqfs_path)), + Entry::file(PackSource::File(sqfs_path)), )?; } @@ -267,12 +237,12 @@ impl S9pk> { .await?; archive.insert_path( Path::new("javascript.squashfs"), - Entry::file(CompatSource::File(sqfs_path)), + Entry::file(PackSource::File(sqfs_path)), )?; archive.insert_path( "manifest.json", - Entry::file(CompatSource::Buffered( + Entry::file(PackSource::Buffered( serde_json::to_vec::(&new_manifest) .with_kind(ErrorKind::Serialization)? .into(), @@ -289,7 +259,6 @@ impl S9pk> { Ok(S9pk::deserialize( &MultiCursorFile::from(File::open(destination.as_ref()).await?), None, - false, ) .await?) } @@ -310,7 +279,7 @@ impl From for Manifest { marketing_site: value.marketing_site.unwrap_or_else(|| default_url.clone()), donation_url: value.donation_url, description: value.description, - images: BTreeSet::new(), + images: BTreeMap::new(), assets: value .volumes .iter() diff --git a/core/startos/src/s9pk/v2/manifest.rs b/core/startos/src/s9pk/v2/manifest.rs index b5ada621b..77e48c126 100644 --- a/core/startos/src/s9pk/v2/manifest.rs +++ b/core/startos/src/s9pk/v2/manifest.rs @@ -1,10 +1,11 @@ use std::collections::{BTreeMap, BTreeSet}; +use std::path::Path; use color_eyre::eyre::eyre; use helpers::const_true; use imbl_value::InternedString; pub use models::PackageId; -use models::{ImageId, VolumeId}; +use models::{mime, ImageId, VolumeId}; use serde::{Deserialize, Serialize}; use ts_rs::TS; use url::Url; @@ -12,6 +13,9 @@ use url::Url; use crate::dependencies::Dependencies; use crate::prelude::*; use crate::s9pk::git_hash::GitHash; +use crate::s9pk::merkle_archive::directory_contents::DirectoryContents; +use crate::s9pk::merkle_archive::expected::{Expected, Filter}; +use crate::s9pk::v2::pack::ImageConfig; use crate::util::serde::Regex; use crate::util::VersionString; use crate::version::{Current, VersionT}; @@ -42,7 +46,7 @@ pub struct Manifest { #[ts(type = "string | null")] pub donation_url: Option, pub description: Description, - pub images: BTreeSet, + pub images: BTreeMap, pub assets: BTreeSet, // TODO: AssetsId pub volumes: BTreeSet, #[serde(default)] @@ -59,6 +63,83 @@ pub struct Manifest { #[serde(default = "const_true")] pub has_config: bool, } +impl Manifest { + pub fn validate_for<'a, T: Clone>( + &self, + arch: Option<&str>, + archive: &'a DirectoryContents, + ) -> Result { + let mut expected = Expected::new(archive); + expected.check_file("manifest.json")?; + expected.check_stem("icon", |ext| { + ext.and_then(|e| e.to_str()) + .and_then(mime) + .map_or(false, |mime| mime.starts_with("image/")) + })?; + expected.check_file("LICENSE.md")?; + expected.check_file("instructions.md")?; + expected.check_file("javascript.squashfs")?; + for assets in &self.assets { + expected.check_file(Path::new("assets").join(assets).with_extension("squashfs"))?; + } + for (image_id, config) in &self.images { + let mut check_arch = |arch: &str| { + let mut arch = arch; + if let Err(e) = expected.check_file( + Path::new("images") + .join(arch) + .join(image_id) + .with_extension("squashfs"), + ) { + if let Some(emulate_as) = &config.emulate_missing_as { + expected.check_file( + Path::new("images") + .join(arch) + .join(image_id) + .with_extension("squashfs"), + )?; + arch = &**emulate_as; + } else { + return Err(e); + } + } + expected.check_file( + Path::new("images") + .join(arch) + .join(image_id) + .with_extension("json"), + )?; + expected.check_file( + Path::new("images") + .join(arch) + .join(image_id) + .with_extension("env"), + )?; + Ok(()) + }; + if let Some(arch) = arch { + check_arch(arch)?; + } else if let Some(arches) = &self.hardware_requirements.arch { + for arch in arches { + check_arch(arch)?; + } + } else if let Some(arch) = config.emulate_missing_as.as_deref() { + if !config.arch.contains(arch) { + return Err(Error::new( + eyre!("`emulateMissingAs` must match an included `arch`"), + ErrorKind::ParseS9pk, + )); + } + for arch in &config.arch { + check_arch(&arch)?; + } + } else { + return Err(Error::new(eyre!("`emulateMissingAs` required for all images if no `arch` specified in `hardwareRequirements`"), ErrorKind::ParseS9pk)); + } + } + Ok(expected.into_filter()) + } +} #[derive(Clone, Debug, Default, Deserialize, Serialize, TS)] #[serde(rename_all = "camelCase")] diff --git a/core/startos/src/s9pk/v2/mod.rs b/core/startos/src/s9pk/v2/mod.rs index e206c8553..a1183efa8 100644 --- a/core/startos/src/s9pk/v2/mod.rs +++ b/core/startos/src/s9pk/v2/mod.rs @@ -14,7 +14,8 @@ use crate::s9pk::merkle_archive::sink::Sink; use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile; use crate::s9pk::merkle_archive::source::{ArchiveSource, DynFileSource, FileSource, Section}; use crate::s9pk::merkle_archive::{Entry, MerkleArchive}; -use crate::ARCH; +use crate::s9pk::v2::pack::{ImageSource, PackSource}; +use crate::util::io::TmpDir; const MAGIC_AND_VERSION: &[u8] = &[0x3b, 0x3b, 0x02]; @@ -22,6 +23,7 @@ pub const SIG_CONTEXT: &str = "s9pk"; pub mod compat; pub mod manifest; +pub mod pack; /** / @@ -34,10 +36,14 @@ pub mod manifest; │ └── .squashfs (xN) └── images └── + ├── .json (xN) ├── .env (xN) └── .squashfs (xN) */ +// this sorts the s9pk to optimize such that the parts that are used first appear earlier in the s9pk +// this is useful for manipulating an s9pk while partially downloaded on a source that does not support +// random access fn priority(s: &str) -> Option { match s { "manifest.json" => Some(0), @@ -51,26 +57,6 @@ fn priority(s: &str) -> Option { } } -fn filter(p: &Path) -> bool { - match p.iter().count() { - 1 if p.file_name() == Some(OsStr::new("manifest.json")) => true, - 1 if p.file_stem() == Some(OsStr::new("icon")) => true, - 1 if p.file_name() == Some(OsStr::new("LICENSE.md")) => true, - 1 if p.file_name() == Some(OsStr::new("instructions.md")) => true, - 1 if p.file_name() == Some(OsStr::new("javascript.squashfs")) => true, - 1 if p.file_name() == Some(OsStr::new("assets")) => true, - 1 if p.file_name() == Some(OsStr::new("images")) => true, - 2 if p.parent() == Some(Path::new("assets")) => { - p.extension().map_or(false, |ext| ext == "squashfs") - } - 2 if p.parent() == Some(Path::new("images")) => p.file_name() == Some(OsStr::new(&*ARCH)), - 3 if p.parent() == Some(&*Path::new("images").join(&*ARCH)) => p - .extension() - .map_or(false, |ext| ext == "squashfs" || ext == "env"), - _ => false, - } -} - #[derive(Clone)] pub struct S9pk> { pub manifest: Manifest, @@ -108,6 +94,11 @@ impl S9pk { }) } + pub fn validate_and_filter(&mut self, arch: Option<&str>) -> Result<(), Error> { + let filter = self.manifest.validate_for(arch, self.archive.contents())?; + filter.keep_checked(self.archive.contents_mut()) + } + pub async fn icon(&self) -> Result<(InternedString, FileContents), Error> { let mut best_icon = None; for (path, icon) in self @@ -174,12 +165,37 @@ impl S9pk { } } +impl + FileSource + Clone> S9pk { + pub async fn load_images(&mut self, tmpdir: &TmpDir) -> Result<(), Error> { + let id = &self.manifest.id; + let version = &self.manifest.version; + for (image_id, image_config) in &mut self.manifest.images { + self.manifest_dirty = true; + for arch in &image_config.arch { + image_config + .source + .load( + tmpdir, + id, + version, + image_id, + arch, + self.archive.contents_mut(), + ) + .await?; + } + image_config.source = ImageSource::Packed; + } + + Ok(()) + } +} + impl S9pk> { #[instrument(skip_all)] pub async fn deserialize( source: &S, commitment: Option<&MerkleArchiveCommitment>, - apply_filter: bool, ) -> Result { use tokio::io::AsyncReadExt; @@ -201,10 +217,6 @@ impl S9pk> { let mut archive = MerkleArchive::deserialize(source, SIG_CONTEXT, &mut header, commitment).await?; - if apply_filter { - archive.filter(filter)?; - } - archive.sort_by(|a, b| match (priority(a), priority(b)) { (Some(a), Some(b)) => a.cmp(&b), (Some(_), None) => std::cmp::Ordering::Less, @@ -216,15 +228,11 @@ impl S9pk> { } } impl S9pk { - pub async fn from_file(file: File, apply_filter: bool) -> Result { - Self::deserialize(&MultiCursorFile::from(file), None, apply_filter).await + pub async fn from_file(file: File) -> Result { + Self::deserialize(&MultiCursorFile::from(file), None).await } - pub async fn open( - path: impl AsRef, - id: Option<&PackageId>, - apply_filter: bool, - ) -> Result { - let res = Self::from_file(tokio::fs::File::open(path).await?, apply_filter).await?; + pub async fn open(path: impl AsRef, id: Option<&PackageId>) -> Result { + let res = Self::from_file(tokio::fs::File::open(path).await?).await?; if let Some(id) = id { ensure_code!( &res.as_manifest().id == id, diff --git a/core/startos/src/s9pk/v2/pack.rs b/core/startos/src/s9pk/v2/pack.rs new file mode 100644 index 000000000..18fe0472b --- /dev/null +++ b/core/startos/src/s9pk/v2/pack.rs @@ -0,0 +1,536 @@ +use std::collections::BTreeSet; +use std::ffi::OsStr; +use std::io::Cursor; +use std::path::{Path, PathBuf}; +use std::sync::Arc; + +use clap::Parser; +use futures::future::{ready, BoxFuture}; +use futures::{FutureExt, TryStreamExt}; +use imbl_value::InternedString; +use models::{ImageId, PackageId, VersionString}; +use serde::{Deserialize, Serialize}; +use tokio::fs::File; +use tokio::io::AsyncRead; +use tokio::process::Command; +use tokio::sync::OnceCell; +use tokio_stream::wrappers::ReadDirStream; +use ts_rs::TS; + +use crate::context::CliContext; +use crate::prelude::*; +use crate::rpc_continuations::Guid; +use crate::s9pk::merkle_archive::directory_contents::DirectoryContents; +use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile; +use crate::s9pk::merkle_archive::source::{ + into_dyn_read, ArchiveSource, DynFileSource, FileSource, +}; +use crate::s9pk::merkle_archive::{Entry, MerkleArchive}; +use crate::s9pk::v2::SIG_CONTEXT; +use crate::s9pk::S9pk; +use crate::util::io::TmpDir; +use crate::util::Invoke; + +#[cfg(not(feature = "docker"))] +pub const CONTAINER_TOOL: &str = "podman"; + +#[cfg(feature = "docker")] +pub const CONTAINER_TOOL: &str = "docker"; + +pub struct SqfsDir { + path: PathBuf, + tmpdir: Arc, + sqfs: OnceCell, +} +impl SqfsDir { + pub fn new(path: PathBuf, tmpdir: Arc) -> Self { + Self { + path, + tmpdir, + sqfs: OnceCell::new(), + } + } + async fn file(&self) -> Result<&MultiCursorFile, Error> { + self.sqfs + .get_or_try_init(|| async move { + let guid = Guid::new(); + let path = self.tmpdir.join(guid.as_ref()).with_extension("squashfs"); + let mut cmd = Command::new("mksquashfs"); + if self.path.extension().and_then(|s| s.to_str()) == Some("tar") { + cmd.arg("-tar"); + } + cmd.arg(&self.path) + .arg(&path) + .invoke(ErrorKind::Filesystem) + .await?; + Ok(MultiCursorFile::from( + File::open(&path) + .await + .with_ctx(|_| (ErrorKind::Filesystem, path.display()))?, + )) + }) + .await + } +} + +#[derive(Clone)] +pub enum PackSource { + Buffered(Arc<[u8]>), + File(PathBuf), + Squashfs(Arc), +} +impl FileSource for PackSource { + type Reader = Box; + async fn size(&self) -> Result { + match self { + Self::Buffered(a) => Ok(a.len() as u64), + Self::File(f) => Ok(tokio::fs::metadata(f) + .await + .with_ctx(|_| (ErrorKind::Filesystem, f.display()))? + .len()), + Self::Squashfs(dir) => dir + .file() + .await + .with_ctx(|_| (ErrorKind::Filesystem, dir.path.display()))? + .size() + .await + .or_not_found("file metadata"), + } + } + async fn reader(&self) -> Result { + match self { + Self::Buffered(a) => Ok(into_dyn_read(Cursor::new(a.clone()))), + Self::File(f) => Ok(into_dyn_read( + File::open(f) + .await + .with_ctx(|_| (ErrorKind::Filesystem, f.display()))?, + )), + Self::Squashfs(dir) => dir.file().await?.fetch_all().await.map(into_dyn_read), + } + } +} +impl From for DynFileSource { + fn from(value: PackSource) -> Self { + DynFileSource::new(value) + } +} + +#[derive(Deserialize, Serialize, Parser)] +pub struct PackParams { + pub path: Option, + #[arg(short = 'o', long = "output")] + pub output: Option, + #[arg(long = "javascript")] + pub javascript: Option, + #[arg(long = "icon")] + pub icon: Option, + #[arg(long = "license")] + pub license: Option, + #[arg(long = "instructions")] + pub instructions: Option, + #[arg(long = "assets")] + pub assets: Option, +} +impl PackParams { + fn path(&self) -> &Path { + self.path.as_deref().unwrap_or(Path::new(".")) + } + fn output(&self, id: &PackageId) -> PathBuf { + self.output + .as_ref() + .cloned() + .unwrap_or_else(|| self.path().join(id).with_extension("s9pk")) + } + fn javascript(&self) -> PathBuf { + self.javascript + .as_ref() + .cloned() + .unwrap_or_else(|| self.path().join("javascript")) + } + async fn icon(&self) -> Result { + if let Some(icon) = &self.icon { + Ok(icon.clone()) + } else { + ReadDirStream::new(tokio::fs::read_dir(self.path()).await?).try_filter(|x| ready(x.path().file_stem() == Some(OsStr::new("icon")))).map_err(Error::from).try_fold(Err(Error::new(eyre!("icon not found"), ErrorKind::NotFound)), |acc, x| async move { match acc { + Ok(_) => Err(Error::new(eyre!("multiple icons found in working directory, please specify which to use with `--icon`"), ErrorKind::InvalidRequest)), + Err(e) => Ok({ + let path = x.path(); + if path.file_stem().and_then(|s| s.to_str()) == Some("icon") { + Ok(path) + } else { + Err(e) + } + }) + }}).await? + } + } + fn license(&self) -> PathBuf { + self.license + .as_ref() + .cloned() + .unwrap_or_else(|| self.path().join("LICENSE.md")) + } + fn instructions(&self) -> PathBuf { + self.instructions + .as_ref() + .cloned() + .unwrap_or_else(|| self.path().join("instructions.md")) + } + fn assets(&self) -> PathBuf { + self.assets + .as_ref() + .cloned() + .unwrap_or_else(|| self.path().join("assets")) + } +} + +#[derive(Debug, Clone, Deserialize, Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct ImageConfig { + pub source: ImageSource, + #[ts(type = "string[]")] + pub arch: BTreeSet, + #[ts(type = "string | null")] + pub emulate_missing_as: Option, +} +impl Default for ImageConfig { + fn default() -> Self { + Self { + source: ImageSource::Packed, + arch: BTreeSet::new(), + emulate_missing_as: None, + } + } +} + +#[derive(Parser)] +struct CliImageConfig { + #[arg(long, conflicts_with("docker-tag"))] + docker_build: bool, + #[arg(long, requires("docker-build"))] + dockerfile: Option, + #[arg(long, requires("docker-build"))] + workdir: Option, + #[arg(long, conflicts_with_all(["dockerfile", "workdir"]))] + docker_tag: Option, + #[arg(long)] + arch: Vec, + #[arg(long)] + emulate_missing_as: Option, +} +impl TryFrom for ImageConfig { + type Error = clap::Error; + fn try_from(value: CliImageConfig) -> Result { + let res = Self { + source: if value.docker_build { + ImageSource::DockerBuild { + dockerfile: value.dockerfile, + workdir: value.workdir, + } + } else if let Some(tag) = value.docker_tag { + ImageSource::DockerTag(tag) + } else { + ImageSource::Packed + }, + arch: value.arch.into_iter().collect(), + emulate_missing_as: value.emulate_missing_as, + }; + res.emulate_missing_as + .as_ref() + .map(|a| { + if !res.arch.contains(a) { + Err(clap::Error::raw( + clap::error::ErrorKind::InvalidValue, + "`emulate-missing-as` must match one of the provided `arch`es", + )) + } else { + Ok(()) + } + }) + .transpose()?; + Ok(res) + } +} +impl clap::Args for ImageConfig { + fn augment_args(cmd: clap::Command) -> clap::Command { + CliImageConfig::augment_args(cmd) + } + fn augment_args_for_update(cmd: clap::Command) -> clap::Command { + CliImageConfig::augment_args_for_update(cmd) + } +} +impl clap::FromArgMatches for ImageConfig { + fn from_arg_matches(matches: &clap::ArgMatches) -> Result { + Self::try_from(CliImageConfig::from_arg_matches(matches)?) + } + fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> Result<(), clap::Error> { + *self = Self::try_from(CliImageConfig::from_arg_matches(matches)?)?; + Ok(()) + } +} + +#[derive(Debug, Clone, Deserialize, Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub enum ImageSource { + Packed, + #[serde(rename_all = "camelCase")] + DockerBuild { + workdir: Option, + dockerfile: Option, + }, + DockerTag(String), +} +impl ImageSource { + #[instrument(skip_all)] + pub fn load<'a, S: From + FileSource + Clone>( + &'a self, + tmpdir: &'a TmpDir, + id: &'a PackageId, + version: &'a VersionString, + image_id: &'a ImageId, + arch: &'a str, + into: &'a mut DirectoryContents, + ) -> BoxFuture<'a, Result<(), Error>> { + #[derive(Deserialize)] + #[serde(rename_all = "PascalCase")] + struct DockerImageConfig { + env: Vec, + #[serde(default)] + working_dir: PathBuf, + #[serde(default)] + user: String, + } + async move { + match self { + ImageSource::Packed => Ok(()), + ImageSource::DockerBuild { + workdir, + dockerfile, + } => { + let workdir = workdir.as_deref().unwrap_or(Path::new(".")); + let dockerfile = dockerfile + .clone() + .unwrap_or_else(|| workdir.join("Dockerfile")); + let docker_platform = if arch == "x86_64" { + "--platform=linux/amd64".to_owned() + } else if arch == "aarch64" { + "--platform=linux/arm64".to_owned() + } else { + format!("--platform=linux/{arch}") + }; + // docker buildx build ${path} -o type=image,name=start9/${id} + let tag = format!("start9/{id}/{image_id}:{version}"); + Command::new(CONTAINER_TOOL) + .arg("build") + .arg(workdir) + .arg("-f") + .arg(dockerfile) + .arg("-t") + .arg(&tag) + .arg(&docker_platform) + .arg("-o") + .arg("type=image") + .capture(false) + .invoke(ErrorKind::Docker) + .await?; + ImageSource::DockerTag(tag.clone()) + .load(tmpdir, id, version, image_id, arch, into) + .await?; + Command::new(CONTAINER_TOOL) + .arg("rmi") + .arg("-f") + .arg(&tag) + .invoke(ErrorKind::Docker) + .await?; + Ok(()) + } + ImageSource::DockerTag(tag) => { + let docker_platform = if arch == "x86_64" { + "--platform=linux/amd64".to_owned() + } else if arch == "aarch64" { + "--platform=linux/arm64".to_owned() + } else { + format!("--platform=linux/{arch}") + }; + let mut inspect_cmd = Command::new(CONTAINER_TOOL); + inspect_cmd + .arg("image") + .arg("inspect") + .arg("--format") + .arg("{{json .Config}}") + .arg(&tag); + let inspect_res = match inspect_cmd.invoke(ErrorKind::Docker).await { + Ok(a) => a, + Err(e) + if { + let msg = e.source.to_string(); + #[cfg(feature = "docker")] + let matches = msg.contains("No such image:"); + #[cfg(not(feature = "docker"))] + let matches = msg.contains(": image not known"); + matches + } => + { + Command::new(CONTAINER_TOOL) + .arg("pull") + .arg(&docker_platform) + .arg(tag) + .capture(false) + .invoke(ErrorKind::Docker) + .await?; + inspect_cmd.invoke(ErrorKind::Docker).await? + } + Err(e) => return Err(e), + }; + let config = serde_json::from_slice::(&inspect_res) + .with_kind(ErrorKind::Deserialization)?; + let base_path = Path::new("images").join(arch).join(image_id); + into.insert_path( + base_path.with_extension("json"), + Entry::file( + PackSource::Buffered( + serde_json::to_vec(&ImageMetadata { + workdir: if config.working_dir == Path::new("") { + "/".into() + } else { + config.working_dir + }, + user: if config.user.is_empty() { + "root".into() + } else { + config.user.into() + }, + }) + .with_kind(ErrorKind::Serialization)? + .into(), + ) + .into(), + ), + )?; + into.insert_path( + base_path.with_extension("env"), + Entry::file( + PackSource::Buffered(config.env.join("\n").into_bytes().into()).into(), + ), + )?; + let dest = tmpdir.join(Guid::new().as_ref()).with_extension("squashfs"); + let container = String::from_utf8( + Command::new(CONTAINER_TOOL) + .arg("create") + .arg(&docker_platform) + .arg(&tag) + .invoke(ErrorKind::Docker) + .await?, + )?; + Command::new(CONTAINER_TOOL) + .arg("export") + .arg(container.trim()) + .pipe(Command::new("mksquashfs").arg("-").arg(&dest).arg("-tar")) + .capture(false) + .invoke(ErrorKind::Docker) + .await?; + Command::new(CONTAINER_TOOL) + .arg("rm") + .arg(container.trim()) + .invoke(ErrorKind::Docker) + .await?; + into.insert_path( + base_path.with_extension("squashfs"), + Entry::file(PackSource::File(dest).into()), + )?; + + Ok(()) + } + } + } + .boxed() + } +} + +#[derive(Debug, Clone, Deserialize, Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct ImageMetadata { + pub workdir: PathBuf, + #[ts(type = "string")] + pub user: InternedString, +} + +#[instrument(skip_all)] +pub async fn pack(ctx: CliContext, params: PackParams) -> Result<(), Error> { + let tmpdir = Arc::new(TmpDir::new().await?); + let mut files = DirectoryContents::::new(); + let js_dir = params.javascript(); + let manifest: Arc<[u8]> = Command::new("node") + .arg("-e") + .arg(format!( + "console.log(JSON.stringify(require('{}/index.js').manifest))", + js_dir.display() + )) + .invoke(ErrorKind::Javascript) + .await? + .into(); + files.insert( + "manifest.json".into(), + Entry::file(PackSource::Buffered(manifest.clone())), + ); + let icon = params.icon().await?; + let icon_ext = icon + .extension() + .or_not_found("icon file extension")? + .to_string_lossy(); + files.insert( + InternedString::from_display(&lazy_format!("icon.{}", icon_ext)), + Entry::file(PackSource::File(icon)), + ); + files.insert( + "LICENSE.md".into(), + Entry::file(PackSource::File(params.license())), + ); + files.insert( + "instructions.md".into(), + Entry::file(PackSource::File(params.instructions())), + ); + files.insert( + "javascript.squashfs".into(), + Entry::file(PackSource::Squashfs(Arc::new(SqfsDir::new( + js_dir, + tmpdir.clone(), + )))), + ); + + let mut s9pk = S9pk::new( + MerkleArchive::new(files, ctx.developer_key()?.clone(), SIG_CONTEXT), + None, + ) + .await?; + + let assets_dir = params.assets(); + for assets in s9pk.as_manifest().assets.clone() { + s9pk.as_archive_mut().contents_mut().insert_path( + Path::new("assets").join(&assets).with_extension("squashfs"), + Entry::file(PackSource::Squashfs(Arc::new(SqfsDir::new( + assets_dir.join(&assets), + tmpdir.clone(), + )))), + )?; + } + + s9pk.load_images(&*tmpdir).await?; + + s9pk.validate_and_filter(None)?; + + s9pk.serialize( + &mut File::create(params.output(&s9pk.as_manifest().id)).await?, + false, + ) + .await?; + + drop(s9pk); + + tmpdir.gc().await?; + + Ok(()) +} diff --git a/core/startos/src/service/action.rs b/core/startos/src/service/action.rs index 5f97685ae..6c5ac4eab 100644 --- a/core/startos/src/service/action.rs +++ b/core/startos/src/service/action.rs @@ -4,6 +4,7 @@ use models::{ActionId, ProcedureName}; use crate::action::ActionResult; use crate::prelude::*; +use crate::rpc_continuations::Guid; use crate::service::config::GetConfig; use crate::service::dependencies::DependencyConfig; use crate::service::{Service, ServiceActor}; @@ -23,13 +24,18 @@ impl Handler for ServiceActor { } async fn handle( &mut self, - Action { id, input }: Action, + id: Guid, + Action { + id: action_id, + input, + }: Action, _: &BackgroundJobQueue, ) -> Self::Response { let container = &self.0.persistent_container; container .execute::( - ProcedureName::RunAction(id), + id, + ProcedureName::RunAction(action_id), input, Some(Duration::from_secs(30)), ) @@ -39,7 +45,20 @@ impl Handler for ServiceActor { } impl Service { - pub async fn action(&self, id: ActionId, input: Value) -> Result { - self.actor.send(Action { id, input }).await? + pub async fn action( + &self, + id: Guid, + action_id: ActionId, + input: Value, + ) -> Result { + self.actor + .send( + id, + Action { + id: action_id, + input, + }, + ) + .await? } } diff --git a/core/startos/src/service/config.rs b/core/startos/src/service/config.rs index 0f166eedb..faa70fc41 100644 --- a/core/startos/src/service/config.rs +++ b/core/startos/src/service/config.rs @@ -5,6 +5,7 @@ use models::ProcedureName; use crate::config::action::ConfigRes; use crate::config::ConfigureContext; use crate::prelude::*; +use crate::rpc_continuations::Guid; use crate::service::dependencies::DependencyConfig; use crate::service::{Service, ServiceActor}; use crate::util::actor::background::BackgroundJobQueue; @@ -19,6 +20,7 @@ impl Handler for ServiceActor { } async fn handle( &mut self, + id: Guid, Configure(ConfigureContext { timeout, config }): Configure, _: &BackgroundJobQueue, ) -> Self::Response { @@ -26,7 +28,7 @@ impl Handler for ServiceActor { let package_id = &self.0.id; container - .execute::(ProcedureName::SetConfig, to_value(&config)?, timeout) + .execute::(id, ProcedureName::SetConfig, to_value(&config)?, timeout) .await .with_kind(ErrorKind::ConfigRulesViolation)?; self.0 @@ -52,10 +54,11 @@ impl Handler for ServiceActor { fn conflicts_with(_: &GetConfig) -> ConflictBuilder { ConflictBuilder::nothing().except::() } - async fn handle(&mut self, _: GetConfig, _: &BackgroundJobQueue) -> Self::Response { + async fn handle(&mut self, id: Guid, _: GetConfig, _: &BackgroundJobQueue) -> Self::Response { let container = &self.0.persistent_container; container .execute::( + id, ProcedureName::GetConfig, Value::Null, Some(Duration::from_secs(30)), // TODO timeout @@ -66,10 +69,10 @@ impl Handler for ServiceActor { } impl Service { - pub async fn configure(&self, ctx: ConfigureContext) -> Result<(), Error> { - self.actor.send(Configure(ctx)).await? + pub async fn configure(&self, id: Guid, ctx: ConfigureContext) -> Result<(), Error> { + self.actor.send(id, Configure(ctx)).await? } - pub async fn get_config(&self) -> Result { - self.actor.send(GetConfig).await? + pub async fn get_config(&self, id: Guid) -> Result { + self.actor.send(id, GetConfig).await? } } diff --git a/core/startos/src/service/control.rs b/core/startos/src/service/control.rs index 0443b80b1..7c4bdf815 100644 --- a/core/startos/src/service/control.rs +++ b/core/startos/src/service/control.rs @@ -1,4 +1,5 @@ use crate::prelude::*; +use crate::rpc_continuations::Guid; use crate::service::config::GetConfig; use crate::service::dependencies::DependencyConfig; use crate::service::start_stop::StartStop; @@ -15,7 +16,7 @@ impl Handler for ServiceActor { .except::() .except::() } - async fn handle(&mut self, _: Start, _: &BackgroundJobQueue) -> Self::Response { + async fn handle(&mut self, _: Guid, _: Start, _: &BackgroundJobQueue) -> Self::Response { self.0.persistent_container.state.send_modify(|x| { x.desired_state = StartStop::Start; }); @@ -23,8 +24,8 @@ impl Handler for ServiceActor { } } impl Service { - pub async fn start(&self) -> Result<(), Error> { - self.actor.send(Start).await + pub async fn start(&self, id: Guid) -> Result<(), Error> { + self.actor.send(id, Start).await } } @@ -36,7 +37,7 @@ impl Handler for ServiceActor { .except::() .except::() } - async fn handle(&mut self, _: Stop, _: &BackgroundJobQueue) -> Self::Response { + async fn handle(&mut self, _: Guid, _: Stop, _: &BackgroundJobQueue) -> Self::Response { let mut transition_state = None; self.0.persistent_container.state.send_modify(|x| { x.desired_state = StartStop::Stop; @@ -51,7 +52,7 @@ impl Handler for ServiceActor { } } impl Service { - pub async fn stop(&self) -> Result<(), Error> { - self.actor.send(Stop).await + pub async fn stop(&self, id: Guid) -> Result<(), Error> { + self.actor.send(id, Stop).await } } diff --git a/core/startos/src/service/dependencies.rs b/core/startos/src/service/dependencies.rs index e71b62917..60fd76ad6 100644 --- a/core/startos/src/service/dependencies.rs +++ b/core/startos/src/service/dependencies.rs @@ -4,35 +4,28 @@ use imbl_value::json; use models::{PackageId, ProcedureName}; use crate::prelude::*; -use crate::service::{Service, ServiceActor}; +use crate::rpc_continuations::Guid; +use crate::service::{Service, ServiceActor, ServiceActorSeed}; use crate::util::actor::background::BackgroundJobQueue; use crate::util::actor::{ConflictBuilder, Handler}; use crate::Config; -pub(super) struct DependencyConfig { - dependency_id: PackageId, - remote_config: Option, -} -impl Handler for ServiceActor { - type Response = Result, Error>; - fn conflicts_with(_: &DependencyConfig) -> ConflictBuilder { - ConflictBuilder::nothing() - } - async fn handle( - &mut self, - DependencyConfig { - dependency_id, - remote_config, - }: DependencyConfig, - _: &BackgroundJobQueue, - ) -> Self::Response { - let container = &self.0.persistent_container; +impl ServiceActorSeed { + async fn dependency_config( + &self, + id: Guid, + dependency_id: PackageId, + remote_config: Option, + ) -> Result, Error> { + let container = &self.persistent_container; container .sanboxed::>( + id.clone(), ProcedureName::UpdateDependency(dependency_id.clone()), json!({ "queryResults": container .execute::( + id, ProcedureName::QueryDependency(dependency_id), Value::Null, Some(Duration::from_secs(30)), @@ -49,17 +42,45 @@ impl Handler for ServiceActor { } } +pub(super) struct DependencyConfig { + dependency_id: PackageId, + remote_config: Option, +} +impl Handler for ServiceActor { + type Response = Result, Error>; + fn conflicts_with(_: &DependencyConfig) -> ConflictBuilder { + ConflictBuilder::nothing() + } + async fn handle( + &mut self, + id: Guid, + DependencyConfig { + dependency_id, + remote_config, + }: DependencyConfig, + _: &BackgroundJobQueue, + ) -> Self::Response { + self.0 + .dependency_config(id, dependency_id, remote_config) + .await + } +} + impl Service { pub async fn dependency_config( &self, + id: Guid, dependency_id: PackageId, remote_config: Option, ) -> Result, Error> { self.actor - .send(DependencyConfig { - dependency_id, - remote_config, - }) + .send( + id, + DependencyConfig { + dependency_id, + remote_config, + }, + ) .await? } } diff --git a/core/startos/src/service/mod.rs b/core/startos/src/service/mod.rs index da641468b..6588de836 100644 --- a/core/startos/src/service/mod.rs +++ b/core/startos/src/service/mod.rs @@ -1,4 +1,5 @@ -use std::sync::Arc; +use std::ops::Deref; +use std::sync::{Arc, Weak}; use std::time::Duration; use chrono::{DateTime, Utc}; @@ -68,13 +69,87 @@ pub enum LoadDisposition { Undo, } +pub struct ServiceRef(Arc); +impl ServiceRef { + pub fn weak(&self) -> Weak { + Arc::downgrade(&self.0) + } + pub async fn uninstall( + self, + target_version: Option, + ) -> Result<(), Error> { + self.seed + .persistent_container + .execute( + Guid::new(), + ProcedureName::Uninit, + to_value(&target_version)?, + None, + ) // TODO timeout + .await?; + let id = self.seed.persistent_container.s9pk.as_manifest().id.clone(); + let ctx = self.seed.ctx.clone(); + self.shutdown().await?; + if target_version.is_none() { + ctx.db + .mutate(|d| d.as_public_mut().as_package_data_mut().remove(&id)) + .await?; + } + Ok(()) + } + pub async fn shutdown(self) -> Result<(), Error> { + if let Some((hdl, shutdown)) = self.seed.persistent_container.rpc_server.send_replace(None) + { + self.seed + .persistent_container + .rpc_client + .request(rpc::Exit, Empty {}) + .await?; + shutdown.shutdown(); + hdl.await.with_kind(ErrorKind::Cancelled)?; + } + let service = Arc::try_unwrap(self.0).map_err(|_| { + Error::new( + eyre!("ServiceActor held somewhere after actor shutdown"), + ErrorKind::Unknown, + ) + })?; + service + .actor + .shutdown(crate::util::actor::PendingMessageStrategy::FinishAll { timeout: None }) // TODO timeout + .await; + Arc::try_unwrap(service.seed) + .map_err(|_| { + Error::new( + eyre!("ServiceActorSeed held somewhere after actor shutdown"), + ErrorKind::Unknown, + ) + })? + .persistent_container + .exit() + .await?; + Ok(()) + } +} +impl Deref for ServiceRef { + type Target = Service; + fn deref(&self) -> &Self::Target { + &*self.0 + } +} +impl From for ServiceRef { + fn from(value: Service) -> Self { + Self(Arc::new(value)) + } +} + pub struct Service { actor: ConcurrentActor, seed: Arc, } impl Service { #[instrument(skip_all)] - async fn new(ctx: RpcContext, s9pk: S9pk, start: StartStop) -> Result { + async fn new(ctx: RpcContext, s9pk: S9pk, start: StartStop) -> Result { let id = s9pk.as_manifest().id.clone(); let persistent_container = PersistentContainer::new( &ctx, s9pk, @@ -89,13 +164,17 @@ impl Service { ctx, synchronized: Arc::new(Notify::new()), }); - seed.persistent_container - .init(Arc::downgrade(&seed)) - .await?; - Ok(Self { + let service: ServiceRef = Self { actor: ConcurrentActor::new(ServiceActor(seed.clone())), seed, - }) + } + .into(); + service + .seed + .persistent_container + .init(service.weak()) + .await?; + Ok(service) } #[instrument(skip_all)] @@ -103,7 +182,7 @@ impl Service { ctx: &RpcContext, id: &PackageId, disposition: LoadDisposition, - ) -> Result, Error> { + ) -> Result, Error> { let handle_installed = { let ctx = ctx.clone(); move |s9pk: S9pk, i: Model| async move { @@ -137,7 +216,7 @@ impl Service { match entry.as_state_info().as_match() { PackageStateMatchModelRef::Installing(_) => { if disposition == LoadDisposition::Retry { - if let Ok(s9pk) = S9pk::open(s9pk_path, Some(id), true).await.map_err(|e| { + if let Ok(s9pk) = S9pk::open(s9pk_path, Some(id)).await.map_err(|e| { tracing::error!("Error opening s9pk for install: {e}"); tracing::debug!("{e:?}") }) { @@ -170,7 +249,7 @@ impl Service { && progress == &Progress::Complete(true) }) { - if let Ok(s9pk) = S9pk::open(&s9pk_path, Some(id), true).await.map_err(|e| { + if let Ok(s9pk) = S9pk::open(&s9pk_path, Some(id)).await.map_err(|e| { tracing::error!("Error opening s9pk for update: {e}"); tracing::debug!("{e:?}") }) { @@ -189,7 +268,7 @@ impl Service { } } } - let s9pk = S9pk::open(s9pk_path, Some(id), true).await?; + let s9pk = S9pk::open(s9pk_path, Some(id)).await?; ctx.db .mutate({ |db| { @@ -214,7 +293,7 @@ impl Service { handle_installed(s9pk, entry).await } PackageStateMatchModelRef::Removing(_) | PackageStateMatchModelRef::Restoring(_) => { - if let Ok(s9pk) = S9pk::open(s9pk_path, Some(id), true).await.map_err(|e| { + if let Ok(s9pk) = S9pk::open(s9pk_path, Some(id)).await.map_err(|e| { tracing::error!("Error opening s9pk for removal: {e}"); tracing::debug!("{e:?}") }) { @@ -225,7 +304,7 @@ impl Service { tracing::debug!("{e:?}") }) { - match service.uninstall(None).await { + match ServiceRef::from(service).uninstall(None).await { Err(e) => { tracing::error!("Error uninstalling service: {e}"); tracing::debug!("{e:?}") @@ -242,7 +321,7 @@ impl Service { Ok(None) } PackageStateMatchModelRef::Installed(_) => { - handle_installed(S9pk::open(s9pk_path, Some(id), true).await?, entry).await + handle_installed(S9pk::open(s9pk_path, Some(id)).await?, entry).await } PackageStateMatchModelRef::Error(e) => Err(Error::new( eyre!("Failed to parse PackageDataEntry, found {e:?}"), @@ -257,7 +336,7 @@ impl Service { s9pk: S9pk, src_version: Option, progress: Option, - ) -> Result { + ) -> Result { let manifest = s9pk.as_manifest().clone(); let developer_key = s9pk.as_archive().signer(); let icon = s9pk.icon_data_url().await?; @@ -265,7 +344,12 @@ impl Service { service .seed .persistent_container - .execute(ProcedureName::Init, to_value(&src_version)?, None) // TODO timeout + .execute( + Guid::new(), + ProcedureName::Init, + to_value(&src_version)?, + None, + ) // TODO timeout .await .with_kind(ErrorKind::MigrationFailed)?; // TODO: handle cancellation if let Some(mut progress) = progress { @@ -301,61 +385,21 @@ impl Service { s9pk: S9pk, backup_source: impl GenericMountGuard, progress: Option, - ) -> Result { + ) -> Result { let service = Service::install(ctx.clone(), s9pk, None, progress).await?; service .actor - .send(transition::restore::Restore { - path: backup_source.path().to_path_buf(), - }) + .send( + Guid::new(), + transition::restore::Restore { + path: backup_source.path().to_path_buf(), + }, + ) .await??; Ok(service) } - pub async fn shutdown(self) -> Result<(), Error> { - self.actor - .shutdown(crate::util::actor::PendingMessageStrategy::FinishAll { timeout: None }) // TODO timeout - .await; - if let Some((hdl, shutdown)) = self.seed.persistent_container.rpc_server.send_replace(None) - { - self.seed - .persistent_container - .rpc_client - .request(rpc::Exit, Empty {}) - .await?; - shutdown.shutdown(); - hdl.await.with_kind(ErrorKind::Cancelled)?; - } - Arc::try_unwrap(self.seed) - .map_err(|_| { - Error::new( - eyre!("ServiceActorSeed held somewhere after actor shutdown"), - ErrorKind::Unknown, - ) - })? - .persistent_container - .exit() - .await?; - Ok(()) - } - - pub async fn uninstall(self, target_version: Option) -> Result<(), Error> { - self.seed - .persistent_container - .execute(ProcedureName::Uninit, to_value(&target_version)?, None) // TODO timeout - .await?; - let id = self.seed.persistent_container.s9pk.as_manifest().id.clone(); - let ctx = self.seed.ctx.clone(); - self.shutdown().await?; - if target_version.is_none() { - ctx.db - .mutate(|d| d.as_public_mut().as_package_data_mut().remove(&id)) - .await?; - } - Ok(()) - } - #[instrument(skip_all)] pub async fn backup(&self, guard: impl GenericMountGuard) -> Result<(), Error> { let id = &self.seed.id; @@ -368,9 +412,12 @@ impl Service { .await?; drop(file); self.actor - .send(transition::backup::Backup { - path: guard.path().to_path_buf(), - }) + .send( + Guid::new(), + transition::backup::Backup { + path: guard.path().to_path_buf(), + }, + ) .await??; Ok(()) } diff --git a/core/startos/src/service/persistent_container.rs b/core/startos/src/service/persistent_container.rs index 03203633a..d9d08e5d3 100644 --- a/core/startos/src/service/persistent_container.rs +++ b/core/startos/src/service/persistent_container.rs @@ -6,8 +6,7 @@ use std::time::Duration; use futures::future::ready; use futures::{Future, FutureExt}; use helpers::NonDetachingJoinHandle; -use imbl_value::InternedString; -use models::{ProcedureName, VolumeId}; +use models::{ImageId, ProcedureName, VolumeId}; use rpc_toolkit::{Empty, Server, ShutdownHandle}; use serde::de::DeserializeOwned; use tokio::fs::File; @@ -24,14 +23,15 @@ use crate::disk::mount::filesystem::idmapped::IdMapped; use crate::disk::mount::filesystem::loop_dev::LoopDev; use crate::disk::mount::filesystem::overlayfs::OverlayGuard; use crate::disk::mount::filesystem::{MountType, ReadOnly}; -use crate::disk::mount::guard::MountGuard; +use crate::disk::mount::guard::{GenericMountGuard, MountGuard}; use crate::lxc::{LxcConfig, LxcContainer, HOST_RPC_SERVER_SOCKET}; use crate::net::net_controller::NetService; use crate::prelude::*; +use crate::rpc_continuations::Guid; use crate::s9pk::merkle_archive::source::FileSource; use crate::s9pk::S9pk; use crate::service::start_stop::StartStop; -use crate::service::{rpc, RunningStatus}; +use crate::service::{rpc, RunningStatus, Service}; use crate::util::rpc_client::UnixRpcClient; use crate::util::Invoke; use crate::volume::{asset_dir, data_dir}; @@ -89,7 +89,8 @@ pub struct PersistentContainer { js_mount: MountGuard, volumes: BTreeMap, assets: BTreeMap, - pub(super) overlays: Arc>>, + pub(super) images: BTreeMap>, + pub(super) overlays: Arc>>>>, pub(super) state: Arc>, pub(super) net_service: Mutex, destroyed: bool, @@ -178,14 +179,62 @@ impl PersistentContainer { .await?, ); } + + let mut images = BTreeMap::new(); let image_path = lxc_container.rootfs_dir().join("media/startos/images"); tokio::fs::create_dir_all(&image_path).await?; - for image in &s9pk.as_manifest().images { + for (image, config) in &s9pk.as_manifest().images { + let mut arch = ARCH; + let mut sqfs_path = Path::new("images") + .join(arch) + .join(image) + .with_extension("squashfs"); + if !s9pk + .as_archive() + .contents() + .get_path(&sqfs_path) + .and_then(|e| e.as_file()) + .is_some() + { + arch = if let Some(arch) = config.emulate_missing_as.as_deref() { + arch + } else { + continue; + }; + sqfs_path = Path::new("images") + .join(arch) + .join(image) + .with_extension("squashfs"); + } + let sqfs = s9pk + .as_archive() + .contents() + .get_path(&sqfs_path) + .and_then(|e| e.as_file()) + .or_not_found(sqfs_path.display())?; + let mountpoint = image_path.join(image); + tokio::fs::create_dir_all(&mountpoint).await?; + Command::new("chown") + .arg("100000:100000") + .arg(&mountpoint) + .invoke(ErrorKind::Filesystem) + .await?; + images.insert( + image.clone(), + Arc::new( + MountGuard::mount( + &IdMapped::new(LoopDev::from(&**sqfs), 0, 100000, 65536), + &mountpoint, + ReadOnly, + ) + .await?, + ), + ); let env_filename = Path::new(image.as_ref()).with_extension("env"); if let Some(env) = s9pk .as_archive() .contents() - .get_path(Path::new("images").join(*ARCH).join(&env_filename)) + .get_path(Path::new("images").join(arch).join(&env_filename)) .and_then(|e| e.as_file()) { env.copy(&mut File::create(image_path.join(&env_filename)).await?) @@ -195,7 +244,7 @@ impl PersistentContainer { if let Some(json) = s9pk .as_archive() .contents() - .get_path(Path::new("images").join(*ARCH).join(&json_filename)) + .get_path(Path::new("images").join(arch).join(&json_filename)) .and_then(|e| e.as_file()) { json.copy(&mut File::create(image_path.join(&json_filename)).await?) @@ -215,6 +264,7 @@ impl PersistentContainer { js_mount, volumes, assets, + images, overlays: Arc::new(Mutex::new(BTreeMap::new())), state: Arc::new(watch::channel(ServiceState::new(start)).0), net_service: Mutex::new(net_service), @@ -257,7 +307,7 @@ impl PersistentContainer { } #[instrument(skip_all)] - pub async fn init(&self, seed: Weak) -> Result<(), Error> { + pub async fn init(&self, seed: Weak) -> Result<(), Error> { let socket_server_context = EffectContext::new(seed); let server = Server::new( move || ready(Ok(socket_server_context.clone())), @@ -330,6 +380,7 @@ impl PersistentContainer { let js_mount = self.js_mount.take(); let volumes = std::mem::take(&mut self.volumes); let assets = std::mem::take(&mut self.assets); + let images = std::mem::take(&mut self.images); let overlays = self.overlays.clone(); let lxc_container = self.lxc_container.take(); self.destroyed = true; @@ -352,6 +403,9 @@ impl PersistentContainer { for (_, overlay) in std::mem::take(&mut *overlays.lock().await) { errs.handle(overlay.unmount(true).await); } + for (_, images) in images { + errs.handle(images.unmount().await); + } errs.handle(js_mount.unmount(true).await); if let Some(lxc_container) = lxc_container { errs.handle(lxc_container.exit().await); @@ -378,6 +432,7 @@ impl PersistentContainer { #[instrument(skip_all)] pub async fn start(&self) -> Result<(), Error> { self.execute( + Guid::new(), ProcedureName::StartMain, Value::Null, Some(Duration::from_secs(5)), // TODO @@ -389,7 +444,7 @@ impl PersistentContainer { #[instrument(skip_all)] pub async fn stop(&self) -> Result { let timeout: Option = self - .execute(ProcedureName::StopMain, Value::Null, None) + .execute(Guid::new(), ProcedureName::StopMain, Value::Null, None) .await?; Ok(timeout.map(|a| *a).unwrap_or(Duration::from_secs(30))) } @@ -397,6 +452,7 @@ impl PersistentContainer { #[instrument(skip_all)] pub async fn execute( &self, + id: Guid, name: ProcedureName, input: Value, timeout: Option, @@ -404,7 +460,7 @@ impl PersistentContainer { where O: DeserializeOwned, { - self._execute(name, input, timeout) + self._execute(id, name, input, timeout) .await .and_then(from_value) } @@ -412,6 +468,7 @@ impl PersistentContainer { #[instrument(skip_all)] pub async fn sanboxed( &self, + id: Guid, name: ProcedureName, input: Value, timeout: Option, @@ -419,7 +476,7 @@ impl PersistentContainer { where O: DeserializeOwned, { - self._sandboxed(name, input, timeout) + self._sandboxed(id, name, input, timeout) .await .and_then(from_value) } @@ -427,13 +484,15 @@ impl PersistentContainer { #[instrument(skip_all)] async fn _execute( &self, + id: Guid, name: ProcedureName, input: Value, timeout: Option, ) -> Result { - let fut = self - .rpc_client - .request(rpc::Execute, rpc::ExecuteParams::new(name, input, timeout)); + let fut = self.rpc_client.request( + rpc::Execute, + rpc::ExecuteParams::new(id, name, input, timeout), + ); Ok(if let Some(timeout) = timeout { tokio::time::timeout(timeout, fut) @@ -447,13 +506,15 @@ impl PersistentContainer { #[instrument(skip_all)] async fn _sandboxed( &self, + id: Guid, name: ProcedureName, input: Value, timeout: Option, ) -> Result { - let fut = self - .rpc_client - .request(rpc::Sandbox, rpc::ExecuteParams::new(name, input, timeout)); + let fut = self.rpc_client.request( + rpc::Sandbox, + rpc::ExecuteParams::new(id, name, input, timeout), + ); Ok(if let Some(timeout) = timeout { tokio::time::timeout(timeout, fut) diff --git a/core/startos/src/service/properties.rs b/core/startos/src/service/properties.rs index 3e795207a..3f5201f1d 100644 --- a/core/startos/src/service/properties.rs +++ b/core/startos/src/service/properties.rs @@ -3,6 +3,7 @@ use std::time::Duration; use models::ProcedureName; use crate::prelude::*; +use crate::rpc_continuations::Guid; use crate::service::Service; impl Service { @@ -11,6 +12,7 @@ impl Service { let container = &self.seed.persistent_container; container .execute::( + Guid::new(), ProcedureName::Properties, Value::Null, Some(Duration::from_secs(30)), diff --git a/core/startos/src/service/rpc.rs b/core/startos/src/service/rpc.rs index 65c8b98fe..eff44b2cf 100644 --- a/core/startos/src/service/rpc.rs +++ b/core/startos/src/service/rpc.rs @@ -7,6 +7,7 @@ use rpc_toolkit::Empty; use ts_rs::TS; use crate::prelude::*; +use crate::rpc_continuations::Guid; #[derive(Clone)] pub struct Init; @@ -46,14 +47,21 @@ impl serde::Serialize for Exit { #[derive(Clone, serde::Deserialize, serde::Serialize, TS)] pub struct ExecuteParams { + id: Guid, procedure: String, #[ts(type = "any")] input: Value, timeout: Option, } impl ExecuteParams { - pub fn new(procedure: ProcedureName, input: Value, timeout: Option) -> Self { + pub fn new( + id: Guid, + procedure: ProcedureName, + input: Value, + timeout: Option, + ) -> Self { Self { + id, procedure: procedure.js_function_name(), input, timeout: timeout.map(|d| d.as_millis()), diff --git a/core/startos/src/service/service_effect_handler.rs b/core/startos/src/service/service_effect_handler.rs index aedf4c194..28a611854 100644 --- a/core/startos/src/service/service_effect_handler.rs +++ b/core/startos/src/service/service_effect_handler.rs @@ -7,9 +7,9 @@ use std::str::FromStr; use std::sync::{Arc, Weak}; use clap::builder::ValueParserFactory; -use clap::Parser; +use clap::{CommandFactory, FromArgMatches, Parser}; use emver::VersionRange; -use imbl_value::{json, InternedString}; +use imbl_value::json; use itertools::Itertools; use models::{ ActionId, DataUrl, HealthCheckId, HostId, ImageId, PackageId, ServiceInterfaceId, VolumeId, @@ -25,35 +25,34 @@ use crate::db::model::package::{ ActionMetadata, CurrentDependencies, CurrentDependencyInfo, CurrentDependencyKind, ManifestPreference, }; -use crate::disk::mount::filesystem::idmapped::IdMapped; -use crate::disk::mount::filesystem::loop_dev::LoopDev; use crate::disk::mount::filesystem::overlayfs::OverlayGuard; +use crate::echo; use crate::net::host::address::HostAddress; use crate::net::host::binding::{BindOptions, LanInfo}; use crate::net::host::{Host, HostKind}; use crate::net::service_interface::{AddressInfo, ServiceInterface, ServiceInterfaceType}; use crate::prelude::*; +use crate::rpc_continuations::Guid; use crate::s9pk::merkle_archive::source::http::HttpSource; use crate::s9pk::rpc::SKIP_ENV; use crate::s9pk::S9pk; use crate::service::cli::ContainerCliContext; -use crate::service::ServiceActorSeed; +use crate::service::Service; use crate::status::health_check::HealthCheckResult; use crate::status::MainStatus; use crate::util::clap::FromStrParser; -use crate::util::{new_guid, Invoke}; -use crate::{echo, ARCH}; +use crate::util::Invoke; #[derive(Clone)] -pub(super) struct EffectContext(Weak); +pub(super) struct EffectContext(Weak); impl EffectContext { - pub fn new(seed: Weak) -> Self { - Self(seed) + pub fn new(service: Weak) -> Self { + Self(service) } } impl Context for EffectContext {} impl EffectContext { - fn deref(&self) -> Result, Error> { + fn deref(&self) -> Result, Error> { if let Some(seed) = Weak::upgrade(&self.0) { Ok(seed) } else { @@ -66,11 +65,55 @@ impl EffectContext { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -struct RpcData { - id: i64, - method: String, - params: Value, +#[serde(rename_all = "camelCase")] +pub struct WithProcedureId { + #[serde(default)] + procedure_id: Guid, + #[serde(flatten)] + rest: T, } +impl FromArgMatches for WithProcedureId { + fn from_arg_matches(matches: &clap::ArgMatches) -> Result { + let rest = T::from_arg_matches(matches)?; + Ok(Self { + procedure_id: matches.get_one("procedure-id").cloned().unwrap_or_default(), + rest, + }) + } + fn from_arg_matches_mut(matches: &mut clap::ArgMatches) -> Result { + let rest = T::from_arg_matches_mut(matches)?; + Ok(Self { + procedure_id: matches.get_one("procedure-id").cloned().unwrap_or_default(), + rest, + }) + } + fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> Result<(), clap::Error> { + self.rest.update_from_arg_matches(matches)?; + self.procedure_id = matches.get_one("procedure-id").cloned().unwrap_or_default(); + Ok(()) + } + fn update_from_arg_matches_mut( + &mut self, + matches: &mut clap::ArgMatches, + ) -> Result<(), clap::Error> { + self.rest.update_from_arg_matches_mut(matches)?; + self.procedure_id = matches.get_one("procedure-id").cloned().unwrap_or_default(); + Ok(()) + } +} +impl CommandFactory for WithProcedureId { + fn command() -> clap::Command { + T::command_for_update().arg( + clap::Arg::new("procedure-id") + .action(clap::ArgAction::Set) + .value_parser(clap::value_parser!(Guid)), + ) + } + fn command_for_update() -> clap::Command { + Self::command() + } +} + pub fn service_effect_handler() -> ParentHandler { ParentHandler::new() .subcommand("gitInfo", from_fn(|_: C| crate::version::git_info())) @@ -290,6 +333,7 @@ struct MountParams { async fn set_system_smtp(context: EffectContext, data: SetSystemSmtpParams) -> Result<(), Error> { let context = context.deref()?; context + .seed .ctx .db .mutate(|db| { @@ -304,6 +348,7 @@ async fn get_system_smtp( ) -> Result { let context = context.deref()?; let res = context + .seed .ctx .db .peek() @@ -323,7 +368,7 @@ async fn get_system_smtp( } async fn get_container_ip(context: EffectContext, _: Empty) -> Result { let context = context.deref()?; - let net_service = context.persistent_container.net_service.lock().await; + let net_service = context.seed.persistent_container.net_service.lock().await; Ok(net_service.get_ip()) } async fn get_service_port_forward( @@ -333,14 +378,15 @@ async fn get_service_port_forward( let internal_port = data.internal_port as u16; let context = context.deref()?; - let net_service = context.persistent_container.net_service.lock().await; + let net_service = context.seed.persistent_container.net_service.lock().await; net_service.get_ext_port(data.host_id, internal_port) } async fn clear_network_interfaces(context: EffectContext, _: Empty) -> Result<(), Error> { let context = context.deref()?; - let package_id = context.id.clone(); + let package_id = context.seed.id.clone(); context + .seed .ctx .db .mutate(|db| { @@ -369,7 +415,7 @@ async fn export_service_interface( }: ExportServiceInterfaceParams, ) -> Result<(), Error> { let context = context.deref()?; - let package_id = context.id.clone(); + let package_id = context.seed.id.clone(); let service_interface = ServiceInterface { id: id.clone(), @@ -384,6 +430,7 @@ async fn export_service_interface( let svc_interface_with_host_info = service_interface; context + .seed .ctx .db .mutate(|db| { @@ -407,7 +454,7 @@ async fn get_primary_url( }: GetPrimaryUrlParams, ) -> Result, Error> { let context = context.deref()?; - let package_id = package_id.unwrap_or_else(|| context.id.clone()); + let package_id = package_id.unwrap_or_else(|| context.seed.id.clone()); Ok(None) // TODO } @@ -419,9 +466,10 @@ async fn list_service_interfaces( }: ListServiceInterfacesParams, ) -> Result, Error> { let context = context.deref()?; - let package_id = package_id.unwrap_or_else(|| context.id.clone()); + let package_id = package_id.unwrap_or_else(|| context.seed.id.clone()); context + .seed .ctx .db .peek() @@ -435,9 +483,10 @@ async fn list_service_interfaces( } async fn remove_address(context: EffectContext, data: RemoveAddressParams) -> Result<(), Error> { let context = context.deref()?; - let package_id = context.id.clone(); + let package_id = context.seed.id.clone(); context + .seed .ctx .db .mutate(|db| { @@ -454,8 +503,9 @@ async fn remove_address(context: EffectContext, data: RemoveAddressParams) -> Re } async fn export_action(context: EffectContext, data: ExportActionParams) -> Result<(), Error> { let context = context.deref()?; - let package_id = context.id.clone(); + let package_id = context.seed.id.clone(); context + .seed .ctx .db .mutate(|db| { @@ -477,8 +527,9 @@ async fn export_action(context: EffectContext, data: ExportActionParams) -> Resu } async fn remove_action(context: EffectContext, data: RemoveActionParams) -> Result<(), Error> { let context = context.deref()?; - let package_id = context.id.clone(); + let package_id = context.seed.id.clone(); context + .seed .ctx .db .mutate(|db| { @@ -514,16 +565,16 @@ struct GetHostInfoParams { callback: Callback, } async fn get_host_info( - ctx: EffectContext, + context: EffectContext, GetHostInfoParams { callback, package_id, host_id, }: GetHostInfoParams, ) -> Result { - let ctx = ctx.deref()?; - let db = ctx.ctx.db.peek().await; - let package_id = package_id.unwrap_or_else(|| ctx.id.clone()); + let context = context.deref()?; + let db = context.seed.ctx.db.peek().await; + let package_id = package_id.unwrap_or_else(|| context.seed.id.clone()); db.as_public() .as_package_data() @@ -536,8 +587,8 @@ async fn get_host_info( } async fn clear_bindings(context: EffectContext, _: Empty) -> Result<(), Error> { - let ctx = context.deref()?; - let mut svc = ctx.persistent_container.net_service.lock().await; + let context = context.deref()?; + let mut svc = context.seed.persistent_container.net_service.lock().await; svc.clear_bindings().await?; Ok(()) } @@ -559,8 +610,8 @@ async fn bind(context: EffectContext, bind_params: Value) -> Result<(), Error> { internal_port, options, } = from_value(bind_params)?; - let ctx = context.deref()?; - let mut svc = ctx.persistent_container.net_service.lock().await; + let context = context.deref()?; + let mut svc = context.seed.persistent_container.net_service.lock().await; svc.bind(kind, id, internal_port, options).await } @@ -575,16 +626,16 @@ struct GetServiceInterfaceParams { } async fn get_service_interface( - ctx: EffectContext, + context: EffectContext, GetServiceInterfaceParams { callback, package_id, service_interface_id, }: GetServiceInterfaceParams, ) -> Result { - let ctx = ctx.deref()?; - let package_id = package_id.unwrap_or_else(|| ctx.id.clone()); - let db = ctx.ctx.db.peek().await; + let context = context.deref()?; + let package_id = package_id.unwrap_or_else(|| context.seed.id.clone()); + let db = context.seed.ctx.db.peek().await; let interface = db .as_public() @@ -729,8 +780,8 @@ async fn get_store( GetStoreParams { package_id, path }: GetStoreParams, ) -> Result { let context = context.deref()?; - let peeked = context.ctx.db.peek().await; - let package_id = package_id.unwrap_or(context.id.clone()); + let peeked = context.seed.ctx.db.peek().await; + let package_id = package_id.unwrap_or(context.seed.id.clone()); let value = peeked .as_private() .as_package_stores() @@ -758,8 +809,9 @@ async fn set_store( SetStoreParams { value, path }: SetStoreParams, ) -> Result<(), Error> { let context = context.deref()?; - let package_id = context.id.clone(); + let package_id = context.seed.id.clone(); context + .seed .ctx .db .mutate(|db| { @@ -812,7 +864,7 @@ struct ParamsMaybePackageId { async fn exists(context: EffectContext, params: ParamsPackageId) -> Result { let context = context.deref()?; - let peeked = context.ctx.db.peek().await; + let peeked = context.seed.ctx.db.peek().await; let package = peeked .as_public() .as_package_data() @@ -834,31 +886,30 @@ struct ExecuteAction { } async fn execute_action( context: EffectContext, - ExecuteAction { - action_id, - input, - service_id, - }: ExecuteAction, + WithProcedureId { + procedure_id, + rest: + ExecuteAction { + service_id, + action_id, + input, + }, + }: WithProcedureId, ) -> Result { let context = context.deref()?; - let package_id = service_id.clone().unwrap_or_else(|| context.id.clone()); - let service = context.ctx.services.get(&package_id).await; - let service = service.as_ref().ok_or_else(|| { - Error::new( - eyre!("Could not find package {package_id}"), - ErrorKind::Unknown, - ) - })?; + let package_id = service_id + .clone() + .unwrap_or_else(|| context.seed.id.clone()); - Ok(json!(service.action(action_id, input).await?)) + Ok(json!(context.action(procedure_id, action_id, input).await?)) } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct FromService {} async fn get_configured(context: EffectContext, _: Empty) -> Result { let context = context.deref()?; - let peeked = context.ctx.db.peek().await; - let package_id = &context.id; + let peeked = context.seed.ctx.db.peek().await; + let package_id = &context.seed.id; let package = peeked .as_public() .as_package_data() @@ -872,8 +923,8 @@ async fn get_configured(context: EffectContext, _: Empty) -> Result Result { let context = context.deref()?; - let peeked = context.ctx.db.peek().await; - let package_id = params.package_id.unwrap_or_else(|| context.id.clone()); + let peeked = context.seed.ctx.db.peek().await; + let package_id = params.package_id.unwrap_or_else(|| context.seed.id.clone()); let package = peeked .as_public() .as_package_data() @@ -887,7 +938,7 @@ async fn stopped(context: EffectContext, params: ParamsMaybePackageId) -> Result async fn running(context: EffectContext, params: ParamsPackageId) -> Result { dbg!("Starting the running {params:?}"); let context = context.deref()?; - let peeked = context.ctx.db.peek().await; + let peeked = context.seed.ctx.db.peek().await; let package_id = params.package_id; let package = peeked .as_public() @@ -900,30 +951,24 @@ async fn running(context: EffectContext, params: ParamsPackageId) -> Result Result { +async fn restart( + context: EffectContext, + WithProcedureId { procedure_id, .. }: WithProcedureId, +) -> Result<(), Error> { let context = context.deref()?; - let service = context.ctx.services.get(&context.id).await; - let service = service.as_ref().ok_or_else(|| { - Error::new( - eyre!("Could not find package {}", context.id), - ErrorKind::Unknown, - ) - })?; - service.restart().await?; - Ok(json!(())) + dbg!("here"); + context.restart(procedure_id).await?; + dbg!("here"); + Ok(()) } -async fn shutdown(context: EffectContext, _: Empty) -> Result { +async fn shutdown( + context: EffectContext, + WithProcedureId { procedure_id, .. }: WithProcedureId, +) -> Result<(), Error> { let context = context.deref()?; - let service = context.ctx.services.get(&context.id).await; - let service = service.as_ref().ok_or_else(|| { - Error::new( - eyre!("Could not find package {}", context.id), - ErrorKind::Unknown, - ) - })?; - service.stop().await?; - Ok(json!(())) + context.stop(procedure_id).await?; + Ok(()) } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Parser, TS)] @@ -935,8 +980,9 @@ struct SetConfigured { } async fn set_configured(context: EffectContext, params: SetConfigured) -> Result { let context = context.deref()?; - let package_id = &context.id; + let package_id = &context.seed.id; context + .seed .ctx .db .mutate(|db| { @@ -989,9 +1035,9 @@ async fn set_main_status(context: EffectContext, params: SetMainStatus) -> Resul dbg!(format!("Status for main will be is {params:?}")); let context = context.deref()?; match params.status { - SetMainStatusStatus::Running => context.started(), - SetMainStatusStatus::Stopped => context.stopped(), - SetMainStatusStatus::Starting => context.stopped(), + SetMainStatusStatus::Running => context.seed.started(), + SetMainStatusStatus::Stopped => context.seed.stopped(), + SetMainStatusStatus::Starting => context.seed.stopped(), } Ok(Value::Null) } @@ -1011,8 +1057,9 @@ async fn set_health( ) -> Result { let context = context.deref()?; - let package_id = &context.id; + let package_id = &context.seed.id; context + .seed .ctx .db .mutate(move |db| { @@ -1041,17 +1088,17 @@ async fn set_health( #[command(rename_all = "camelCase")] #[ts(export)] pub struct DestroyOverlayedImageParams { - #[ts(type = "string")] - guid: InternedString, + guid: Guid, } #[instrument(skip_all)] pub async fn destroy_overlayed_image( - ctx: EffectContext, + context: EffectContext, DestroyOverlayedImageParams { guid }: DestroyOverlayedImageParams, ) -> Result<(), Error> { - let ctx = ctx.deref()?; - if ctx + let context = context.deref()?; + if context + .seed .persistent_container .overlays .lock() @@ -1068,30 +1115,25 @@ pub async fn destroy_overlayed_image( #[command(rename_all = "camelCase")] #[ts(export)] pub struct CreateOverlayedImageParams { - #[ts(type = "string")] image_id: ImageId, } #[instrument(skip_all)] pub async fn create_overlayed_image( - ctx: EffectContext, + context: EffectContext, CreateOverlayedImageParams { image_id }: CreateOverlayedImageParams, -) -> Result<(PathBuf, InternedString), Error> { - let ctx = ctx.deref()?; - let path = Path::new("images") - .join(*ARCH) - .join(&image_id) - .with_extension("squashfs"); - if let Some(image) = ctx +) -> Result<(PathBuf, Guid), Error> { + let context = context.deref()?; + if let Some(image) = context + .seed .persistent_container - .s9pk - .as_archive() - .contents() - .get_path(&path) - .and_then(|e| e.as_file()) + .images + .get(&image_id) + .cloned() { - let guid = new_guid(); - let rootfs_dir = ctx + let guid = Guid::new(); + let rootfs_dir = context + .seed .persistent_container .lxc_container .get() @@ -1102,7 +1144,9 @@ pub async fn create_overlayed_image( ) })? .rootfs_dir(); - let mountpoint = rootfs_dir.join("media/startos/overlays").join(&*guid); + let mountpoint = rootfs_dir + .join("media/startos/overlays") + .join(guid.as_ref()); tokio::fs::create_dir_all(&mountpoint).await?; let container_mountpoint = Path::new("/").join( mountpoint @@ -1110,18 +1154,16 @@ pub async fn create_overlayed_image( .with_kind(ErrorKind::Incoherent)?, ); tracing::info!("Mounting overlay {guid} for {image_id}"); - let guard = OverlayGuard::mount( - &IdMapped::new(LoopDev::from(&**image), 0, 100000, 65536), - &mountpoint, - ) - .await?; + let guard = OverlayGuard::mount(image, &mountpoint).await?; Command::new("chown") .arg("100000:100000") .arg(&mountpoint) .invoke(ErrorKind::Filesystem) .await?; tracing::info!("Mounted overlay {guid} for {image_id}"); - ctx.persistent_container + context + .seed + .persistent_container .overlays .lock() .await @@ -1228,13 +1270,15 @@ struct SetDependenciesParams { } async fn set_dependencies( - ctx: EffectContext, - SetDependenciesParams { dependencies }: SetDependenciesParams, + context: EffectContext, + WithProcedureId { + procedure_id, + rest: SetDependenciesParams { dependencies }, + }: WithProcedureId, ) -> Result<(), Error> { - let ctx = ctx.deref()?; - let id = &ctx.id; - let service_guard = ctx.ctx.services.get(id).await; - let service = service_guard.as_ref().or_not_found(id)?; + let context = context.deref()?; + let id = &context.seed.id; + let mut deps = BTreeMap::new(); for dependency in dependencies { let (dep_id, kind, registry_url, version_spec) = match dependency { @@ -1264,14 +1308,13 @@ async fn set_dependencies( let remote_s9pk = S9pk::deserialize( &Arc::new( HttpSource::new( - ctx.ctx.client.clone(), + context.seed.ctx.client.clone(), registry_url .join(&format!("package/v2/{}.s9pk?spec={}", dep_id, version_spec))?, ) .await?, ), None, // TODO - true, ) .await?; @@ -1291,14 +1334,19 @@ async fn set_dependencies( ) } }; - let config_satisfied = if let Some(dep_service) = &*ctx.ctx.services.get(&dep_id).await { - service - .dependency_config(dep_id.clone(), dep_service.get_config().await?.config) - .await? - .is_none() - } else { - true - }; + let config_satisfied = + if let Some(dep_service) = &*context.seed.ctx.services.get(&dep_id).await { + context + .dependency_config( + procedure_id.clone(), + dep_id.clone(), + dep_service.get_config(procedure_id.clone()).await?.config, + ) + .await? + .is_none() + } else { + true + }; deps.insert( dep_id, CurrentDependencyInfo { @@ -1311,7 +1359,9 @@ async fn set_dependencies( }, ); } - ctx.ctx + context + .seed + .ctx .db .mutate(|db| { db.as_public_mut() @@ -1324,10 +1374,10 @@ async fn set_dependencies( .await } -async fn get_dependencies(ctx: EffectContext) -> Result, Error> { - let ctx = ctx.deref()?; - let id = &ctx.id; - let db = ctx.ctx.db.peek().await; +async fn get_dependencies(context: EffectContext) -> Result, Error> { + let context = context.deref()?; + let id = &context.seed.id; + let db = context.seed.ctx.db.peek().await; let data = db .as_public() .as_package_data() @@ -1384,16 +1434,16 @@ struct CheckDependenciesResult { } async fn check_dependencies( - ctx: EffectContext, + context: EffectContext, CheckDependenciesParam { package_ids }: CheckDependenciesParam, ) -> Result, Error> { - let ctx = ctx.deref()?; - let db = ctx.ctx.db.peek().await; + let context = context.deref()?; + let db = context.seed.ctx.db.peek().await; let current_dependencies = db .as_public() .as_package_data() - .as_idx(&ctx.id) - .or_not_found(&ctx.id)? + .as_idx(&context.seed.id) + .or_not_found(&context.seed.id)? .as_current_dependencies() .de()?; let package_ids: Vec<_> = package_ids diff --git a/core/startos/src/service/service_map.rs b/core/startos/src/service/service_map.rs index ba9188f32..1474ea35e 100644 --- a/core/startos/src/service/service_map.rs +++ b/core/startos/src/service/service_map.rs @@ -25,7 +25,7 @@ use crate::progress::{ use crate::s9pk::manifest::PackageId; use crate::s9pk::merkle_archive::source::FileSource; use crate::s9pk::S9pk; -use crate::service::{LoadDisposition, Service}; +use crate::service::{LoadDisposition, Service, ServiceRef}; use crate::status::{MainStatus, Status}; use crate::util::serde::Pem; @@ -39,23 +39,22 @@ pub struct InstallProgressHandles { /// This is the structure to contain all the services #[derive(Default)] -pub struct ServiceMap(Mutex>>>>); +pub struct ServiceMap(Mutex>>>>); impl ServiceMap { - async fn entry(&self, id: &PackageId) -> Arc>> { + async fn entry(&self, id: &PackageId) -> Arc>> { let mut lock = self.0.lock().await; - dbg!(lock.keys().collect::>()); lock.entry(id.clone()) .or_insert_with(|| Arc::new(RwLock::new(None))) .clone() } #[instrument(skip_all)] - pub async fn get(&self, id: &PackageId) -> OwnedRwLockReadGuard> { + pub async fn get(&self, id: &PackageId) -> OwnedRwLockReadGuard> { self.entry(id).await.read_owned().await } #[instrument(skip_all)] - pub async fn get_mut(&self, id: &PackageId) -> OwnedRwLockWriteGuard> { + pub async fn get_mut(&self, id: &PackageId) -> OwnedRwLockWriteGuard> { self.entry(id).await.write_owned().await } @@ -83,7 +82,7 @@ impl ServiceMap { shutdown_err = service.shutdown().await; } // TODO: retry on error? - *service = Service::load(ctx, id, disposition).await?; + *service = Service::load(ctx, id, disposition).await?.map(From::from); shutdown_err?; Ok(()) } @@ -95,6 +94,7 @@ impl ServiceMap { mut s9pk: S9pk, recovery_source: Option, ) -> Result { + s9pk.validate_and_filter(ctx.s9pk_arch)?; let manifest = s9pk.as_manifest().clone(); let id = manifest.id.clone(); let icon = s9pk.icon_data_url().await?; @@ -128,7 +128,7 @@ impl ServiceMap { ); let restoring = recovery_source.is_some(); - let mut reload_guard = ServiceReloadGuard::new(ctx.clone(), id.clone(), op_name); + let mut reload_guard = ServiceRefReloadGuard::new(ctx.clone(), id.clone(), op_name); reload_guard .handle(ctx.db.mutate({ @@ -231,7 +231,7 @@ impl ServiceMap { Ok(reload_guard .handle_last(async move { finalization_progress.start(); - let s9pk = S9pk::open(&installed_path, Some(&id), true).await?; + let s9pk = S9pk::open(&installed_path, Some(&id)).await?; let prev = if let Some(service) = service.take() { ensure_code!( recovery_source.is_none(), @@ -264,7 +264,8 @@ impl ServiceMap { progress_handle, }), ) - .await?, + .await? + .into(), ); } else { *service = Some( @@ -277,7 +278,8 @@ impl ServiceMap { progress_handle, }), ) - .await?, + .await? + .into(), ); } sync_progress_task.await.map_err(|_| { @@ -295,7 +297,7 @@ impl ServiceMap { pub async fn uninstall(&self, ctx: &RpcContext, id: &PackageId) -> Result<(), Error> { let mut guard = self.get_mut(id).await; if let Some(service) = guard.take() { - ServiceReloadGuard::new(ctx.clone(), id.clone(), "Uninstall") + ServiceRefReloadGuard::new(ctx.clone(), id.clone(), "Uninstall") .handle_last(async move { let res = service.uninstall(None).await; drop(guard); @@ -326,17 +328,17 @@ impl ServiceMap { } } -pub struct ServiceReloadGuard(Option); -impl Drop for ServiceReloadGuard { +pub struct ServiceRefReloadGuard(Option); +impl Drop for ServiceRefReloadGuard { fn drop(&mut self) { if let Some(info) = self.0.take() { tokio::spawn(info.reload(None)); } } } -impl ServiceReloadGuard { +impl ServiceRefReloadGuard { pub fn new(ctx: RpcContext, id: PackageId, operation: &'static str) -> Self { - Self(Some(ServiceReloadInfo { ctx, id, operation })) + Self(Some(ServiceRefReloadInfo { ctx, id, operation })) } pub async fn handle( @@ -365,12 +367,12 @@ impl ServiceReloadGuard { } } -struct ServiceReloadInfo { +struct ServiceRefReloadInfo { ctx: RpcContext, id: PackageId, operation: &'static str, } -impl ServiceReloadInfo { +impl ServiceRefReloadInfo { async fn reload(self, error: Option) -> Result<(), Error> { self.ctx .services diff --git a/core/startos/src/service/transition/backup.rs b/core/startos/src/service/transition/backup.rs index e6a5cb817..f7591f0d9 100644 --- a/core/startos/src/service/transition/backup.rs +++ b/core/startos/src/service/transition/backup.rs @@ -6,6 +6,7 @@ use models::ProcedureName; use super::TempDesiredRestore; use crate::disk::mount::filesystem::ReadWrite; use crate::prelude::*; +use crate::rpc_continuations::Guid; use crate::service::config::GetConfig; use crate::service::dependencies::DependencyConfig; use crate::service::transition::{TransitionKind, TransitionState}; @@ -24,7 +25,12 @@ impl Handler for ServiceActor { .except::() .except::() } - async fn handle(&mut self, backup: Backup, jobs: &BackgroundJobQueue) -> Self::Response { + async fn handle( + &mut self, + id: Guid, + backup: Backup, + jobs: &BackgroundJobQueue, + ) -> Self::Response { // So Need a handle to just a single field in the state let temp: TempDesiredRestore = TempDesiredRestore::new(&self.0.persistent_container.state); let mut current = self.0.persistent_container.state.subscribe(); @@ -45,7 +51,7 @@ impl Handler for ServiceActor { .mount_backup(path, ReadWrite) .await?; seed.persistent_container - .execute(ProcedureName::CreateBackup, Value::Null, None) + .execute(id, ProcedureName::CreateBackup, Value::Null, None) .await?; backup_guard.unmount(true).await?; diff --git a/core/startos/src/service/transition/restart.rs b/core/startos/src/service/transition/restart.rs index 07466def1..a39291621 100644 --- a/core/startos/src/service/transition/restart.rs +++ b/core/startos/src/service/transition/restart.rs @@ -2,6 +2,7 @@ use futures::FutureExt; use super::TempDesiredRestore; use crate::prelude::*; +use crate::rpc_continuations::Guid; use crate::service::config::GetConfig; use crate::service::dependencies::DependencyConfig; use crate::service::transition::{TransitionKind, TransitionState}; @@ -18,7 +19,8 @@ impl Handler for ServiceActor { .except::() .except::() } - async fn handle(&mut self, _: Restart, jobs: &BackgroundJobQueue) -> Self::Response { + async fn handle(&mut self, _: Guid, _: Restart, jobs: &BackgroundJobQueue) -> Self::Response { + dbg!("here"); // So Need a handle to just a single field in the state let temp = TempDesiredRestore::new(&self.0.persistent_container.state); let mut current = self.0.persistent_container.state.subscribe(); @@ -74,7 +76,8 @@ impl Handler for ServiceActor { } impl Service { #[instrument(skip_all)] - pub async fn restart(&self) -> Result<(), Error> { - self.actor.send(Restart).await + pub async fn restart(&self, id: Guid) -> Result<(), Error> { + dbg!("here"); + self.actor.send(id, Restart).await } } diff --git a/core/startos/src/service/transition/restore.rs b/core/startos/src/service/transition/restore.rs index b32ade4f5..1c4020ea4 100644 --- a/core/startos/src/service/transition/restore.rs +++ b/core/startos/src/service/transition/restore.rs @@ -5,6 +5,7 @@ use models::ProcedureName; use crate::disk::mount::filesystem::ReadOnly; use crate::prelude::*; +use crate::rpc_continuations::Guid; use crate::service::transition::{TransitionKind, TransitionState}; use crate::service::ServiceActor; use crate::util::actor::background::BackgroundJobQueue; @@ -19,7 +20,12 @@ impl Handler for ServiceActor { fn conflicts_with(_: &Restore) -> ConflictBuilder { ConflictBuilder::everything() } - async fn handle(&mut self, restore: Restore, jobs: &BackgroundJobQueue) -> Self::Response { + async fn handle( + &mut self, + id: Guid, + restore: Restore, + jobs: &BackgroundJobQueue, + ) -> Self::Response { // So Need a handle to just a single field in the state let path = restore.path.clone(); let seed = self.0.clone(); @@ -32,7 +38,7 @@ impl Handler for ServiceActor { .mount_backup(path, ReadOnly) .await?; seed.persistent_container - .execute(ProcedureName::RestoreBackup, Value::Null, None) + .execute(id, ProcedureName::RestoreBackup, Value::Null, None) .await?; backup_guard.unmount(true).await?; diff --git a/core/startos/src/util/actor/concurrent.rs b/core/startos/src/util/actor/concurrent.rs index e32470d80..7b26fc4c7 100644 --- a/core/startos/src/util/actor/concurrent.rs +++ b/core/startos/src/util/actor/concurrent.rs @@ -8,6 +8,7 @@ use helpers::NonDetachingJoinHandle; use tokio::sync::{mpsc, oneshot}; use crate::prelude::*; +use crate::rpc_continuations::Guid; use crate::util::actor::background::{BackgroundJobQueue, BackgroundJobRunner}; use crate::util::actor::{Actor, ConflictFn, Handler, PendingMessageStrategy, Request}; @@ -18,6 +19,7 @@ struct ConcurrentRunner { waiting: Vec>, recv: mpsc::UnboundedReceiver>, handlers: Vec<( + Guid, Arc>, oneshot::Sender>, BoxFuture<'static, Box>, @@ -41,16 +43,21 @@ impl Future for ConcurrentRunner { } }); if this.shutdown.is_some() { - while let std::task::Poll::Ready(Some((msg, reply))) = this.recv.poll_recv(cx) { - if this.handlers.iter().any(|(f, _, _)| f(&*msg)) { - this.waiting.push((msg, reply)); + while let std::task::Poll::Ready(Some((id, msg, reply))) = this.recv.poll_recv(cx) { + if this + .handlers + .iter() + .any(|(hid, f, _, _)| &id != hid && f(&*msg)) + { + this.waiting.push((id, msg, reply)); } else { let mut actor = this.actor.clone(); let queue = this.queue.clone(); this.handlers.push(( + id.clone(), msg.conflicts_with(), reply, - async move { msg.handle_with(&mut actor, &queue).await }.boxed(), + async move { msg.handle_with(id, &mut actor, &queue).await }.boxed(), )) } } @@ -62,29 +69,34 @@ impl Future for ConcurrentRunner { .handlers .iter_mut() .enumerate() - .filter_map(|(i, (_, _, f))| match f.poll_unpin(cx) { + .filter_map(|(i, (_, _, _, f))| match f.poll_unpin(cx) { std::task::Poll::Pending => None, std::task::Poll::Ready(res) => Some((i, res)), }) .collect::>(); for (idx, res) in complete.into_iter().rev() { #[allow(clippy::let_underscore_future)] - let (f, reply, _) = this.handlers.swap_remove(idx); + let (_, f, reply, _) = this.handlers.swap_remove(idx); let _ = reply.send(res); // TODO: replace with Vec::extract_if once stable if this.shutdown.is_some() { let mut i = 0; while i < this.waiting.len() { - if f(&*this.waiting[i].0) - && !this.handlers.iter().any(|(f, _, _)| f(&*this.waiting[i].0)) + if f(&*this.waiting[i].1) + && !this + .handlers + .iter() + .any(|(_, f, _, _)| f(&*this.waiting[i].1)) { - let (msg, reply) = this.waiting.remove(i); + let (id, msg, reply) = this.waiting.remove(i); let mut actor = this.actor.clone(); let queue = this.queue.clone(); this.handlers.push(( + id.clone(), msg.conflicts_with(), reply, - async move { msg.handle_with(&mut actor, &queue).await }.boxed(), + async move { msg.handle_with(id, &mut actor, &queue).await } + .boxed(), )); cont = true; } else { @@ -137,6 +149,7 @@ impl ConcurrentActor { /// Message is guaranteed to be queued immediately pub fn queue( &self, + id: Guid, message: M, ) -> impl Future> where @@ -150,7 +163,7 @@ impl ConcurrentActor { } let (reply_send, reply_recv) = oneshot::channel(); self.messenger - .send((Box::new(message), reply_send)) + .send((id, Box::new(message), reply_send)) .unwrap(); futures::future::Either::Right( reply_recv @@ -170,11 +183,11 @@ impl ConcurrentActor { ) } - pub async fn send(&self, message: M) -> Result + pub async fn send(&self, id: Guid, message: M) -> Result where A: Handler, { - self.queue(message).await + self.queue(id, message).await } pub async fn shutdown(self, strategy: PendingMessageStrategy) { diff --git a/core/startos/src/util/actor/mod.rs b/core/startos/src/util/actor/mod.rs index 5cef7c22e..d85e53757 100644 --- a/core/startos/src/util/actor/mod.rs +++ b/core/startos/src/util/actor/mod.rs @@ -9,6 +9,7 @@ use tokio::sync::oneshot; #[allow(unused_imports)] use crate::prelude::*; +use crate::rpc_continuations::Guid; use crate::util::actor::background::BackgroundJobQueue; pub mod background; @@ -28,6 +29,7 @@ pub trait Handler: Actor { } fn handle( &mut self, + id: Guid, msg: M, jobs: &BackgroundJobQueue, ) -> impl Future + Send; @@ -39,6 +41,7 @@ trait Message: Send + Any { fn conflicts_with(&self) -> Arc>; fn handle_with<'a>( self: Box, + id: Guid, actor: &'a mut A, jobs: &'a BackgroundJobQueue, ) -> BoxFuture<'a, Box>; @@ -52,10 +55,11 @@ where } fn handle_with<'a>( self: Box, + id: Guid, actor: &'a mut A, jobs: &'a BackgroundJobQueue, ) -> BoxFuture<'a, Box> { - async move { Box::new(actor.handle(*self, jobs).await) as Box }.boxed() + async move { Box::new(actor.handle(id, *self, jobs).await) as Box }.boxed() } } impl dyn Message { @@ -80,7 +84,11 @@ impl dyn Message { } } -type Request = (Box>, oneshot::Sender>); +type Request = ( + Guid, + Box>, + oneshot::Sender>, +); pub enum PendingMessageStrategy { CancelAll, diff --git a/core/startos/src/util/actor/simple.rs b/core/startos/src/util/actor/simple.rs index 6f880a57a..7f2ad388e 100644 --- a/core/startos/src/util/actor/simple.rs +++ b/core/startos/src/util/actor/simple.rs @@ -7,6 +7,7 @@ use tokio::sync::oneshot::error::TryRecvError; use tokio::sync::{mpsc, oneshot}; use crate::prelude::*; +use crate::rpc_continuations::Guid; use crate::util::actor::background::BackgroundJobQueue; use crate::util::actor::{Actor, Handler, PendingMessageStrategy, Request}; @@ -26,9 +27,9 @@ impl SimpleActor { tokio::select! { _ = &mut runner => (), msg = messenger_recv.recv() => match msg { - Some((msg, reply)) if shutdown_recv.try_recv() == Err(TryRecvError::Empty) => { + Some((id, msg, reply)) if shutdown_recv.try_recv() == Err(TryRecvError::Empty) => { tokio::select! { - res = msg.handle_with(&mut actor, &queue) => { let _ = reply.send(res); }, + res = msg.handle_with(id, &mut actor, &queue) => { let _ = reply.send(res); }, _ = &mut runner => (), } } @@ -60,7 +61,7 @@ impl SimpleActor { } let (reply_send, reply_recv) = oneshot::channel(); self.messenger - .send((Box::new(message), reply_send)) + .send((Guid::new(), Box::new(message), reply_send)) .unwrap(); futures::future::Either::Right( reply_recv diff --git a/core/startos/src/util/io.rs b/core/startos/src/util/io.rs index 16bafd8f6..f4476ee2b 100644 --- a/core/startos/src/util/io.rs +++ b/core/startos/src/util/io.rs @@ -681,8 +681,6 @@ impl AsyncWrite for TimeoutStream { } } -pub struct TmpFile {} - #[derive(Debug)] pub struct TmpDir { path: PathBuf, @@ -707,6 +705,14 @@ impl TmpDir { tokio::fs::remove_dir_all(&self.path).await?; Ok(()) } + + pub async fn gc(self: Arc) -> Result<(), Error> { + if let Ok(dir) = Arc::try_unwrap(self) { + dir.delete().await + } else { + Ok(()) + } + } } impl std::ops::Deref for TmpDir { type Target = Path; diff --git a/core/startos/src/util/mod.rs b/core/startos/src/util/mod.rs index aa17b6d7a..4346a0b1e 100644 --- a/core/startos/src/util/mod.rs +++ b/core/startos/src/util/mod.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeMap; +use std::collections::{BTreeMap, VecDeque}; use std::future::Future; use std::marker::PhantomData; use std::path::{Path, PathBuf}; @@ -11,6 +11,8 @@ use std::time::Duration; use async_trait::async_trait; use color_eyre::eyre::{self, eyre}; use fd_lock_rs::FdLock; +use futures::future::BoxFuture; +use futures::FutureExt; use helpers::canonicalize; pub use helpers::NonDetachingJoinHandle; use imbl_value::InternedString; @@ -19,7 +21,8 @@ pub use models::VersionString; use pin_project::pin_project; use sha2::Digest; use tokio::fs::File; -use tokio::sync::{Mutex, OwnedMutexGuard, RwLock}; +use tokio::io::{AsyncRead, AsyncReadExt, BufReader}; +use tokio::sync::{oneshot, Mutex, OwnedMutexGuard, RwLock}; use tracing::instrument; use crate::shutdown::Shutdown; @@ -62,11 +65,16 @@ pub trait Invoke<'a> { where Self: 'ext, 'ext: 'a; + fn pipe<'ext: 'a>( + &'ext mut self, + next: &'ext mut tokio::process::Command, + ) -> Self::Extended<'ext>; fn timeout<'ext: 'a>(&'ext mut self, timeout: Option) -> Self::Extended<'ext>; fn input<'ext: 'a, Input: tokio::io::AsyncRead + Unpin + Send>( &'ext mut self, input: Option<&'ext mut Input>, ) -> Self::Extended<'ext>; + fn capture<'ext: 'a>(&'ext mut self, capture: bool) -> Self::Extended<'ext>; fn invoke( &mut self, error_kind: crate::ErrorKind, @@ -76,7 +84,20 @@ pub trait Invoke<'a> { pub struct ExtendedCommand<'a> { cmd: &'a mut tokio::process::Command, timeout: Option, - input: Option<&'a mut (dyn tokio::io::AsyncRead + Unpin + Send)>, + input: Option<&'a mut (dyn AsyncRead + Unpin + Send)>, + pipe: VecDeque<&'a mut tokio::process::Command>, + capture: bool, +} +impl<'a> From<&'a mut tokio::process::Command> for ExtendedCommand<'a> { + fn from(value: &'a mut tokio::process::Command) -> Self { + ExtendedCommand { + cmd: value, + timeout: None, + input: None, + pipe: VecDeque::new(), + capture: true, + } + } } impl<'a> std::ops::Deref for ExtendedCommand<'a> { type Target = tokio::process::Command; @@ -95,35 +116,38 @@ impl<'a> Invoke<'a> for tokio::process::Command { where Self: 'ext, 'ext: 'a; - fn timeout<'ext: 'a>(&'ext mut self, timeout: Option) -> Self::Extended<'ext> { - ExtendedCommand { - cmd: self, - timeout, - input: None, - } + fn pipe<'ext: 'a>( + &'ext mut self, + next: &'ext mut tokio::process::Command, + ) -> Self::Extended<'ext> { + let mut cmd = ExtendedCommand::from(self); + cmd.pipe.push_back(next); + cmd } - fn input<'ext: 'a, Input: tokio::io::AsyncRead + Unpin + Send>( + fn timeout<'ext: 'a>(&'ext mut self, timeout: Option) -> Self::Extended<'ext> { + let mut cmd = ExtendedCommand::from(self); + cmd.timeout = timeout; + cmd + } + fn input<'ext: 'a, Input: AsyncRead + Unpin + Send>( &'ext mut self, input: Option<&'ext mut Input>, ) -> Self::Extended<'ext> { - ExtendedCommand { - cmd: self, - timeout: None, - input: if let Some(input) = input { - Some(&mut *input) - } else { - None - }, - } + let mut cmd = ExtendedCommand::from(self); + cmd.input = if let Some(input) = input { + Some(&mut *input) + } else { + None + }; + cmd + } + fn capture<'ext: 'a>(&'ext mut self, capture: bool) -> Self::Extended<'ext> { + let mut cmd = ExtendedCommand::from(self); + cmd.capture = capture; + cmd } async fn invoke(&mut self, error_kind: crate::ErrorKind) -> Result, Error> { - ExtendedCommand { - cmd: self, - timeout: None, - input: None, - } - .invoke(error_kind) - .await + ExtendedCommand::from(self).invoke(error_kind).await } } @@ -132,6 +156,13 @@ impl<'a> Invoke<'a> for ExtendedCommand<'a> { where Self: 'ext, 'ext: 'a; + fn pipe<'ext: 'a>( + &'ext mut self, + next: &'ext mut tokio::process::Command, + ) -> Self::Extended<'ext> { + self.pipe.push_back(next.kill_on_drop(true)); + self + } fn timeout<'ext: 'a>(&'ext mut self, timeout: Option) -> Self::Extended<'ext> { self.timeout = timeout; self @@ -147,39 +178,150 @@ impl<'a> Invoke<'a> for ExtendedCommand<'a> { }; self } + fn capture<'ext: 'a>(&'ext mut self, capture: bool) -> Self::Extended<'ext> { + self.capture = capture; + self + } + #[instrument(skip_all)] async fn invoke(&mut self, error_kind: crate::ErrorKind) -> Result, Error> { + let cmd_str = self + .cmd + .as_std() + .get_program() + .to_string_lossy() + .into_owned(); self.cmd.kill_on_drop(true); if self.input.is_some() { self.cmd.stdin(Stdio::piped()); } - self.cmd.stdout(Stdio::piped()); - self.cmd.stderr(Stdio::piped()); - let mut child = self.cmd.spawn().with_kind(error_kind)?; - if let (Some(mut stdin), Some(input)) = (child.stdin.take(), self.input.take()) { - use tokio::io::AsyncWriteExt; - tokio::io::copy(input, &mut stdin).await?; - stdin.flush().await?; - stdin.shutdown().await?; - drop(stdin); + if self.pipe.is_empty() { + if self.capture { + self.cmd.stdout(Stdio::piped()); + self.cmd.stderr(Stdio::piped()); + } + let mut child = self.cmd.spawn().with_ctx(|_| (error_kind, &cmd_str))?; + if let (Some(mut stdin), Some(input)) = (child.stdin.take(), self.input.take()) { + use tokio::io::AsyncWriteExt; + tokio::io::copy(input, &mut stdin).await?; + stdin.flush().await?; + stdin.shutdown().await?; + drop(stdin); + } + let res = match self.timeout { + None => child + .wait_with_output() + .await + .with_ctx(|_| (error_kind, &cmd_str))?, + Some(t) => tokio::time::timeout(t, child.wait_with_output()) + .await + .with_kind(ErrorKind::Timeout)? + .with_ctx(|_| (error_kind, &cmd_str))?, + }; + crate::ensure_code!( + res.status.success(), + error_kind, + "{}", + Some(&res.stderr) + .filter(|a| !a.is_empty()) + .or(Some(&res.stdout)) + .filter(|a| !a.is_empty()) + .and_then(|a| std::str::from_utf8(a).ok()) + .unwrap_or(&format!( + "{} exited with code {}", + self.cmd.as_std().get_program().to_string_lossy(), + res.status + )) + ); + Ok(res.stdout) + } else { + let mut futures = Vec::>>::new(); // todo: predict capacity + + let mut cmds = std::mem::take(&mut self.pipe); + cmds.push_front(&mut *self.cmd); + let len = cmds.len(); + + let timeout = self.timeout; + + let mut prev = self + .input + .take() + .map(|i| Box::new(i) as Box); + for (idx, cmd) in IntoIterator::into_iter(cmds).enumerate() { + let last = idx == len - 1; + if self.capture || !last { + cmd.stdout(Stdio::piped()); + } + if self.capture { + cmd.stderr(Stdio::piped()); + } + if prev.is_some() { + cmd.stdin(Stdio::piped()); + } + let mut child = cmd.spawn().with_kind(error_kind)?; + let input = std::mem::replace( + &mut prev, + child + .stdout + .take() + .map(|i| Box::new(BufReader::new(i)) as Box), + ); + futures.push( + async move { + if let (Some(mut stdin), Some(mut input)) = (child.stdin.take(), input) { + use tokio::io::AsyncWriteExt; + tokio::io::copy(&mut input, &mut stdin).await?; + stdin.flush().await?; + stdin.shutdown().await?; + drop(stdin); + } + let res = match timeout { + None => child.wait_with_output().await?, + Some(t) => tokio::time::timeout(t, child.wait_with_output()) + .await + .with_kind(ErrorKind::Timeout)??, + }; + crate::ensure_code!( + res.status.success(), + error_kind, + "{}", + Some(&res.stderr) + .filter(|a| !a.is_empty()) + .or(Some(&res.stdout)) + .filter(|a| !a.is_empty()) + .and_then(|a| std::str::from_utf8(a).ok()) + .unwrap_or(&format!( + "{} exited with code {}", + cmd.as_std().get_program().to_string_lossy(), + res.status + )) + ); + + Ok(()) + } + .boxed(), + ); + } + + let (send, recv) = oneshot::channel(); + futures.push( + async move { + if let Some(mut prev) = prev { + let mut res = Vec::new(); + prev.read_to_end(&mut res).await?; + send.send(res).unwrap(); + } else { + send.send(Vec::new()).unwrap(); + } + + Ok(()) + } + .boxed(), + ); + + futures::future::try_join_all(futures).await?; + + Ok(recv.await.unwrap()) } - let res = match self.timeout { - None => child.wait_with_output().await?, - Some(t) => tokio::time::timeout(t, child.wait_with_output()) - .await - .with_kind(ErrorKind::Timeout)??, - }; - crate::ensure_code!( - res.status.success(), - error_kind, - "{}", - Some(&res.stderr) - .filter(|a| !a.is_empty()) - .or(Some(&res.stdout)) - .filter(|a| !a.is_empty()) - .and_then(|a| std::str::from_utf8(a).ok()) - .unwrap_or(&format!("Unknown Error ({})", res.status)) - ); - Ok(res.stdout) } } diff --git a/sdk/lib/StartSdk.ts b/sdk/lib/StartSdk.ts index f5959a084..4208928c9 100644 --- a/sdk/lib/StartSdk.ts +++ b/sdk/lib/StartSdk.ts @@ -187,7 +187,10 @@ export class StartSdk { nullIfEmpty, runCommand: async ( effects: Effects, - image: { id: Manifest["images"][number]; sharedRun?: boolean }, + image: { + id: keyof Manifest["images"] & T.ImageId + sharedRun?: boolean + }, command: ValidIfNoStupidEscape | [string, ...string[]], options: CommandOptions & { mounts?: { path: string; options: MountOptions }[] @@ -396,7 +399,7 @@ export class StartSdk { setupProperties: ( fn: (options: { effects: Effects }) => Promise, - ): T.ExpectedExports.Properties => + ): T.ExpectedExports.properties => (options) => fn(options).then(nullifyProperties), setupUninstall: (fn: UninstallFn) => @@ -743,7 +746,7 @@ export class StartSdk { export async function runCommand( effects: Effects, - image: { id: Manifest["images"][number]; sharedRun?: boolean }, + image: { id: keyof Manifest["images"] & T.ImageId; sharedRun?: boolean }, command: string | [string, ...string[]], options: CommandOptions & { mounts?: { path: string; options: MountOptions }[] diff --git a/sdk/lib/health/HealthCheck.ts b/sdk/lib/health/HealthCheck.ts index ca0e3ebb9..1ed8652bf 100644 --- a/sdk/lib/health/HealthCheck.ts +++ b/sdk/lib/health/HealthCheck.ts @@ -8,12 +8,13 @@ import { defaultTrigger } from "../trigger/defaultTrigger" import { once } from "../util/once" import { Overlay } from "../util/Overlay" import { object, unknown } from "ts-matches" +import { T } from ".." export type HealthCheckParams = { effects: Effects name: string image: { - id: Manifest["images"][number] + id: keyof Manifest["images"] & T.ImageId sharedRun?: boolean } trigger?: Trigger diff --git a/sdk/lib/interfaces/Host.ts b/sdk/lib/interfaces/Host.ts index 96a76628a..aa27a289c 100644 --- a/sdk/lib/interfaces/Host.ts +++ b/sdk/lib/interfaces/Host.ts @@ -69,12 +69,12 @@ type NotProtocolsWithSslVariants = Exclude< type BindOptionsByKnownProtocol = | { protocol: ProtocolsWithSslVariants - preferredExternalPort: number + preferredExternalPort?: number addSsl?: Partial } | { protocol: NotProtocolsWithSslVariants - preferredExternalPort: number + preferredExternalPort?: number addSsl?: AddSslOptions } export type BindOptionsByProtocol = BindOptionsByKnownProtocol | BindOptions diff --git a/sdk/lib/mainFn/CommandController.ts b/sdk/lib/mainFn/CommandController.ts index a68e1e426..264574f7c 100644 --- a/sdk/lib/mainFn/CommandController.ts +++ b/sdk/lib/mainFn/CommandController.ts @@ -1,6 +1,6 @@ import { NO_TIMEOUT, SIGKILL, SIGTERM } from "../StartSdk" import { SDKManifest } from "../manifest/ManifestTypes" -import { Effects, ValidIfNoStupidEscape } from "../types" +import { Effects, ImageId, ValidIfNoStupidEscape } from "../types" import { MountOptions, Overlay } from "../util/Overlay" import { splitCommand } from "../util/splitCommand" import { cpExecFile, cpExec } from "./Daemons" @@ -15,7 +15,7 @@ export class CommandController { return async ( effects: Effects, imageId: { - id: Manifest["images"][number] + id: keyof Manifest["images"] & ImageId sharedRun?: boolean }, command: ValidIfNoStupidEscape | [string, ...string[]], diff --git a/sdk/lib/mainFn/Daemon.ts b/sdk/lib/mainFn/Daemon.ts index 2ec438801..c48865f94 100644 --- a/sdk/lib/mainFn/Daemon.ts +++ b/sdk/lib/mainFn/Daemon.ts @@ -1,5 +1,5 @@ import { SDKManifest } from "../manifest/ManifestTypes" -import { Effects, ValidIfNoStupidEscape } from "../types" +import { Effects, ImageId, ValidIfNoStupidEscape } from "../types" import { MountOptions, Overlay } from "../util/Overlay" import { CommandController } from "./CommandController" @@ -18,7 +18,7 @@ export class Daemon { return async ( effects: Effects, imageId: { - id: Manifest["images"][number] + id: keyof Manifest["images"] & ImageId sharedRun?: boolean }, command: ValidIfNoStupidEscape | [string, ...string[]], diff --git a/sdk/lib/mainFn/Daemons.ts b/sdk/lib/mainFn/Daemons.ts index 8962061f5..d51d444a8 100644 --- a/sdk/lib/mainFn/Daemons.ts +++ b/sdk/lib/mainFn/Daemons.ts @@ -5,7 +5,12 @@ import { SDKManifest } from "../manifest/ManifestTypes" import { Trigger } from "../trigger" import { TriggerInput } from "../trigger/TriggerInput" import { defaultTrigger } from "../trigger/defaultTrigger" -import { DaemonReturned, Effects, ValidIfNoStupidEscape } from "../types" +import { + DaemonReturned, + Effects, + ImageId, + ValidIfNoStupidEscape, +} from "../types" import { Mounts } from "./Mounts" import { CommandOptions, MountOptions, Overlay } from "../util/Overlay" import { splitCommand } from "../util/splitCommand" @@ -34,8 +39,8 @@ type DaemonsParams< Id extends string, > = { command: ValidIfNoStupidEscape | [string, ...string[]] - image: { id: Manifest["images"][number]; sharedRun?: boolean } - mounts: { path: string; options: MountOptions }[] + image: { id: keyof Manifest["images"] & ImageId; sharedRun?: boolean } + mounts: Mounts env?: Record ready: Ready requires: Exclude[] @@ -116,12 +121,10 @@ export class Daemons { options: DaemonsParams, ) { const daemonIndex = this.daemons.length - const daemon = Daemon.of()( - this.effects, - options.image, - options.command, - options, - ) + const daemon = Daemon.of()(this.effects, options.image, options.command, { + ...options, + mounts: options.mounts.build(), + }) const healthDaemon = new HealthDaemon( daemon, daemonIndex, diff --git a/sdk/lib/manifest/ManifestTypes.ts b/sdk/lib/manifest/ManifestTypes.ts index ed8703bf2..c820930c8 100644 --- a/sdk/lib/manifest/ManifestTypes.ts +++ b/sdk/lib/manifest/ManifestTypes.ts @@ -1,5 +1,5 @@ import { ValidEmVer } from "../emverLite/mod" -import { ActionMetadata } from "../types" +import { ActionMetadata, ImageConfig, ImageId } from "../types" export interface Container { /** This should be pointing to a docker container name */ @@ -28,8 +28,6 @@ export type SDKManifest = { readonly releaseNotes: string /** The type of license for the project. Include the LICENSE in the root of the project directory. A license is required for a Start9 package.*/ readonly license: string // name of license - /** A list of normie (hosted, SaaS, custodial, etc) services this services intends to replace */ - readonly replaces: Readonly /** The Start9 wrapper repository URL for the package. This repo contains the manifest file (this), * any scripts necessary for configuration, backups, actions, or health checks (more below). This key * must exist. But could be embedded into the source repository @@ -52,7 +50,7 @@ export type SDKManifest = { } /** Defines the os images needed to run the container processes */ - readonly images: string[] + readonly images: Record /** This denotes readonly asset directories that should be available to mount to the container. * Assuming that there will be three files with names along the lines: * icon.* : the icon that will be this packages icon on the ui diff --git a/sdk/lib/manifest/setupManifest.ts b/sdk/lib/manifest/setupManifest.ts index 41c74baa0..8bd39a7aa 100644 --- a/sdk/lib/manifest/setupManifest.ts +++ b/sdk/lib/manifest/setupManifest.ts @@ -1,18 +1,19 @@ +import { ImageConfig, ImageId, VolumeId } from "../osBindings" import { SDKManifest, ManifestVersion } from "./ManifestTypes" export function setupManifest< Id extends string, Version extends ManifestVersion, Dependencies extends Record, - VolumesTypes extends string, - AssetTypes extends string, - ImagesTypes extends string, + VolumesTypes extends VolumeId, + AssetTypes extends VolumeId, + ImagesTypes extends ImageId, Manifest extends SDKManifest & { dependencies: Dependencies id: Id version: Version assets: AssetTypes[] - images: ImagesTypes[] + images: Record volumes: VolumesTypes[] }, >(manifest: Manifest): Manifest { diff --git a/sdk/lib/osBindings/CreateOverlayedImageParams.ts b/sdk/lib/osBindings/CreateOverlayedImageParams.ts index b8579ac7d..aad94f01f 100644 --- a/sdk/lib/osBindings/CreateOverlayedImageParams.ts +++ b/sdk/lib/osBindings/CreateOverlayedImageParams.ts @@ -1,3 +1,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ImageId } from "./ImageId" -export type CreateOverlayedImageParams = { imageId: string } +export type CreateOverlayedImageParams = { imageId: ImageId } diff --git a/sdk/lib/osBindings/DestroyOverlayedImageParams.ts b/sdk/lib/osBindings/DestroyOverlayedImageParams.ts index 82fc1cc67..b5b7484a2 100644 --- a/sdk/lib/osBindings/DestroyOverlayedImageParams.ts +++ b/sdk/lib/osBindings/DestroyOverlayedImageParams.ts @@ -1,3 +1,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Guid } from "./Guid" -export type DestroyOverlayedImageParams = { guid: string } +export type DestroyOverlayedImageParams = { guid: Guid } diff --git a/sdk/lib/osBindings/ImageConfig.ts b/sdk/lib/osBindings/ImageConfig.ts new file mode 100644 index 000000000..2b1033b83 --- /dev/null +++ b/sdk/lib/osBindings/ImageConfig.ts @@ -0,0 +1,8 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ImageSource } from "./ImageSource" + +export type ImageConfig = { + source: ImageSource + arch: string[] + emulateMissingAs: string | null +} diff --git a/sdk/lib/osBindings/ImageMetadata.ts b/sdk/lib/osBindings/ImageMetadata.ts new file mode 100644 index 000000000..b50f7a084 --- /dev/null +++ b/sdk/lib/osBindings/ImageMetadata.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ImageMetadata = { workdir: string; user: string } diff --git a/sdk/lib/osBindings/ImageSource.ts b/sdk/lib/osBindings/ImageSource.ts new file mode 100644 index 000000000..a71684e46 --- /dev/null +++ b/sdk/lib/osBindings/ImageSource.ts @@ -0,0 +1,6 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ImageSource = + | "packed" + | { dockerBuild: { workdir: string | null; dockerfile: string | null } } + | { dockerTag: string } diff --git a/sdk/lib/osBindings/LoginParams.ts b/sdk/lib/osBindings/LoginParams.ts new file mode 100644 index 000000000..272f7ed07 --- /dev/null +++ b/sdk/lib/osBindings/LoginParams.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PasswordType } from "./PasswordType" + +export type LoginParams = { password: PasswordType | null; metadata: any } diff --git a/sdk/lib/osBindings/Manifest.ts b/sdk/lib/osBindings/Manifest.ts index 7f06be25e..15b96bd13 100644 --- a/sdk/lib/osBindings/Manifest.ts +++ b/sdk/lib/osBindings/Manifest.ts @@ -3,6 +3,7 @@ import type { Alerts } from "./Alerts" import type { Dependencies } from "./Dependencies" import type { Description } from "./Description" import type { HardwareRequirements } from "./HardwareRequirements" +import type { ImageConfig } from "./ImageConfig" import type { ImageId } from "./ImageId" import type { PackageId } from "./PackageId" import type { Version } from "./Version" @@ -20,7 +21,7 @@ export type Manifest = { marketingSite: string donationUrl: string | null description: Description - images: Array + images: { [key: ImageId]: ImageConfig } assets: Array volumes: Array alerts: Alerts diff --git a/sdk/lib/osBindings/index.ts b/sdk/lib/osBindings/index.ts index efd7a5efb..06a4bed7e 100644 --- a/sdk/lib/osBindings/index.ts +++ b/sdk/lib/osBindings/index.ts @@ -69,7 +69,10 @@ export { HostKind } from "./HostKind" export { HostnameInfo } from "./HostnameInfo" export { Hosts } from "./Hosts" export { Host } from "./Host" +export { ImageConfig } from "./ImageConfig" export { ImageId } from "./ImageId" +export { ImageMetadata } from "./ImageMetadata" +export { ImageSource } from "./ImageSource" export { InstalledState } from "./InstalledState" export { InstallingInfo } from "./InstallingInfo" export { InstallingState } from "./InstallingState" @@ -78,6 +81,7 @@ export { IpInfo } from "./IpInfo" export { LanInfo } from "./LanInfo" export { ListServiceInterfacesParams } from "./ListServiceInterfacesParams" export { ListVersionSignersParams } from "./ListVersionSignersParams" +export { LoginParams } from "./LoginParams" export { MainStatus } from "./MainStatus" export { Manifest } from "./Manifest" export { MaybeUtf8String } from "./MaybeUtf8String" diff --git a/sdk/lib/test/configBuilder.test.ts b/sdk/lib/test/configBuilder.test.ts index 2df40b95c..9738475a6 100644 --- a/sdk/lib/test/configBuilder.test.ts +++ b/sdk/lib/test/configBuilder.test.ts @@ -400,7 +400,7 @@ describe("values", () => { long: "", }, containers: {}, - images: [], + images: {}, volumes: [], assets: [], alerts: { diff --git a/sdk/lib/test/output.sdk.ts b/sdk/lib/test/output.sdk.ts index a0bab1f6e..189491be5 100644 --- a/sdk/lib/test/output.sdk.ts +++ b/sdk/lib/test/output.sdk.ts @@ -21,7 +21,7 @@ export const sdk = StartSdk.of() long: "", }, containers: {}, - images: [], + images: {}, volumes: [], assets: [], alerts: { diff --git a/sdk/lib/types.ts b/sdk/lib/types.ts index 2da92cd56..1f1245adc 100644 --- a/sdk/lib/types.ts +++ b/sdk/lib/types.ts @@ -11,6 +11,7 @@ import { GetPrimaryUrlParams, LanInfo, BindParams, + Manifest, } from "./osBindings" import { MainEffects, ServiceInterfaceType, Signals } from "./StartSdk" @@ -110,9 +111,26 @@ export namespace ExpectedExports { */ export type dependencyConfig = Record - export type Properties = (options: { + export type properties = (options: { effects: Effects }) => Promise + + export type manifest = Manifest +} +export type ABI = { + setConfig: ExpectedExports.setConfig + getConfig: ExpectedExports.getConfig + createBackup: ExpectedExports.createBackup + restoreBackup: ExpectedExports.restoreBackup + actions: ExpectedExports.actions + actionsMetadata: ExpectedExports.actionsMetadata + main: ExpectedExports.main + afterShutdown: ExpectedExports.afterShutdown + init: ExpectedExports.init + uninit: ExpectedExports.uninit + dependencyConfig: ExpectedExports.dependencyConfig + properties: ExpectedExports.properties + manifest: ExpectedExports.manifest } export type TimeMs = number export type VersionString = string @@ -453,8 +471,8 @@ export type Effects = { /** Exists could be useful during the runtime to know if some service is running, option dep */ running(options: { packageId: PackageId }): Promise - restart(): void - shutdown(): void + restart(): Promise + shutdown(): Promise mount(options: { location: string diff --git a/sdk/lib/util/Overlay.ts b/sdk/lib/util/Overlay.ts index 794b78732..fbc854c57 100644 --- a/sdk/lib/util/Overlay.ts +++ b/sdk/lib/util/Overlay.ts @@ -8,16 +8,18 @@ const WORKDIR = (imageId: string) => `/media/startos/images/${imageId}/` export class Overlay { private constructor( readonly effects: T.Effects, - readonly imageId: string, + readonly imageId: T.ImageId, readonly rootfs: string, - readonly guid: string, + readonly guid: T.Guid, ) {} static async of( effects: T.Effects, - image: { id: string; sharedRun?: boolean }, + image: { id: T.ImageId; sharedRun?: boolean }, ) { - const { id: imageId, sharedRun } = image - const [rootfs, guid] = await effects.createOverlayedImage({ imageId }) + const { id, sharedRun } = image + const [rootfs, guid] = await effects.createOverlayedImage({ + imageId: id as string, + }) const shared = ["dev", "sys", "proc"] if (!!sharedRun) { @@ -33,7 +35,7 @@ export class Overlay { ]) } - return new Overlay(effects, imageId, rootfs, guid) + return new Overlay(effects, id, rootfs, guid) } async mount(options: MountOptions, path: string): Promise { @@ -97,7 +99,7 @@ export class Overlay { stdout: string | Buffer stderr: string | Buffer }> { - const imageMeta: any = await fs + const imageMeta: T.ImageMetadata = await fs .readFile(`/media/startos/images/${this.imageId}.json`, { encoding: "utf8", }) diff --git a/sdk/lib/util/fileHelper.ts b/sdk/lib/util/fileHelper.ts index 07cfb2c4a..54f1eca06 100644 --- a/sdk/lib/util/fileHelper.ts +++ b/sdk/lib/util/fileHelper.ts @@ -3,7 +3,7 @@ import * as YAML from "yaml" import * as TOML from "@iarna/toml" import _ from "lodash" import * as T from "../types" -import * as fs from "fs" +import * as fs from "node:fs/promises" const previousPath = /(.+?)\/([^/]*)$/ @@ -59,28 +59,24 @@ export class FileHelper { readonly readData: (stringValue: string) => A, ) {} async write(data: A, effects: T.Effects) { - if (previousPath.exec(this.path)) { - await new Promise((resolve, reject) => - fs.mkdir(this.path, (err: any) => (!err ? resolve(null) : reject(err))), - ) + const parent = previousPath.exec(this.path) + if (parent) { + await fs.mkdir(parent[1], { recursive: true }) } - await new Promise((resolve, reject) => - fs.writeFile(this.path, this.writeData(data), (err: any) => - !err ? resolve(null) : reject(err), - ), - ) + await fs.writeFile(this.path, this.writeData(data)) } async read(effects: T.Effects) { - if (!fs.existsSync(this.path)) { + if ( + !(await fs.access(this.path).then( + () => true, + () => false, + )) + ) { return null } return this.readData( - await new Promise((resolve, reject) => - fs.readFile(this.path, (err: any, data: any) => - !err ? resolve(data.toString("utf-8")) : reject(err), - ), - ), + await fs.readFile(this.path).then((data) => data.toString("utf-8")), ) } @@ -142,7 +138,7 @@ export class FileHelper { return new FileHelper( path, (inData) => { - return JSON.stringify(inData, null, 2) + return YAML.stringify(inData, null, 2) }, (inString) => { return shape.unsafeCast(YAML.parse(inString)) diff --git a/sdk/package-lock.json b/sdk/package-lock.json index 0c457540f..a50066b34 100644 --- a/sdk/package-lock.json +++ b/sdk/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "isomorphic-fetch": "^3.0.0", - "lodash": "4.*.*", + "lodash": "^4.17.21", "ts-matches": "^5.4.1" }, "devDependencies": { diff --git a/sdk/package.json b/sdk/package.json index 53ee291ee..d6c3a1ca5 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@start9labs/start-sdk", - "version": "0.3.6-alpha1", + "version": "0.3.6-alpha5", "description": "Software development kit to facilitate packaging services for StartOS", "main": "./cjs/lib/index.js", "types": "./cjs/lib/index.d.ts", @@ -31,8 +31,10 @@ "homepage": "https://github.com/Start9Labs/start-sdk#readme", "dependencies": { "isomorphic-fetch": "^3.0.0", - "lodash": "4.*.*", - "ts-matches": "^5.4.1" + "lodash": "^4.17.21", + "ts-matches": "^5.4.1", + "yaml": "^2.2.2", + "@iarna/toml": "^2.2.5" }, "prettier": { "trailingComma": "all", @@ -41,7 +43,6 @@ "singleQuote": false }, "devDependencies": { - "@iarna/toml": "^2.2.5", "@types/jest": "^29.4.0", "@types/lodash": "^4.17.5", "jest": "^29.4.3", @@ -49,7 +50,6 @@ "ts-jest": "^29.0.5", "ts-node": "^10.9.1", "tsx": "^4.7.1", - "typescript": "^5.0.4", - "yaml": "^2.2.2" + "typescript": "^5.0.4" } } diff --git a/web/projects/ui/src/app/services/api/api.fixures.ts b/web/projects/ui/src/app/services/api/api.fixures.ts index 11d56ff15..5642eece5 100644 --- a/web/projects/ui/src/app/services/api/api.fixures.ts +++ b/web/projects/ui/src/app/services/api/api.fixures.ts @@ -69,7 +69,13 @@ export module Mock { osVersion: '0.2.12', dependencies: {}, hasConfig: true, - images: ['main'], + images: { + main: { + source: 'packed', + arch: ['x86_64', 'aarch64'], + emulateMissingAs: 'aarch64', + }, + }, assets: [], volumes: ['main'], hardwareRequirements: { @@ -116,7 +122,13 @@ export module Mock { }, }, hasConfig: true, - images: ['main'], + images: { + main: { + source: 'packed', + arch: ['x86_64', 'aarch64'], + emulateMissingAs: 'aarch64', + }, + }, assets: [], volumes: ['main'], hardwareRequirements: { @@ -157,7 +169,13 @@ export module Mock { }, }, hasConfig: false, - images: ['main'], + images: { + main: { + source: 'packed', + arch: ['x86_64', 'aarch64'], + emulateMissingAs: 'aarch64', + }, + }, assets: [], volumes: ['main'], hardwareRequirements: {