diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..d408d0a
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,70 @@
+# CI Arboretum : build + tests sur Node 22/24, puis smoke de packaging npx.
+name: CI
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+permissions:
+ contents: read
+
+jobs:
+ test:
+ name: Build & test (Node ${{ matrix.node }})
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ node: [22, 24]
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: ${{ matrix.node }}
+ cache: npm
+ - run: npm ci
+ - run: npm run typecheck
+ - run: npm run build
+ - run: npx vitest run
+
+ pack-smoke:
+ name: Pack & boot smoke (Node 22)
+ runs-on: ubuntu-latest
+ needs: test
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: npm
+ - run: npm ci
+ - run: npm run build
+ # @arboretum/shared est une dépendance runtime non publiée : on packe les
+ # deux tarballs et on les installe ensemble dans un projet vierge.
+ - name: Pack tarballs
+ run: |
+ mkdir -p /tmp/tarballs
+ npm pack -w @arboretum/shared -w git-arboretum --pack-destination /tmp/tarballs
+ ls -l /tmp/tarballs
+ - name: Install tarballs in an empty project
+ run: |
+ mkdir /tmp/smoke
+ cd /tmp/smoke
+ npm init -y
+ npm i /tmp/tarballs/*.tgz
+ - name: Boot smoke (expect 401 on /api/v1/sessions)
+ run: |
+ cd /tmp/smoke
+ timeout 30 node_modules/.bin/arboretum --port 7999 --db ./t.db &
+ server_pid=$!
+ code=000
+ for i in $(seq 1 20); do
+ sleep 0.5
+ code=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:7999/api/v1/sessions || true)
+ if [ "$code" = "401" ]; then break; fi
+ done
+ echo "GET /api/v1/sessions -> HTTP $code"
+ kill "$server_pid" 2>/dev/null || true
+ test "$code" = "401"
diff --git a/.gitignore b/.gitignore
index 1e5e42c..f534d40 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,4 @@ dist/
.DS_Store
spikes/**/tmp/
spikes/**/captures/
+packages/server/public/
diff --git a/.nvmrc b/.nvmrc
new file mode 100644
index 0000000..2bd5a0a
--- /dev/null
+++ b/.nvmrc
@@ -0,0 +1 @@
+22
diff --git a/package-lock.json b/package-lock.json
index 7a108d3..79734f9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,6 +23,90 @@
"resolved": "packages/shared",
"link": true
},
+ "node_modules/@arboretum/web": {
+ "resolved": "packages/web",
+ "link": true
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
+ "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.7"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
+ "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@emnapi/core": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
+ "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.1",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
+ "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
+ "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz",
@@ -694,6 +778,67 @@
"node": ">=18.0.0 <25.0.0"
}
},
+ "node_modules/@intlify/core-base": {
+ "version": "11.4.5",
+ "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.4.5.tgz",
+ "integrity": "sha512-lja3F/iKVIvTa48mIwmrIeDcQUFZ0F0drvFvT8AwINOvbwnAzl/S/p8p2DxILZpWEUHRi1qewfWNIkMvhD3kKA==",
+ "license": "MIT",
+ "dependencies": {
+ "@intlify/devtools-types": "11.4.5",
+ "@intlify/message-compiler": "11.4.5",
+ "@intlify/shared": "11.4.5"
+ },
+ "engines": {
+ "node": ">= 22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ }
+ },
+ "node_modules/@intlify/devtools-types": {
+ "version": "11.4.5",
+ "resolved": "https://registry.npmjs.org/@intlify/devtools-types/-/devtools-types-11.4.5.tgz",
+ "integrity": "sha512-W5vydP9Yq3t82IyWqCM6aR0BTWCZrN5RAwjZEPpH8I2OQWp2RLy03Evh2ANZlSMhcvGAoyDg25k0so85Kwncpw==",
+ "license": "MIT",
+ "dependencies": {
+ "@intlify/core-base": "11.4.5",
+ "@intlify/shared": "11.4.5"
+ },
+ "engines": {
+ "node": ">= 22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ }
+ },
+ "node_modules/@intlify/message-compiler": {
+ "version": "11.4.5",
+ "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.4.5.tgz",
+ "integrity": "sha512-IEOZiHtbQopyPc/Dz2M869lOlZYX1SdcniNJwphATDYHhovvIneEKf1EFF37DE7NAABZtza1FNtnwwqZWInfpw==",
+ "license": "MIT",
+ "dependencies": {
+ "@intlify/shared": "11.4.5",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ }
+ },
+ "node_modules/@intlify/shared": {
+ "version": "11.4.5",
+ "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.4.5.tgz",
+ "integrity": "sha512-g/i5mtdUa9ia/8BaJ4w6ZRHgAXYQd9XyCaQPRMvsd8d5qmZwkjoTmHrNsI28Q/7I8h+2ijUkI4uEnnMCziKupQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ }
+ },
"node_modules/@isaacs/cliui": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz",
@@ -703,13 +848,55 @@
"node": ">=18"
}
},
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
- "dev": true,
"license": "MIT"
},
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
"node_modules/@lukeed/ms": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz",
@@ -719,12 +906,305 @@
"node": ">=8"
}
},
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz",
+ "integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@tybys/wasm-util": "^0.10.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1"
+ }
+ },
+ "node_modules/@oxc-project/types": {
+ "version": "0.133.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz",
+ "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ }
+ },
"node_modules/@pinojs/redact": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz",
"integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==",
"license": "MIT"
},
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz",
+ "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz",
+ "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz",
+ "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz",
+ "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz",
+ "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz",
+ "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz",
+ "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz",
+ "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz",
+ "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz",
+ "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz",
+ "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz",
+ "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-wasm32-wasi": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz",
+ "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "1.10.0",
+ "@emnapi/runtime": "1.10.0",
+ "@napi-rs/wasm-runtime": "^1.1.4"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz",
+ "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz",
+ "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
+ "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.61.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.61.1.tgz",
@@ -1075,6 +1555,289 @@
"win32"
]
},
+ "node_modules/@tailwindcss/node": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz",
+ "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/remapping": "^2.3.5",
+ "enhanced-resolve": "^5.21.0",
+ "jiti": "^2.6.1",
+ "lightningcss": "1.32.0",
+ "magic-string": "^0.30.21",
+ "source-map-js": "^1.2.1",
+ "tailwindcss": "4.3.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz",
+ "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.3.0",
+ "@tailwindcss/oxide-darwin-arm64": "4.3.0",
+ "@tailwindcss/oxide-darwin-x64": "4.3.0",
+ "@tailwindcss/oxide-freebsd-x64": "4.3.0",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.3.0",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.3.0",
+ "@tailwindcss/oxide-linux-x64-musl": "4.3.0",
+ "@tailwindcss/oxide-wasm32-wasi": "4.3.0",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.3.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-android-arm64": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz",
+ "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-arm64": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz",
+ "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-x64": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz",
+ "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-freebsd-x64": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz",
+ "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz",
+ "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz",
+ "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz",
+ "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz",
+ "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-musl": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz",
+ "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz",
+ "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==",
+ "bundleDependencies": [
+ "@napi-rs/wasm-runtime",
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@tybys/wasm-util",
+ "@emnapi/wasi-threads",
+ "tslib"
+ ],
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.10.0",
+ "@emnapi/runtime": "^1.10.0",
+ "@emnapi/wasi-threads": "^1.2.1",
+ "@napi-rs/wasm-runtime": "^1.1.4",
+ "@tybys/wasm-util": "^0.10.1",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz",
+ "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz",
+ "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/vite": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.0.tgz",
+ "integrity": "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tailwindcss/node": "4.3.0",
+ "@tailwindcss/oxide": "4.3.0",
+ "tailwindcss": "4.3.0"
+ },
+ "peerDependencies": {
+ "vite": "^5.2.0 || ^6 || ^7 || ^8"
+ }
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.2",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
+ "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@types/chai": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
@@ -1120,6 +1883,23 @@
"@types/node": "*"
}
},
+ "node_modules/@vitejs/plugin-vue": {
+ "version": "6.0.7",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.7.tgz",
+ "integrity": "sha512-km+p+XdSz9Sxm5rqUbqcSfZYaAniKxWBj1KURl+Jr7UaPvvX7BmaWMdP69I5rrFDeQGyxAG7NXdc57vz+snhWg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rolldown/pluginutils": "^1.0.1"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0",
+ "vue": "^3.2.25"
+ }
+ },
"node_modules/@vitest/expect": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.6.tgz",
@@ -1235,6 +2015,217 @@
"url": "https://opencollective.com/vitest"
}
},
+ "node_modules/@volar/language-core": {
+ "version": "2.4.28",
+ "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz",
+ "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/source-map": "2.4.28"
+ }
+ },
+ "node_modules/@volar/source-map": {
+ "version": "2.4.28",
+ "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz",
+ "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@volar/typescript": {
+ "version": "2.4.28",
+ "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz",
+ "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/language-core": "2.4.28",
+ "path-browserify": "^1.0.1",
+ "vscode-uri": "^3.0.8"
+ }
+ },
+ "node_modules/@vue/compiler-core": {
+ "version": "3.5.38",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.38.tgz",
+ "integrity": "sha512-s99aGxWYig9ErHbct27KXEGhrBYlRI6c4MwAgXErOAbX9xiW37/uMa+XUDO69zLz83dng8UUZ70CTOJrLrYrEQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.7",
+ "@vue/shared": "3.5.38",
+ "entities": "^7.0.1",
+ "estree-walker": "^2.0.2",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "node_modules/@vue/compiler-core/node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "license": "MIT"
+ },
+ "node_modules/@vue/compiler-dom": {
+ "version": "3.5.38",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.38.tgz",
+ "integrity": "sha512-JTqp25l8aFfJYF7/KmsXZjAxJz7T+SjmTJLoXVjHtc2BrSgSiW2n9Aem/cWq1OPe68A8JL06B3eVdhlP0H4TVw==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-core": "3.5.38",
+ "@vue/shared": "3.5.38"
+ }
+ },
+ "node_modules/@vue/compiler-sfc": {
+ "version": "3.5.38",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.38.tgz",
+ "integrity": "sha512-DuA2GiZawSEW442iw/9+Fkol8hTgb4Ke5KkhmSry65QA7YuyMbIdy8p0XZRMvNwJdgRz307W8g1CSzdvS4nuNg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.7",
+ "@vue/compiler-core": "3.5.38",
+ "@vue/compiler-dom": "3.5.38",
+ "@vue/compiler-ssr": "3.5.38",
+ "@vue/shared": "3.5.38",
+ "estree-walker": "^2.0.2",
+ "magic-string": "^0.30.21",
+ "postcss": "^8.5.15",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "node_modules/@vue/compiler-sfc/node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "license": "MIT"
+ },
+ "node_modules/@vue/compiler-ssr": {
+ "version": "3.5.38",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.38.tgz",
+ "integrity": "sha512-7s+W5Gc42FGxZMcuwl8H5B29T8BJPMdBT7KHFE+BbAuZ/iTEdTtv7z2XiMjiaUUw4w3ZcCEdHs36RuYJ2VA7bA==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-dom": "3.5.38",
+ "@vue/shared": "3.5.38"
+ }
+ },
+ "node_modules/@vue/devtools-api": {
+ "version": "7.7.9",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz",
+ "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-kit": "^7.7.9"
+ }
+ },
+ "node_modules/@vue/devtools-kit": {
+ "version": "7.7.9",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz",
+ "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-shared": "^7.7.9",
+ "birpc": "^2.3.0",
+ "hookable": "^5.5.3",
+ "mitt": "^3.0.1",
+ "perfect-debounce": "^1.0.0",
+ "speakingurl": "^14.0.1",
+ "superjson": "^2.2.2"
+ }
+ },
+ "node_modules/@vue/devtools-shared": {
+ "version": "7.7.9",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz",
+ "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==",
+ "license": "MIT",
+ "dependencies": {
+ "rfdc": "^1.4.1"
+ }
+ },
+ "node_modules/@vue/language-core": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.3.4.tgz",
+ "integrity": "sha512-IuHqQ5zGGOE7CXP72VX6A42IVeIzYv4WAhO6arej11TRNqtdZfGyH8Yr2FOCaDX0dSQG+JwULLoFHGY1igYVjQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/language-core": "2.4.28",
+ "@vue/compiler-dom": "^3.5.0",
+ "@vue/shared": "^3.5.0",
+ "alien-signals": "^3.2.0",
+ "muggle-string": "^0.4.1",
+ "path-browserify": "^1.0.1",
+ "picomatch": "^4.0.4"
+ }
+ },
+ "node_modules/@vue/reactivity": {
+ "version": "3.5.38",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.38.tgz",
+ "integrity": "sha512-pG6LV/NDNRbKizcUjFFLAfjaL8mcv4DmR9avNcUw2gDHBzZneuS2TWCmp633ynzxz9YYKNeEPK2I8Wraqy2HUQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/shared": "3.5.38"
+ }
+ },
+ "node_modules/@vue/runtime-core": {
+ "version": "3.5.38",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.38.tgz",
+ "integrity": "sha512-iyW8WVfF1CpCXxncZY5Ei6rSd6oZr5DgEom//fUjRBRl56AXPD+s9ATvukRt77ZFTuYlnVA1bxY+dJB94tWVYw==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/reactivity": "3.5.38",
+ "@vue/shared": "3.5.38"
+ }
+ },
+ "node_modules/@vue/runtime-dom": {
+ "version": "3.5.38",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.38.tgz",
+ "integrity": "sha512-apX2wt9sdfDshS+a2xueFZLVpt0GkRJZSoPmrW/SA4yzXTznhfcMVW59gr7h4YQeY0vJhdJkk2rsIDwgfFgC5A==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/reactivity": "3.5.38",
+ "@vue/runtime-core": "3.5.38",
+ "@vue/shared": "3.5.38",
+ "csstype": "^3.2.3"
+ }
+ },
+ "node_modules/@vue/server-renderer": {
+ "version": "3.5.38",
+ "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.38.tgz",
+ "integrity": "sha512-vue8vbf2QlV4quHqzwmJy6dWfmRhP1J8l4wtZg60CL6VoKqcPY2oe7may3+1d9qfpedjK5PRLFqd5k3Isj9mUw==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-ssr": "3.5.38",
+ "@vue/shared": "3.5.38"
+ },
+ "peerDependencies": {
+ "vue": "3.5.38"
+ }
+ },
+ "node_modules/@vue/shared": {
+ "version": "3.5.38",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.38.tgz",
+ "integrity": "sha512-FTW0AFZNaK5/mOqvGBwVfUlNLU38TiQn4+DQgIFUnrBBJQ1crMJ82yeGQLV5jyKFsO8yRukpbuP7x+nRbH6aug==",
+ "license": "MIT"
+ },
+ "node_modules/@xterm/addon-fit": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.11.0.tgz",
+ "integrity": "sha512-jYcgT6xtVYhnhgxh3QgYDnnNMYTcf8ElbxxFzX0IZo+vabQqSPAjC3c1wJrKB5E19VwQei89QCiZZP86DCPF7g==",
+ "license": "MIT"
+ },
+ "node_modules/@xterm/addon-webgl": {
+ "version": "0.19.0",
+ "resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.19.0.tgz",
+ "integrity": "sha512-b3fMOsyLVuCeNJWxolACEUED0vm7qC0cy4wRvf3oURSzDTYVQiGPhTnhWZwIHdvC48Y+oLhvYXnY4XDXPoJo6A==",
+ "license": "MIT"
+ },
+ "node_modules/@xterm/xterm": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.0.0.tgz",
+ "integrity": "sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg==",
+ "license": "MIT",
+ "workspaces": [
+ "addons/*"
+ ]
+ },
"node_modules/abstract-logging": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
@@ -1274,6 +2265,13 @@
}
}
},
+ "node_modules/alien-signals": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.2.1.tgz",
+ "integrity": "sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/assertion-error": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
@@ -1342,6 +2340,15 @@
],
"license": "MIT"
},
+ "node_modules/birpc": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz",
+ "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
@@ -1457,6 +2464,21 @@
"url": "https://opencollective.com/express"
}
},
+ "node_modules/copy-anything": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz",
+ "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==",
+ "license": "MIT",
+ "dependencies": {
+ "is-what": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mesqueeb"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -1471,6 +2493,12 @@
"node": ">= 8"
}
},
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "license": "MIT"
+ },
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -1571,6 +2599,32 @@
"once": "^1.4.0"
}
},
+ "node_modules/enhanced-resolve": {
+ "version": "5.24.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.0.tgz",
+ "integrity": "sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.3.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/entities": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
+ "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
"node_modules/es-module-lexer": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
@@ -1877,6 +2931,19 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/hookable": {
+ "version": "5.5.3",
+ "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
+ "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
+ "license": "MIT"
+ },
"node_modules/http-errors": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
@@ -1938,6 +3005,18 @@
"node": ">= 10"
}
},
+ "node_modules/is-what": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz",
+ "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mesqueeb"
+ }
+ },
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -1959,6 +3038,16 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/jiti": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz",
+ "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
"node_modules/js-tokens": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz",
@@ -2028,6 +3117,278 @@
],
"license": "MIT"
},
+ "node_modules/lightningcss": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
+ "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.32.0",
+ "lightningcss-darwin-arm64": "1.32.0",
+ "lightningcss-darwin-x64": "1.32.0",
+ "lightningcss-freebsd-x64": "1.32.0",
+ "lightningcss-linux-arm-gnueabihf": "1.32.0",
+ "lightningcss-linux-arm64-gnu": "1.32.0",
+ "lightningcss-linux-arm64-musl": "1.32.0",
+ "lightningcss-linux-x64-gnu": "1.32.0",
+ "lightningcss-linux-x64-musl": "1.32.0",
+ "lightningcss-win32-arm64-msvc": "1.32.0",
+ "lightningcss-win32-x64-msvc": "1.32.0"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
+ "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
+ "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
+ "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
+ "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
+ "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
+ "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
+ "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
+ "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
+ "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
+ "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
+ "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
"node_modules/loupe": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz",
@@ -2048,7 +3409,6 @@
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
@@ -2111,6 +3471,12 @@
"node": ">=16 || 14 >=14.17"
}
},
+ "node_modules/mitt": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
+ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+ "license": "MIT"
+ },
"node_modules/mkdirp-classic": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
@@ -2124,11 +3490,17 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/muggle-string": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz",
+ "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/nanoid": {
"version": "3.3.12",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -2191,6 +3563,13 @@
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
"license": "BlueOak-1.0.0"
},
+ "node_modules/path-browserify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
+ "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -2233,11 +3612,16 @@
"node": ">= 14.16"
}
},
+ "node_modules/perfect-debounce": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
+ "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
+ "license": "MIT"
+ },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
@@ -2253,6 +3637,27 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/pinia": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz",
+ "integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-api": "^7.7.7"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/posva"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.5.0",
+ "vue": "^3.5.11"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
"node_modules/pino": {
"version": "10.3.1",
"resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz",
@@ -2294,7 +3699,6 @@
"version": "8.5.15",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -2450,6 +3854,40 @@
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
"license": "MIT"
},
+ "node_modules/rolldown": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz",
+ "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@oxc-project/types": "=0.133.0",
+ "@rolldown/pluginutils": "^1.0.0"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm64": "1.0.3",
+ "@rolldown/binding-darwin-arm64": "1.0.3",
+ "@rolldown/binding-darwin-x64": "1.0.3",
+ "@rolldown/binding-freebsd-x64": "1.0.3",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.0.3",
+ "@rolldown/binding-linux-arm64-gnu": "1.0.3",
+ "@rolldown/binding-linux-arm64-musl": "1.0.3",
+ "@rolldown/binding-linux-ppc64-gnu": "1.0.3",
+ "@rolldown/binding-linux-s390x-gnu": "1.0.3",
+ "@rolldown/binding-linux-x64-gnu": "1.0.3",
+ "@rolldown/binding-linux-x64-musl": "1.0.3",
+ "@rolldown/binding-openharmony-arm64": "1.0.3",
+ "@rolldown/binding-wasm32-wasi": "1.0.3",
+ "@rolldown/binding-win32-arm64-msvc": "1.0.3",
+ "@rolldown/binding-win32-x64-msvc": "1.0.3"
+ }
+ },
"node_modules/rollup": {
"version": "4.61.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.61.1.tgz",
@@ -2684,7 +4122,15 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
- "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/speakingurl": {
+ "version": "14.0.1",
+ "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
+ "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@@ -2759,6 +4205,39 @@
"url": "https://github.com/sponsors/antfu"
}
},
+ "node_modules/superjson": {
+ "version": "2.2.6",
+ "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz",
+ "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==",
+ "license": "MIT",
+ "dependencies": {
+ "copy-anything": "^4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz",
+ "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tapable": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz",
+ "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
"node_modules/tar-fs": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
@@ -2884,6 +4363,14 @@
"node": ">=0.6"
}
},
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD",
+ "optional": true
+ },
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@@ -2900,7 +4387,7 @@
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
@@ -3094,6 +4581,99 @@
}
}
},
+ "node_modules/vscode-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz",
+ "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vue": {
+ "version": "3.5.38",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.38.tgz",
+ "integrity": "sha512-vAMKHfImQlYSy0C+PBue4s3ERZ2xGKfgZg5GXAsLInq1dyh2H78ILVP5sK0KPFPVW4kv+OGCIvBEondcjpZp7A==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-dom": "3.5.38",
+ "@vue/compiler-sfc": "3.5.38",
+ "@vue/runtime-dom": "3.5.38",
+ "@vue/server-renderer": "3.5.38",
+ "@vue/shared": "3.5.38"
+ },
+ "peerDependencies": {
+ "typescript": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vue-i18n": {
+ "version": "11.4.5",
+ "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.4.5.tgz",
+ "integrity": "sha512-rm8YJ6RpjOrkcgS2GLrZwLvs/VbhxbTSuEspbyXDo233+fPK0OMFNLOj3fdQYVKdOgcpSfLW91JhbqgpkkcBWA==",
+ "license": "MIT",
+ "dependencies": {
+ "@intlify/core-base": "11.4.5",
+ "@intlify/devtools-types": "11.4.5",
+ "@intlify/shared": "11.4.5",
+ "@vue/devtools-api": "^6.5.0"
+ },
+ "engines": {
+ "node": ">= 22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ },
+ "peerDependencies": {
+ "vue": "^3.0.0"
+ }
+ },
+ "node_modules/vue-i18n/node_modules/@vue/devtools-api": {
+ "version": "6.6.4",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+ "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
+ "license": "MIT"
+ },
+ "node_modules/vue-router": {
+ "version": "4.6.4",
+ "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz",
+ "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-api": "^6.6.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/posva"
+ },
+ "peerDependencies": {
+ "vue": "^3.5.0"
+ }
+ },
+ "node_modules/vue-router/node_modules/@vue/devtools-api": {
+ "version": "6.6.4",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+ "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
+ "license": "MIT"
+ },
+ "node_modules/vue-tsc": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.3.4.tgz",
+ "integrity": "sha512-XA/JqmQwS2GZmfgpjOEGdrKwaTSEuPwxpHa7/t6f4yiGrJb3gVHTPb9wBfByMNZwQ+xDXs41b8gaS2DKsOozUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/typescript": "2.4.28",
+ "@vue/language-core": "3.3.4"
+ },
+ "bin": {
+ "vue-tsc": "bin/vue-tsc.js"
+ },
+ "peerDependencies": {
+ "typescript": ">=5.0.0"
+ }
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -3178,6 +4758,106 @@
"packages/shared": {
"name": "@arboretum/shared",
"version": "0.1.0"
+ },
+ "packages/web": {
+ "name": "@arboretum/web",
+ "version": "0.1.0",
+ "dependencies": {
+ "@arboretum/shared": "*",
+ "@xterm/addon-fit": "^0.11.0",
+ "@xterm/addon-webgl": "^0.19.0",
+ "@xterm/xterm": "^6.0.0",
+ "pinia": "^3.0.4",
+ "vue": "^3.5.38",
+ "vue-i18n": "^11.4.5",
+ "vue-router": "^4.6.4"
+ },
+ "devDependencies": {
+ "@tailwindcss/vite": "^4.3.0",
+ "@vitejs/plugin-vue": "^6.0.7",
+ "tailwindcss": "^4.3.0",
+ "typescript": "^5.7.0",
+ "vite": "^8.0.16",
+ "vue-tsc": "^3.3.4"
+ }
+ },
+ "packages/web/node_modules/vite": {
+ "version": "8.0.16",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz",
+ "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lightningcss": "^1.32.0",
+ "picomatch": "^4.0.4",
+ "postcss": "^8.5.15",
+ "rolldown": "1.0.3",
+ "tinyglobby": "^0.2.17"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "@vitejs/devtools": "^0.1.18",
+ "esbuild": "^0.27.0 || ^0.28.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "@vitejs/devtools": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
}
}
}
diff --git a/package.json b/package.json
index 8de8b30..f82d786 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
"scripts": {
"build": "npm run build -w @arboretum/shared -w git-arboretum -w @arboretum/web",
"typecheck": "tsc -b packages/shared packages/server",
+ "pack": "npm run build && npm pack -w git-arboretum",
"test": "vitest run",
"dev:server": "npm run dev -w git-arboretum",
"dev:web": "npm run dev -w @arboretum/web"
diff --git a/packages/server/package.json b/packages/server/package.json
index 36f7968..4baed9b 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -18,6 +18,7 @@
"scripts": {
"build": "tsc -b",
"dev": "tsc -b --watch & node --watch dist/index.js",
+ "prepack": "node scripts/copy-web.mjs",
"test": "vitest run"
},
"dependencies": {
diff --git a/packages/server/scripts/acceptance-p1.mjs b/packages/server/scripts/acceptance-p1.mjs
new file mode 100644
index 0000000..cb28521
--- /dev/null
+++ b/packages/server/scripts/acceptance-p1.mjs
@@ -0,0 +1,167 @@
+#!/usr/bin/env node
+// Acceptation P1 (sans navigateur, sans quota Claude) : vrai daemon + vrai client WS.
+// Couvre : login, hello, sub, spawn bash, attach+replay, stdin/output, flood 10 Mo
+// avec flow control par ACK + RSS borné, déconnexion/reconnexion+replay, kill, drain.
+import { spawn } from 'node:child_process';
+import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
+import { tmpdir } from 'node:os';
+import { join, dirname } from 'node:path';
+import { fileURLToPath } from 'node:url';
+import { createRequire } from 'node:module';
+
+const require = createRequire(import.meta.url);
+const WebSocket = require('ws');
+
+const PORT = 7541;
+const ORIGIN = `http://127.0.0.1:${PORT}`;
+const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
+const serverDir = join(dirname(fileURLToPath(import.meta.url)), '..');
+const results = [];
+const check = (name, ok, detail = '') => {
+ results.push({ name, ok, detail });
+ console.log(`${ok ? '✅' : '❌'} ${name}${detail ? ` — ${detail}` : ''}`);
+};
+
+const tmp = mkdtempSync(join(tmpdir(), 'arb-accept-'));
+const srv = spawn('node', [join(serverDir, 'dist', 'index.js'), '--port', String(PORT), '--db', join(tmp, 'a.db')], {
+ env: { ...process.env, ARBORETUM_LOG: 'warn' },
+ stdio: ['ignore', 'pipe', 'pipe'],
+});
+let srvOut = '';
+srv.stdout.on('data', (d) => (srvOut += d));
+srv.stderr.on('data', (d) => (srvOut += d));
+
+try {
+ await sleep(1500);
+ const token = /arb_[0-9a-f]+/.exec(srvOut)?.[0];
+ check('boot + token bootstrap', !!token);
+
+ const login = await fetch(`${ORIGIN}/api/v1/auth/login`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json', Origin: ORIGIN },
+ body: JSON.stringify({ token }),
+ });
+ const cookie = login.headers.get('set-cookie')?.split(';')[0] ?? '';
+ check('login → cookie', login.status === 200 && cookie.startsWith('arb_session='));
+
+ // Client WS minimal multiplexé
+ function wsClient() {
+ const ws = new WebSocket(`ws://127.0.0.1:${PORT}/ws`, { headers: { Origin: ORIGIN, Cookie: cookie } });
+ ws.binaryType = 'arraybuffer';
+ const state = { msgs: [], outputs: new Map(), resyncs: new Map(), bytes: new Map() };
+ ws.on('message', (data, isBinary) => {
+ if (!isBinary) {
+ state.msgs.push(JSON.parse(String(data)));
+ return;
+ }
+ const buf = Buffer.from(data);
+ const type = buf.readUInt8(0);
+ const channel = buf.readUInt32LE(1);
+ const payload = buf.subarray(5);
+ if (type === 0x02) {
+ state.resyncs.set(channel, (state.resyncs.get(channel) ?? 0) + 1);
+ state.bytes.set(channel, 0); // resync : le compteur ACK repart de zéro
+ state.outputs.set(channel, payload.toString('latin1'));
+ } else {
+ state.outputs.set(channel, ((state.outputs.get(channel) ?? '') + payload.toString('latin1')).slice(-300000));
+ const total = (state.bytes.get(channel) ?? 0) + payload.length;
+ state.bytes.set(channel, total);
+ // ACK tous les 64 Kio traités (invariant : pas d'ACK <= LOW_WATERMARK)
+ const acked = state.lastAck?.get(channel) ?? 0;
+ if (total - acked >= 64 * 1024) {
+ (state.lastAck ??= new Map()).set(channel, total);
+ ws.send(JSON.stringify({ type: 'ack', channel, bytes: total }));
+ }
+ }
+ });
+ const waitMsg = async (pred, timeout = 8000) => {
+ const t0 = Date.now();
+ while (Date.now() - t0 < timeout) {
+ const m = state.msgs.find(pred);
+ if (m) return m;
+ await sleep(50);
+ }
+ return null;
+ };
+ return { ws, state, waitMsg, send: (m) => ws.send(JSON.stringify(m)) };
+ }
+
+ const c1 = wsClient();
+ await new Promise((res, rej) => (c1.ws.on('open', res), c1.ws.on('error', rej)));
+ c1.send({ type: 'hello', protocol: 1 });
+ check('hello_ok', !!(await c1.waitMsg((m) => m.type === 'hello_ok')));
+ c1.send({ type: 'sub', topics: ['sessions'] });
+
+ // Spawn bash + attach
+ const created = await fetch(`${ORIGIN}/api/v1/sessions`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json', Origin: ORIGIN, Cookie: cookie },
+ body: JSON.stringify({ cwd: tmp, command: 'bash' }),
+ });
+ const sid = (await created.json()).session.id;
+ check('spawn bash', created.status === 201 && !!sid);
+
+ c1.send({ type: 'attach', sessionId: sid, mode: 'interactive', cols: 120, rows: 32 });
+ const att = await c1.waitMsg((m) => m.type === 'attached');
+ check('attach interactif + controlling', att?.controlling === true);
+ const ch = att.channel;
+
+ // stdin → output
+ await sleep(400);
+ c1.send({ type: 'stdin', channel: ch, data: 'echo ACCEPT-$((21*2))\r' });
+ await sleep(800);
+ check('stdin → output', (c1.state.outputs.get(ch) ?? '').includes('ACCEPT-42'));
+
+ // Flood 10 Mo (base64 → ~14 Mo de flux TTY) avec ACK, RSS serveur borné
+ const rssBefore = readFileSync(`/proc/${srv.pid}/status`, 'utf8').match(/VmRSS:\s+(\d+) kB/)?.[1];
+ c1.send({ type: 'stdin', channel: ch, data: 'head -c 10485760 /dev/urandom | base64; echo FLOOD-END-MARKER\r' });
+ const t0 = Date.now();
+ let floodOk = false;
+ let received = 0;
+ while (Date.now() - t0 < 60000) {
+ received = c1.state.bytes.get(ch) ?? 0;
+ if ((c1.state.outputs.get(ch) ?? '').includes('FLOOD-END-MARKER')) {
+ floodOk = true;
+ break;
+ }
+ await sleep(100);
+ }
+ const rssAfter = readFileSync(`/proc/${srv.pid}/status`, 'utf8').match(/VmRSS:\s+(\d+) kB/)?.[1];
+ const rssMb = Math.round((Number(rssAfter) - Number(rssBefore)) / 1024);
+ check('flood 10 Mo complet via ACK', floodOk && received > 13_000_000, `${(received / 1048576).toFixed(1)} Mo reçus en ${Date.now() - t0} ms`);
+ check('RSS serveur borné pendant le flood', Number(rssAfter) < 300 * 1024, `Δ ${rssMb} Mo (RSS ${Math.round(Number(rssAfter) / 1024)} Mo)`);
+
+ // Déconnexion brutale → la session survit → reconnexion + replay
+ c1.ws.terminate();
+ await sleep(2000);
+ const c2 = wsClient();
+ await new Promise((res, rej) => (c2.ws.on('open', res), c2.ws.on('error', rej)));
+ c2.send({ type: 'hello', protocol: 1 });
+ await c2.waitMsg((m) => m.type === 'hello_ok');
+ c2.send({ type: 'attach', sessionId: sid, mode: 'interactive', cols: 100, rows: 30 });
+ const att2 = await c2.waitMsg((m) => m.type === 'attached');
+ await sleep(500);
+ const replay = c2.state.outputs.get(att2.channel) ?? '';
+ check('reconnexion : session vivante + replay', att2?.controlling === true && c2.state.resyncs.get(att2.channel) === 1 && replay.length > 1000, `replay ${(replay.length / 1024).toFixed(0)} Kio`);
+ c2.send({ type: 'stdin', channel: att2.channel, data: 'echo AFTER-RECONNECT\r' });
+ await sleep(800);
+ check('terminal utilisable après reconnexion', (c2.state.outputs.get(att2.channel) ?? '').includes('AFTER-RECONNECT'));
+
+ // Kill + notification session_exit (sub)
+ c2.send({ type: 'sub', topics: ['sessions'] });
+ await fetch(`${ORIGIN}/api/v1/sessions/${sid}`, { method: 'DELETE', headers: { Origin: ORIGIN, Cookie: cookie } });
+ const exited = await c2.waitMsg((m) => m.type === 'session_exit' && m.sessionId === sid, 8000);
+ const detached = await c2.waitMsg((m) => m.type === 'detached' && m.reason === 'session_exit', 8000);
+ check('kill → session_exit + detached broadcast', !!exited && !!detached);
+ c2.ws.close();
+} catch (err) {
+ check('exception', false, String(err));
+} finally {
+ srv.kill('SIGTERM');
+ await sleep(1500);
+ check('arrêt propre du daemon (SIGTERM)', srv.exitCode === 0 || srv.signalCode === null || srv.exitCode === null);
+ rmSync(tmp, { recursive: true, force: true });
+ const failed = results.filter((r) => !r.ok);
+ console.log(failed.length === 0 ? '\nACCEPTANCE P1: ALL GREEN' : `\nACCEPTANCE P1: ${failed.length} FAILURE(S)`);
+ process.exit(failed.length === 0 ? 0 : 1);
+}
diff --git a/packages/server/scripts/copy-web.mjs b/packages/server/scripts/copy-web.mjs
new file mode 100644
index 0000000..736a7b1
--- /dev/null
+++ b/packages/server/scripts/copy-web.mjs
@@ -0,0 +1,31 @@
+#!/usr/bin/env node
+// Embarque la SPA buildée (packages/web/dist) dans public/ du package server,
+// pour que le tarball npm soit autonome. Branché sur le hook "prepack".
+// ARBORETUM_PACK_NO_WEB=1 : mode tolérant (CI/smoke) — placeholder à la place du front.
+import { cpSync, existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
+import { join, dirname } from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const serverDir = join(dirname(fileURLToPath(import.meta.url)), '..');
+const webDist = join(serverDir, '..', 'web', 'dist');
+const publicDir = join(serverDir, 'public');
+
+if (!existsSync(webDist)) {
+ if (process.env.ARBORETUM_PACK_NO_WEB === '1') {
+ rmSync(publicDir, { recursive: true, force: true });
+ mkdirSync(publicDir, { recursive: true });
+ writeFileSync(
+ join(publicDir, 'index.html'),
+ '\n\n
Arboretum\n' +
+ 'Arboretum server is running, but this package was built without the web UI.
\n\n',
+ );
+ console.log('copy-web: ARBORETUM_PACK_NO_WEB=1 — wrote placeholder public/index.html');
+ process.exit(0);
+ }
+ console.error(`copy-web: ${webDist} not found — run npm run build -w @arboretum/web first`);
+ process.exit(1);
+}
+
+rmSync(publicDir, { recursive: true, force: true });
+cpSync(webDist, publicDir, { recursive: true });
+console.log(`copy-web: copied ${webDist} -> ${publicDir}`);
diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts
index bc9deb3..4d5fc18 100644
--- a/packages/server/src/app.ts
+++ b/packages/server/src/app.ts
@@ -73,7 +73,11 @@ export function buildApp(config: Config, db: Db, serverVersion: string): AppBund
registerAuthRoutes(app, auth, limiter, serverVersion);
registerSessionRoutes(app, manager);
- registerWsGateway(app, manager, serverVersion);
+ // La route websocket doit être déclarée APRÈS le chargement du plugin (contexte
+ // encapsulé) — sinon le handler reçoit la signature REST (request, reply).
+ void app.register(async (scoped) => {
+ registerWsGateway(scoped, manager, serverVersion);
+ });
// SPA buildée embarquée dans le paquet npm (public/) — absente en dev (vite dev sert le front)
const publicDir = join(dirname(fileURLToPath(import.meta.url)), '..', 'public');
diff --git a/packages/server/src/auth/service.ts b/packages/server/src/auth/service.ts
index dfc2d07..197c030 100644
--- a/packages/server/src/auth/service.ts
+++ b/packages/server/src/auth/service.ts
@@ -69,7 +69,9 @@ export class AuthService {
const expected = this.sign(payload);
if (sig.length !== expected.length || !timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) return null;
const [tokenId, expiresStr] = payload.split('.');
- if (!tokenId || !expiresStr || Number(expiresStr) < Date.now()) return null;
+ if (!tokenId || !expiresStr) return null;
+ const expires = Number(expiresStr);
+ if (!Number.isFinite(expires) || expires < Date.now()) return null;
const row = this.db
.prepare('SELECT id, label FROM auth_tokens WHERE id = ? AND revoked_at IS NULL')
.get(tokenId) as { id: string; label: string } | undefined;
diff --git a/packages/server/src/core/pty-manager.ts b/packages/server/src/core/pty-manager.ts
index 806dfec..5631782 100644
--- a/packages/server/src/core/pty-manager.ts
+++ b/packages/server/src/core/pty-manager.ts
@@ -244,14 +244,13 @@ export class PtyManager extends EventEmitter {
private handleExit(s: ManagedSession, exitCode: number | null, signal: number | null): void {
s.exited = { exitCode, signal };
if (s.killTimer) clearTimeout(s.killTimer);
- this.db
- .prepare('UPDATE sessions SET ended_at = ?, exit_code = ? WHERE id = ?')
- .run(new Date().toISOString(), exitCode, s.id);
+ const endedAt = new Date().toISOString();
+ this.db.prepare('UPDATE sessions SET ended_at = ?, exit_code = ? WHERE id = ?').run(endedAt, exitCode, s.id);
for (const c of s.clients) c.onDetached('session_exit');
s.clients.clear();
this.live.delete(s.id);
this.emit('session_exit', { sessionId: s.id, exitCode, signal });
- this.emit('session_update', { ...this.summarize(s), live: false, status: 'exited' });
+ this.emit('session_update', { ...this.summarize(s), live: false, status: 'exited', endedAt });
}
private summarize(s: ManagedSession): SessionSummary {
diff --git a/packages/server/src/core/ring-buffer.ts b/packages/server/src/core/ring-buffer.ts
index dec462b..0e8a6a6 100644
--- a/packages/server/src/core/ring-buffer.ts
+++ b/packages/server/src/core/ring-buffer.ts
@@ -22,20 +22,23 @@ export class RingBuffer {
}
write(chunk: Buffer): void {
+ // Chunk plus grand que la fenêtre : on ne stocke que la queue, mais en comptant
+ // les octets sautés dans l'offset pour préserver l'invariant « l'octet k du flux
+ // est à la position k % capacity » (sinon tail/readFrom rendent la fenêtre désordonnée).
if (chunk.length >= this.capacity) {
- chunk.copy(this.buf, 0, chunk.length - this.capacity);
- this.length = this.capacity;
- } else {
- const writePos = this.total % this.capacity;
- const tailSpace = this.capacity - writePos;
- if (chunk.length <= tailSpace) {
- chunk.copy(this.buf, writePos);
- } else {
- chunk.copy(this.buf, writePos, 0, tailSpace);
- chunk.copy(this.buf, 0, tailSpace);
- }
- this.length = Math.min(this.capacity, this.length + chunk.length);
+ const skipped = chunk.length - this.capacity;
+ this.total += skipped;
+ chunk = chunk.subarray(skipped);
}
+ const writePos = this.total % this.capacity;
+ const tailSpace = this.capacity - writePos;
+ if (chunk.length <= tailSpace) {
+ chunk.copy(this.buf, writePos);
+ } else {
+ chunk.copy(this.buf, writePos, 0, tailSpace);
+ chunk.copy(this.buf, 0, tailSpace);
+ }
+ this.length = Math.min(this.capacity, this.length + chunk.length);
this.total += chunk.length;
}
diff --git a/packages/server/test/app.e2e.test.ts b/packages/server/test/app.e2e.test.ts
new file mode 100644
index 0000000..8767026
--- /dev/null
+++ b/packages/server/test/app.e2e.test.ts
@@ -0,0 +1,270 @@
+import { mkdtempSync, rmSync } from 'node:fs';
+import { tmpdir } from 'node:os';
+import { join } from 'node:path';
+import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
+import { buildApp, type AppBundle } from '../src/app.js';
+import { openDb, type Db } from '../src/db/index.js';
+import type { Config } from '../src/config.js';
+
+// Aucun vrai PTY en CI : le module node-pty est remplacé par un faux inerte.
+vi.mock('@homebridge/node-pty-prebuilt-multiarch', () => {
+ class FakePty {
+ pid = 424242;
+ write = vi.fn();
+ resize = vi.fn();
+ pause = vi.fn();
+ resume = vi.fn();
+ kill = vi.fn();
+ onData(_cb: (d: string) => void): { dispose: () => void } {
+ return { dispose: () => {} };
+ }
+ onExit(_cb: (e: { exitCode: number; signal?: number }) => void): { dispose: () => void } {
+ return { dispose: () => {} };
+ }
+ }
+ return { default: { spawn: (): FakePty => new FakePty() } };
+});
+
+process.env.ARBORETUM_LOG = 'silent';
+
+const PORT = 7317;
+const ALLOWED_ORIGIN = 'https://arboretum.test-tailnet.example';
+
+interface TestApp {
+ bundle: AppBundle;
+ db: Db;
+ token: string;
+}
+
+let dir: string;
+const apps: TestApp[] = [];
+
+function makeApp(name: string): TestApp {
+ const dbPath = join(dir, `${name}.db`);
+ const db = openDb(dbPath);
+ const config: Config = {
+ port: PORT,
+ bind: '127.0.0.1',
+ dbPath,
+ dataDir: dir,
+ allowedOrigins: [ALLOWED_ORIGIN],
+ printToken: false,
+ };
+ const bundle = buildApp(config, db, '0.0.0-test');
+ const token = bundle.auth.ensureBootstrapToken();
+ if (!token) throw new Error('bootstrap token attendu sur une base vierge');
+ const app: TestApp = { bundle, db, token };
+ apps.push(app);
+ return app;
+}
+
+beforeAll(() => {
+ dir = mkdtempSync(join(tmpdir(), 'arboretum-e2e-'));
+});
+
+afterAll(async () => {
+ for (const a of apps) await a.bundle.app.close();
+ for (const a of apps) a.db.close();
+ rmSync(dir, { recursive: true, force: true });
+});
+
+describe('app e2e — auth, origin et sessions', () => {
+ let t: TestApp;
+ let cookieValue: string;
+
+ // Le rate limiter du login est GLOBAL à l'app : un seul login réussi ici,
+ // toutes les variantes d'échec vivent dans le describe rate limit (app dédiée).
+ beforeAll(async () => {
+ t = makeApp('main');
+ const res = await t.bundle.app.inject({ method: 'POST', url: '/api/v1/auth/login', payload: { token: t.token } });
+ expect(res.statusCode).toBe(200);
+ expect(res.json()).toEqual({ ok: true, label: 'initial' });
+ const cookie = res.cookies.find((c) => c.name === 'arb_session');
+ expect(cookie).toBeDefined();
+ expect(cookie?.httpOnly).toBe(true);
+ expect(cookie?.sameSite).toBe('Strict');
+ cookieValue = cookie!.value;
+ });
+
+ it('routes API sans auth → 401', async () => {
+ for (const url of ['/api/v1/sessions', '/api/v1/auth/me']) {
+ const res = await t.bundle.app.inject({ method: 'GET', url });
+ expect(res.statusCode).toBe(401);
+ expect(res.json()).toMatchObject({ error: { code: 'UNAUTHORIZED' } });
+ }
+ const post = await t.bundle.app.inject({ method: 'POST', url: '/api/v1/sessions', payload: { cwd: tmpdir() } });
+ expect(post.statusCode).toBe(401);
+ });
+
+ it('cookie émis au login → /me identifié', async () => {
+ const me = await t.bundle.app.inject({
+ method: 'GET',
+ url: '/api/v1/auth/me',
+ cookies: { arb_session: cookieValue },
+ });
+ expect(me.statusCode).toBe(200);
+ expect(me.json()).toEqual({ ok: true, tokenLabel: 'initial', serverVersion: '0.0.0-test' });
+ });
+
+ it('cookie altéré → 401', async () => {
+ const tampered = cookieValue.slice(0, -1) + (cookieValue.endsWith('a') ? 'b' : 'a');
+ const me = await t.bundle.app.inject({ method: 'GET', url: '/api/v1/auth/me', cookies: { arb_session: tampered } });
+ expect(me.statusCode).toBe(401);
+ });
+
+ it('Bearer token accepté sur les routes API', async () => {
+ const res = await t.bundle.app.inject({
+ method: 'GET',
+ url: '/api/v1/sessions',
+ headers: { authorization: `Bearer ${t.token}` },
+ });
+ expect(res.statusCode).toBe(200);
+ expect(res.json()).toEqual({ sessions: [] });
+ });
+
+ it('Origin interdite → 403 même avec un Bearer valide, et même sur la route publique de login', async () => {
+ const res = await t.bundle.app.inject({
+ method: 'GET',
+ url: '/api/v1/sessions',
+ headers: { authorization: `Bearer ${t.token}`, origin: 'https://evil.example' },
+ });
+ expect(res.statusCode).toBe(403);
+ expect(res.json()).toMatchObject({ error: { code: 'BAD_ORIGIN' } });
+
+ const login = await t.bundle.app.inject({
+ method: 'POST',
+ url: '/api/v1/auth/login',
+ headers: { origin: 'https://evil.example' },
+ payload: { token: t.token },
+ });
+ expect(login.statusCode).toBe(403);
+ });
+
+ it('Origins autorisées : loopback du port configuré et --allow-origin', async () => {
+ for (const origin of [`http://127.0.0.1:${PORT}`, `http://localhost:${PORT}`, ALLOWED_ORIGIN]) {
+ const res = await t.bundle.app.inject({
+ method: 'GET',
+ url: '/api/v1/sessions',
+ headers: { authorization: `Bearer ${t.token}`, origin },
+ });
+ expect(res.statusCode, `origin ${origin}`).toBe(200);
+ }
+ });
+
+ it('POST /sessions : cwd relatif → 400', async () => {
+ const res = await t.bundle.app.inject({
+ method: 'POST',
+ url: '/api/v1/sessions',
+ headers: { authorization: `Bearer ${t.token}` },
+ payload: { cwd: 'relative/path' },
+ });
+ expect(res.statusCode).toBe(400);
+ expect(res.json()).toMatchObject({ error: { code: 'BAD_REQUEST' } });
+ });
+
+ it('POST /sessions : cwd absent ou non-string → 400', async () => {
+ const res1 = await t.bundle.app.inject({
+ method: 'POST',
+ url: '/api/v1/sessions',
+ headers: { authorization: `Bearer ${t.token}` },
+ payload: {},
+ });
+ expect(res1.statusCode).toBe(400);
+ const res2 = await t.bundle.app.inject({
+ method: 'POST',
+ url: '/api/v1/sessions',
+ headers: { authorization: `Bearer ${t.token}` },
+ payload: { cwd: 42 },
+ });
+ expect(res2.statusCode).toBe(400);
+ });
+
+ it('POST /sessions : cwd inexistant → 400 SPAWN_FAILED', async () => {
+ const res = await t.bundle.app.inject({
+ method: 'POST',
+ url: '/api/v1/sessions',
+ headers: { authorization: `Bearer ${t.token}` },
+ payload: { cwd: '/definitely/not/a/dir' },
+ });
+ expect(res.statusCode).toBe(400);
+ expect(res.json()).toMatchObject({ error: { code: 'SPAWN_FAILED' } });
+ });
+
+ it('POST /sessions : command inconnue → 400', async () => {
+ const res = await t.bundle.app.inject({
+ method: 'POST',
+ url: '/api/v1/sessions',
+ headers: { authorization: `Bearer ${t.token}` },
+ payload: { cwd: tmpdir(), command: 'zsh' },
+ });
+ expect(res.statusCode).toBe(400);
+ });
+
+ it('POST /sessions valide (bash, pty mocké) → 201 puis listée', async () => {
+ const res = await t.bundle.app.inject({
+ method: 'POST',
+ url: '/api/v1/sessions',
+ headers: { authorization: `Bearer ${t.token}` },
+ payload: { cwd: tmpdir(), command: 'bash' },
+ });
+ expect(res.statusCode).toBe(201);
+ const { session } = res.json() as { session: { id: string; cwd: string; command: string; live: boolean; status: string } };
+ expect(session).toMatchObject({ cwd: tmpdir(), command: 'bash', live: true, status: 'running' });
+
+ const list = await t.bundle.app.inject({
+ method: 'GET',
+ url: '/api/v1/sessions',
+ headers: { authorization: `Bearer ${t.token}` },
+ });
+ const sessions = (list.json() as { sessions: Array<{ id: string }> }).sessions;
+ expect(sessions.some((s) => s.id === session.id)).toBe(true);
+ });
+
+ it('DELETE /sessions/:id inconnu → 404', async () => {
+ const res = await t.bundle.app.inject({
+ method: 'DELETE',
+ url: '/api/v1/sessions/unknown-id',
+ headers: { authorization: `Bearer ${t.token}` },
+ });
+ expect(res.statusCode).toBe(404);
+ });
+
+ it('logout → cookie effacé', async () => {
+ const res = await t.bundle.app.inject({ method: 'POST', url: '/api/v1/auth/logout', cookies: { arb_session: cookieValue } });
+ expect(res.statusCode).toBe(200);
+ const cleared = res.cookies.find((c) => c.name === 'arb_session');
+ expect(cleared?.value).toBe('');
+ });
+});
+
+describe('app e2e — rate limit du login', () => {
+ it('mauvais token et bodies invalides → 401, puis 429 dès le 4e essai (backoff après 3 échecs consécutifs)', async () => {
+ const t = makeApp('ratelimit-failures');
+ const bad = await t.bundle.app.inject({ method: 'POST', url: '/api/v1/auth/login', payload: { token: 'arb_badbadbadbad' } });
+ expect(bad.statusCode).toBe(401);
+ expect(bad.json()).toMatchObject({ error: { code: 'BAD_TOKEN' } });
+ const noToken = await t.bundle.app.inject({ method: 'POST', url: '/api/v1/auth/login', payload: {} });
+ expect(noToken.statusCode).toBe(401);
+ const numToken = await t.bundle.app.inject({ method: 'POST', url: '/api/v1/auth/login', payload: { token: 42 } });
+ expect(numToken.statusCode).toBe(401);
+
+ // 3 échecs consécutifs → verrou exponentiel : le backoff bloque AVANT la fenêtre 5/min
+ const blocked = await t.bundle.app.inject({ method: 'POST', url: '/api/v1/auth/login', payload: { token: 'arb_badbadbadbad' } });
+ expect(blocked.statusCode).toBe(429);
+ expect(blocked.json()).toMatchObject({ error: { code: 'RATE_LIMITED' } });
+ // même un login avec le BON token est bloqué pendant le backoff
+ const goodButLocked = await t.bundle.app.inject({ method: 'POST', url: '/api/v1/auth/login', payload: { token: t.token } });
+ expect(goodButLocked.statusCode).toBe(429);
+ });
+
+ it('limite 5 tentatives/minute : la 6e → 429 même avec le bon token', async () => {
+ const t = makeApp('ratelimit-window');
+ for (let i = 0; i < 5; i++) {
+ const res = await t.bundle.app.inject({ method: 'POST', url: '/api/v1/auth/login', payload: { token: t.token } });
+ expect(res.statusCode, `tentative ${i + 1}`).toBe(200);
+ }
+ const blocked = await t.bundle.app.inject({ method: 'POST', url: '/api/v1/auth/login', payload: { token: t.token } });
+ expect(blocked.statusCode).toBe(429);
+ expect(blocked.json()).toMatchObject({ error: { code: 'RATE_LIMITED' } });
+ });
+});
diff --git a/packages/server/test/auth.test.ts b/packages/server/test/auth.test.ts
new file mode 100644
index 0000000..b6b4996
--- /dev/null
+++ b/packages/server/test/auth.test.ts
@@ -0,0 +1,208 @@
+import { createHmac } from 'node:crypto';
+import { beforeEach, describe, expect, it } from 'vitest';
+import { AuthService, LoginRateLimiter, type AuthContext } from '../src/auth/service.js';
+import { getSetting, openDb, type Db } from '../src/db/index.js';
+
+describe('AuthService', () => {
+ let db: Db;
+ let auth: AuthService;
+
+ beforeEach(() => {
+ db = openDb(':memory:');
+ auth = new AuthService(db);
+ });
+
+ describe('bootstrap token', () => {
+ it('créé une seule fois : le second appel renvoie null', () => {
+ const token = auth.ensureBootstrapToken();
+ expect(token).toMatch(/^arb_[0-9a-f]{48}$/);
+ expect(auth.ensureBootstrapToken()).toBeNull();
+ });
+
+ it('recréé si tous les tokens sont révoqués', () => {
+ const first = auth.ensureBootstrapToken();
+ expect(first).not.toBeNull();
+ db.prepare('UPDATE auth_tokens SET revoked_at = ?').run(new Date().toISOString());
+ const second = auth.ensureBootstrapToken();
+ expect(second).not.toBeNull();
+ expect(second).not.toBe(first);
+ });
+ });
+
+ describe('verifyRawToken', () => {
+ it('token valide → contexte avec label', () => {
+ const raw = auth.ensureBootstrapToken();
+ const ctx = auth.verifyRawToken(raw as string);
+ expect(ctx).not.toBeNull();
+ expect(ctx?.label).toBe('initial');
+ expect(typeof ctx?.tokenId).toBe('string');
+ });
+
+ it('met à jour last_used_at au passage', () => {
+ const raw = auth.ensureBootstrapToken() as string;
+ const ctx = auth.verifyRawToken(raw) as AuthContext;
+ const row = db.prepare('SELECT last_used_at FROM auth_tokens WHERE id = ?').get(ctx.tokenId) as { last_used_at: string | null };
+ expect(row.last_used_at).not.toBeNull();
+ });
+
+ it('token inconnu, trop court ou trop long → null', () => {
+ auth.ensureBootstrapToken();
+ expect(auth.verifyRawToken('arb_' + 'f'.repeat(48))).toBeNull(); // bon format, mauvaise valeur
+ expect(auth.verifyRawToken('short')).toBeNull(); // < 8 chars
+ expect(auth.verifyRawToken('a'.repeat(129))).toBeNull(); // > 128 chars
+ expect(auth.verifyRawToken('')).toBeNull();
+ });
+
+ it('token révoqué → null', () => {
+ const raw = auth.ensureBootstrapToken() as string;
+ expect(auth.verifyRawToken(raw)).not.toBeNull();
+ db.prepare('UPDATE auth_tokens SET revoked_at = ?').run(new Date().toISOString());
+ expect(auth.verifyRawToken(raw)).toBeNull();
+ });
+
+ it('createToken : deux tokens coexistent avec leurs labels', () => {
+ auth.ensureBootstrapToken();
+ const raw2 = auth.createToken('laptop');
+ expect(auth.verifyRawToken(raw2)?.label).toBe('laptop');
+ });
+ });
+
+ describe('cookies', () => {
+ let ctx: AuthContext;
+
+ beforeEach(() => {
+ const raw = auth.ensureBootstrapToken() as string;
+ ctx = auth.verifyRawToken(raw) as AuthContext;
+ });
+
+ // Signe un payload arbitraire avec le vrai secret du serveur (lu en base),
+ // pour forger des cookies à la structure valide mais au contenu choisi.
+ function forge(payload: string): string {
+ const secretHex = getSetting(db, 'server_secret') as string;
+ const sig = createHmac('sha256', Buffer.from(secretHex, 'hex')).update(payload).digest('hex');
+ return `${payload}.${sig}`;
+ }
+
+ it('cookie émis puis vérifié → même tokenId', () => {
+ const cookie = auth.issueCookie(ctx);
+ const back = auth.verifyCookie(cookie);
+ expect(back).toEqual({ tokenId: ctx.tokenId, label: 'initial' });
+ });
+
+ it('le secret persiste : un second AuthService sur la même base vérifie le cookie', () => {
+ const cookie = auth.issueCookie(ctx);
+ const auth2 = new AuthService(db);
+ expect(auth2.verifyCookie(cookie)?.tokenId).toBe(ctx.tokenId);
+ });
+
+ it('cookie altéré (signature ou payload) → null', () => {
+ const cookie = auth.issueCookie(ctx);
+ // un caractère de la signature inversé
+ const last = cookie.slice(-1);
+ const tamperedSig = cookie.slice(0, -1) + (last === 'a' ? 'b' : 'a');
+ expect(auth.verifyCookie(tamperedSig)).toBeNull();
+ // expiration repoussée dans le payload sans re-signer
+ const lastDot = cookie.lastIndexOf('.');
+ const [tokenId, expires] = cookie.slice(0, lastDot).split('.') as [string, string];
+ const inflated = `${tokenId}.${Number(expires) + 1_000_000}.${cookie.slice(lastDot + 1)}`;
+ expect(auth.verifyCookie(inflated)).toBeNull();
+ });
+
+ it('cookie malformé ou absent → null', () => {
+ expect(auth.verifyCookie(undefined)).toBeNull();
+ expect(auth.verifyCookie('')).toBeNull();
+ expect(auth.verifyCookie('garbage')).toBeNull();
+ expect(auth.verifyCookie('.sig')).toBeNull();
+ expect(auth.verifyCookie('a.b.c.d')).toBeNull();
+ });
+
+ it('cookie expiré forgé avec le vrai secret → null (la forge non expirée passe, preuve que seule l’expiration rejette)', () => {
+ const fresh = forge(`${ctx.tokenId}.${Date.now() + 10_000}`);
+ expect(auth.verifyCookie(fresh)?.tokenId).toBe(ctx.tokenId);
+ const expired = forge(`${ctx.tokenId}.${Date.now() - 1}`);
+ expect(auth.verifyCookie(expired)).toBeNull();
+ });
+
+ it('cookie forgé avec tokenId vide → null', () => {
+ expect(auth.verifyCookie(forge(`.${Date.now() + 10_000}`))).toBeNull();
+ });
+
+ it('cookie forgé avec expiration non numérique → null', () => {
+ expect(auth.verifyCookie(forge(`${ctx.tokenId}.notanumber`))).toBeNull();
+ });
+
+ it('cookie valide mais token révoqué entre-temps → null', () => {
+ const cookie = auth.issueCookie(ctx);
+ db.prepare('UPDATE auth_tokens SET revoked_at = ? WHERE id = ?').run(new Date().toISOString(), ctx.tokenId);
+ expect(auth.verifyCookie(cookie)).toBeNull();
+ });
+
+ it('cookie signé pour un tokenId inexistant → null', () => {
+ const cookie = forge(`00000000-0000-0000-0000-000000000000.${Date.now() + 10_000}`);
+ expect(auth.verifyCookie(cookie)).toBeNull();
+ });
+ });
+});
+
+describe('LoginRateLimiter', () => {
+ it('5 tentatives par minute glissante, puis délai renvoyé', () => {
+ let t = 0;
+ const lim = new LoginRateLimiter(5, () => t);
+ for (let i = 0; i < 5; i++) expect(lim.check()).toBeNull();
+ expect(lim.check()).toBe(60_000); // toutes les tentatives à t=0
+ t = 59_999;
+ expect(lim.check()).toBe(1); // il reste 1 ms avant que la fenêtre glisse
+ t = 60_000;
+ expect(lim.check()).toBeNull(); // fenêtre vidée
+ });
+
+ it('la fenêtre glisse tentative par tentative', () => {
+ let t = 0;
+ const lim = new LoginRateLimiter(2, () => t);
+ expect(lim.check()).toBeNull(); // t=0
+ t = 30_000;
+ expect(lim.check()).toBeNull(); // t=30s
+ t = 40_000;
+ expect(lim.check()).toBe(20_000); // bloqué par la tentative t=0
+ t = 60_000;
+ expect(lim.check()).toBeNull(); // t=0 expirée, t=30s reste
+ });
+
+ it('backoff exponentiel à partir de 3 échecs consécutifs', () => {
+ let t = 0;
+ const lim = new LoginRateLimiter(1000, () => t); // plafond haut pour isoler le backoff
+ lim.recordFailure();
+ lim.recordFailure();
+ expect(lim.check()).toBeNull(); // 2 échecs : pas encore de lock
+ lim.recordFailure(); // 3e échec → 30 s
+ expect(lim.check()).toBe(30_000);
+ t = 29_999;
+ expect(lim.check()).toBe(1);
+ t = 30_000;
+ expect(lim.check()).toBeNull(); // lock levé
+ lim.recordFailure(); // 4e échec → 60 s
+ expect(lim.check()).toBe(60_000);
+ t = 90_000;
+ lim.recordFailure(); // 5e échec → 120 s
+ expect(lim.check()).toBe(120_000);
+ });
+
+ it('le backoff est plafonné à 30 minutes', () => {
+ let t = 0;
+ const lim = new LoginRateLimiter(1000, () => t);
+ for (let i = 0; i < 20; i++) lim.recordFailure();
+ expect(lim.check()).toBe(30 * 60_000);
+ });
+
+ it('reset complet au succès', () => {
+ let t = 0;
+ const lim = new LoginRateLimiter(1000, () => t);
+ for (let i = 0; i < 5; i++) lim.recordFailure();
+ expect(lim.check()).not.toBeNull(); // verrouillé
+ lim.recordSuccess();
+ expect(lim.check()).toBeNull(); // déverrouillé immédiatement
+ lim.recordFailure();
+ lim.recordFailure();
+ expect(lim.check()).toBeNull(); // le compteur d'échecs consécutifs est bien reparti de zéro
+ });
+});
diff --git a/packages/server/test/pty-manager.test.ts b/packages/server/test/pty-manager.test.ts
new file mode 100644
index 0000000..980ad02
--- /dev/null
+++ b/packages/server/test/pty-manager.test.ts
@@ -0,0 +1,416 @@
+import { tmpdir } from 'node:os';
+import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest';
+import { FLOW, REPLAY_TAIL_BYTES, type SessionSummary } from '@arboretum/shared';
+import { PtyManager, type ClientBinding } from '../src/core/pty-manager.js';
+import { openDb, type Db } from '../src/db/index.js';
+
+interface FakePty {
+ pid: number;
+ file: string;
+ args: string[];
+ opts: { cwd: string; cols: number; rows: number };
+ write: Mock;
+ resize: Mock;
+ pause: Mock;
+ resume: Mock;
+ emitData(data: string): void;
+ emitExit(exitCode: number, signal?: number): void;
+}
+
+const ptyMock = vi.hoisted(() => ({ instances: [] as unknown[] }));
+
+vi.mock('@homebridge/node-pty-prebuilt-multiarch', () => {
+ let nextPid = 100_000;
+ class FakePtyImpl {
+ pid = nextPid++;
+ write = vi.fn();
+ resize = vi.fn();
+ pause = vi.fn();
+ resume = vi.fn();
+ kill = vi.fn();
+ private dataCbs: Array<(d: string) => void> = [];
+ private exitCbs: Array<(e: { exitCode: number; signal?: number }) => void> = [];
+ constructor(
+ readonly file: string,
+ readonly args: string[],
+ readonly opts: unknown,
+ ) {}
+ onData(cb: (d: string) => void): { dispose: () => void } {
+ this.dataCbs.push(cb);
+ return { dispose: () => {} };
+ }
+ onExit(cb: (e: { exitCode: number; signal?: number }) => void): { dispose: () => void } {
+ this.exitCbs.push(cb);
+ return { dispose: () => {} };
+ }
+ emitData(d: string): void {
+ for (const cb of this.dataCbs) cb(d);
+ }
+ emitExit(exitCode: number, signal?: number): void {
+ for (const cb of this.exitCbs) cb(signal === undefined ? { exitCode } : { exitCode, signal });
+ }
+ }
+ return {
+ default: {
+ spawn: (file: string, args: string[], opts: unknown): FakePtyImpl => {
+ const p = new FakePtyImpl(file, args, opts);
+ ptyMock.instances.push(p);
+ return p;
+ },
+ },
+ };
+});
+
+type BindingSpies = ClientBinding & {
+ sendOutput: Mock<(payload: Buffer) => void>;
+ sendResync: Mock<(payload: Buffer) => void>;
+ onDetached: Mock<(reason: 'session_exit' | 'replaced') => void>;
+ onControlChanged: Mock<(controlling: boolean) => void>;
+};
+
+let channelSeq = 1;
+function makeBinding(mode: 'interactive' | 'observer'): BindingSpies {
+ return {
+ channel: channelSeq++,
+ mode,
+ controlling: false,
+ sentBytes: 0,
+ ackedBytes: 0,
+ lagging: false,
+ sendOutput: vi.fn(),
+ sendResync: vi.fn(),
+ onDetached: vi.fn(),
+ onControlChanged: vi.fn(),
+ };
+}
+
+function lastPty(): FakePty {
+ return ptyMock.instances.at(-1) as FakePty;
+}
+
+describe('PtyManager (pty mocké)', () => {
+ let db: Db;
+ let manager: PtyManager;
+ const cwd = tmpdir();
+
+ beforeEach(() => {
+ ptyMock.instances.length = 0;
+ db = openDb(':memory:');
+ manager = new PtyManager(db);
+ });
+
+ function spawnBash(): { summary: SessionSummary; pty: FakePty } {
+ const summary = manager.spawn({ cwd, command: 'bash' });
+ return { summary, pty: lastPty() };
+ }
+
+ describe('spawn', () => {
+ it('émet session_update, insère en base et lance le pty avec le bon spec', () => {
+ const updates: SessionSummary[] = [];
+ manager.on('session_update', (s) => updates.push(s));
+ const { summary, pty } = spawnBash();
+
+ expect(updates).toHaveLength(1);
+ expect(updates[0]).toEqual(summary);
+ expect(summary.status).toBe('running');
+ expect(summary.live).toBe(true);
+ expect(summary.clients).toBe(0);
+ expect(summary.command).toBe('bash');
+
+ expect(pty.file).toBe('bash');
+ expect(pty.args).toEqual(['--norc']);
+ expect(pty.opts.cwd).toBe(cwd);
+ expect(pty.opts.cols).toBe(120);
+ expect(pty.opts.rows).toBe(32);
+
+ const row = db.prepare('SELECT cwd, command FROM sessions WHERE id = ?').get(summary.id) as { cwd: string; command: string };
+ expect(row).toEqual({ cwd, command: 'bash' });
+ expect(manager.get(summary.id)?.id).toBe(summary.id);
+ });
+
+ it('cwd inexistant → erreur statusCode 400, rien en base', () => {
+ expect(() => manager.spawn({ cwd: '/definitely/not/a/dir', command: 'bash' })).toThrowError(
+ expect.objectContaining({ statusCode: 400 }),
+ );
+ const row = db.prepare('SELECT COUNT(*) AS n FROM sessions').get() as { n: number };
+ expect(row.n).toBe(0);
+ });
+ });
+
+ describe('attach', () => {
+ it('session inconnue → NOT_FOUND', () => {
+ const res = manager.attach('nope', makeBinding('interactive'), 80, 24);
+ expect(res).toEqual({ ok: false, code: 'NOT_FOUND' });
+ });
+
+ it('resync à l’attach = queue du ring (REPLAY_TAIL_BYTES max)', () => {
+ const { summary, pty } = spawnBash();
+ const chunks = ['A', 'B', 'C'].map((c) => c.repeat(100 * 1024));
+ for (const c of chunks) pty.emitData(c);
+
+ const b = makeBinding('interactive');
+ const res = manager.attach(summary.id, b, 80, 24);
+ expect(res).toEqual({ ok: true, controlling: true });
+
+ expect(b.sendResync).toHaveBeenCalledTimes(1);
+ const payload = b.sendResync.mock.calls[0]![0]!;
+ const full = Buffer.from(chunks.join(''), 'ascii');
+ expect(payload.length).toBe(REPLAY_TAIL_BYTES);
+ expect(payload.equals(full.subarray(full.length - REPLAY_TAIL_BYTES))).toBe(true);
+ // compteurs de flow control remis à zéro après le resync initial
+ expect(b.sentBytes).toBe(0);
+ expect(b.ackedBytes).toBe(0);
+ });
+
+ it('ring vide → resync avec payload vide', () => {
+ const { summary } = spawnBash();
+ const b = makeBinding('interactive');
+ manager.attach(summary.id, b, 80, 24);
+ expect(b.sendResync).toHaveBeenCalledTimes(1);
+ expect(b.sendResync.mock.calls[0]![0]!.length).toBe(0);
+ });
+
+ it('contrôle au premier interactif seulement, les observers ne comptent pas', () => {
+ const { summary, pty } = spawnBash();
+ const obs = makeBinding('observer');
+ const a = makeBinding('interactive');
+ const b = makeBinding('interactive');
+
+ expect(manager.attach(summary.id, obs, 80, 24)).toEqual({ ok: true, controlling: false });
+ expect(pty.resize).not.toHaveBeenCalled(); // un observer ne redimensionne pas
+
+ expect(manager.attach(summary.id, a, 100, 30)).toEqual({ ok: true, controlling: true });
+ expect(pty.resize).toHaveBeenCalledWith(100, 30); // le contrôleur impose sa taille
+
+ expect(manager.attach(summary.id, b, 200, 50)).toEqual({ ok: true, controlling: false });
+ expect(pty.resize).toHaveBeenCalledTimes(1); // pas de resize pour le non-contrôleur
+ expect(manager.get(summary.id)?.clients).toBe(3);
+ });
+ });
+
+ describe('write / resize', () => {
+ it('observer read-only : write refusé', () => {
+ const { summary, pty } = spawnBash();
+ const obs = makeBinding('observer');
+ manager.attach(summary.id, obs, 80, 24);
+ expect(manager.write(summary.id, obs, 'ls\r')).toBe('not_controlling');
+ expect(pty.write).not.toHaveBeenCalled();
+ });
+
+ it('tout interactif peut écrire (même non contrôleur), session inconnue → gone', () => {
+ const { summary, pty } = spawnBash();
+ const a = makeBinding('interactive');
+ const b = makeBinding('interactive');
+ manager.attach(summary.id, a, 80, 24);
+ manager.attach(summary.id, b, 80, 24);
+ expect(manager.write(summary.id, a, 'echo a\r')).toBe('ok');
+ expect(manager.write(summary.id, b, 'echo b\r')).toBe('ok');
+ expect(pty.write).toHaveBeenCalledTimes(2);
+ expect(manager.write('nope', a, 'x')).toBe('gone');
+ });
+
+ it('resize ignoré pour les non-contrôleurs', () => {
+ const { summary, pty } = spawnBash();
+ const a = makeBinding('interactive');
+ const b = makeBinding('interactive');
+ manager.attach(summary.id, a, 80, 24);
+ manager.attach(summary.id, b, 80, 24);
+ pty.resize.mockClear();
+ manager.resize(summary.id, b, 50, 20);
+ expect(pty.resize).not.toHaveBeenCalled();
+ manager.resize(summary.id, a, 90, 28);
+ expect(pty.resize).toHaveBeenCalledWith(90, 28);
+ });
+ });
+
+ describe('flow control', () => {
+ it('pause() UNIQUEMENT quand tous les interactifs dépassent HIGH ; resume() quand le min repasse sous LOW', () => {
+ const { summary, pty } = spawnBash();
+ const a = makeBinding('interactive');
+ const b = makeBinding('interactive');
+ manager.attach(summary.id, a, 80, 24);
+ manager.attach(summary.id, b, 80, 24);
+
+ const chunk = 'x'.repeat(200 * 1024); // 200 Kio ASCII
+ pty.emitData(chunk); // a=200K, b=200K — sous HIGH (384K)
+ expect(pty.pause).not.toHaveBeenCalled();
+
+ manager.ack(summary.id, b, 200 * 1024); // b rattrape tout
+ pty.emitData(chunk); // a=400K > HIGH, b=200K < HIGH
+ expect(pty.pause).not.toHaveBeenCalled(); // un seul interactif au-dessus ne suffit pas
+
+ pty.emitData(chunk); // a=600K, b=400K → TOUS au-dessus de HIGH
+ expect(a.sentBytes - a.ackedBytes).toBeGreaterThan(FLOW.HIGH_WATERMARK);
+ expect(b.sentBytes - b.ackedBytes).toBeGreaterThan(FLOW.HIGH_WATERMARK);
+ expect(pty.pause).toHaveBeenCalledTimes(1);
+ expect(pty.resume).not.toHaveBeenCalled();
+
+ manager.ack(summary.id, b, 600 * 1024); // outstanding b = 0 < LOW
+ expect(pty.resume).toHaveBeenCalledTimes(1);
+ expect(pty.pause).toHaveBeenCalledTimes(1); // pas de re-pause intempestive
+ });
+
+ it('les observers ne freinent jamais le PTY', () => {
+ const { summary, pty } = spawnBash();
+ const a = makeBinding('interactive');
+ const obs = makeBinding('observer');
+ manager.attach(summary.id, a, 80, 24);
+ manager.attach(summary.id, obs, 80, 24);
+
+ const chunk = 'y'.repeat(200 * 1024);
+ for (let i = 0; i < 3; i++) {
+ pty.emitData(chunk);
+ manager.ack(summary.id, a, a.sentBytes); // l'interactif suit le rythme
+ }
+ expect(obs.sentBytes - obs.ackedBytes).toBeGreaterThan(FLOW.HIGH_WATERMARK);
+ expect(pty.pause).not.toHaveBeenCalled();
+ });
+
+ it('session sans interactif (observers seuls) : jamais de pause', () => {
+ const { summary, pty } = spawnBash();
+ const obs = makeBinding('observer');
+ manager.attach(summary.id, obs, 80, 24);
+ pty.emitData('z'.repeat(500 * 1024));
+ expect(pty.pause).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('client lagging', () => {
+ it('> LAGGING_BYTES sans ACK → flux coupé, puis resync au rattrapage', () => {
+ const { summary, pty } = spawnBash();
+ const a = makeBinding('interactive');
+ manager.attach(summary.id, a, 80, 24);
+
+ const mib = 1024 * 1024;
+ const chunks = ['A', 'B', 'C', 'D'].map((c) => c.repeat(mib));
+
+ pty.emitData(chunks[0]!); // outstanding 1 Mio
+ pty.emitData(chunks[1]!); // outstanding 2 Mio — pas strictement > LAGGING_BYTES
+ expect(a.lagging).toBe(false);
+ expect(a.sendOutput).toHaveBeenCalledTimes(2);
+
+ pty.emitData(chunks[2]!); // outstanding 3 Mio > 2 Mio → lagging
+ expect(a.lagging).toBe(true);
+ expect(a.sendOutput).toHaveBeenCalledTimes(3); // ce chunk est parti avant le marquage
+ // le seul interactif est lagging : le PTY ne doit pas rester en pause
+ expect(pty.resume).toHaveBeenCalled();
+
+ pty.emitData(chunks[3]!); // flux coupé pour le client lagging
+ expect(a.sendOutput).toHaveBeenCalledTimes(3);
+
+ manager.ack(summary.id, a, 3 * mib); // rattrapage : outstanding 0 < LOW
+ expect(a.lagging).toBe(false);
+ expect(a.sendResync).toHaveBeenCalledTimes(2); // attach + rattrapage
+ const payload = a.sendResync.mock.calls[1]![0]!;
+ const full = Buffer.from(chunks.join(''), 'ascii');
+ expect(payload.equals(full.subarray(full.length - REPLAY_TAIL_BYTES))).toBe(true);
+ expect(a.sentBytes).toBe(0);
+ expect(a.ackedBytes).toBe(0);
+
+ // le flux reprend normalement après le resync
+ pty.emitData('tail');
+ expect(a.sendOutput).toHaveBeenCalledTimes(4);
+ expect(a.sendOutput.mock.calls[3]![0]!.toString('ascii')).toBe('tail');
+ });
+ });
+
+ describe('detach et transfert de contrôle', () => {
+ it('detach du contrôleur → transfert au prochain interactif (onControlChanged)', () => {
+ const { summary } = spawnBash();
+ const a = makeBinding('interactive');
+ const b = makeBinding('interactive');
+ const obs = makeBinding('observer');
+ manager.attach(summary.id, a, 80, 24);
+ manager.attach(summary.id, obs, 80, 24);
+ manager.attach(summary.id, b, 80, 24);
+ expect(a.controlling).toBe(true);
+ expect(b.controlling).toBe(false);
+
+ manager.detach(summary.id, a);
+ expect(b.controlling).toBe(true);
+ expect(b.onControlChanged).toHaveBeenCalledExactlyOnceWith(true);
+ expect(obs.onControlChanged).not.toHaveBeenCalled();
+ expect(manager.get(summary.id)?.clients).toBe(2);
+ });
+
+ it('detach d’un non-contrôleur : aucun transfert', () => {
+ const { summary } = spawnBash();
+ const a = makeBinding('interactive');
+ const b = makeBinding('interactive');
+ manager.attach(summary.id, a, 80, 24);
+ manager.attach(summary.id, b, 80, 24);
+ manager.detach(summary.id, b);
+ expect(a.controlling).toBe(true);
+ expect(a.onControlChanged).not.toHaveBeenCalled();
+ });
+
+ it('detach du dernier interactif : un observer ne récupère jamais le contrôle', () => {
+ const { summary } = spawnBash();
+ const a = makeBinding('interactive');
+ const obs = makeBinding('observer');
+ manager.attach(summary.id, a, 80, 24);
+ manager.attach(summary.id, obs, 80, 24);
+ manager.detach(summary.id, a);
+ expect(obs.controlling).toBe(false);
+ expect(obs.onControlChanged).not.toHaveBeenCalled();
+ });
+
+ it('un nouvel interactif après detach du contrôleur reprend le contrôle', () => {
+ const { summary } = spawnBash();
+ const a = makeBinding('interactive');
+ manager.attach(summary.id, a, 80, 24);
+ manager.detach(summary.id, a);
+ const b = makeBinding('interactive');
+ expect(manager.attach(summary.id, b, 80, 24)).toEqual({ ok: true, controlling: true });
+ });
+ });
+
+ describe('exit', () => {
+ it('onDetached(session_exit) pour tous, DB mise à jour, événements émis', () => {
+ const { summary, pty } = spawnBash();
+ const a = makeBinding('interactive');
+ const obs = makeBinding('observer');
+ manager.attach(summary.id, a, 80, 24);
+ manager.attach(summary.id, obs, 80, 24);
+
+ const exits: Array<{ sessionId: string; exitCode: number | null; signal: number | null }> = [];
+ const updates: SessionSummary[] = [];
+ manager.on('session_exit', (e) => exits.push(e));
+ manager.on('session_update', (s) => updates.push(s));
+
+ pty.emitExit(0);
+
+ expect(a.onDetached).toHaveBeenCalledExactlyOnceWith('session_exit');
+ expect(obs.onDetached).toHaveBeenCalledExactlyOnceWith('session_exit');
+
+ expect(exits).toEqual([{ sessionId: summary.id, exitCode: 0, signal: null }]);
+ expect(updates).toHaveLength(1);
+ expect(updates[0]).toMatchObject({ id: summary.id, status: 'exited', live: false, exitCode: 0 });
+
+ const row = db.prepare('SELECT ended_at, exit_code FROM sessions WHERE id = ?').get(summary.id) as { ended_at: string | null; exit_code: number | null };
+ expect(row.ended_at).not.toBeNull();
+ expect(row.exit_code).toBe(0);
+
+ expect(manager.get(summary.id)).toBeNull();
+ const listed = manager.list().find((s) => s.id === summary.id);
+ expect(listed).toMatchObject({ status: 'exited', live: false, exitCode: 0 });
+ });
+
+ it('exit avec signal : propagé dans session_exit', () => {
+ const { summary, pty } = spawnBash();
+ const exits: Array<{ sessionId: string; exitCode: number | null; signal: number | null }> = [];
+ manager.on('session_exit', (e) => exits.push(e));
+ pty.emitExit(1, 15);
+ expect(exits).toEqual([{ sessionId: summary.id, exitCode: 1, signal: 15 }]);
+ });
+
+ it('après exit : write → gone, attach → NOT_FOUND', () => {
+ const { summary, pty } = spawnBash();
+ const a = makeBinding('interactive');
+ manager.attach(summary.id, a, 80, 24);
+ pty.emitExit(0);
+ expect(manager.write(summary.id, a, 'x')).toBe('gone');
+ expect(manager.attach(summary.id, makeBinding('interactive'), 80, 24)).toEqual({ ok: false, code: 'NOT_FOUND' });
+ });
+ });
+});
diff --git a/packages/server/test/ring-buffer.test.ts b/packages/server/test/ring-buffer.test.ts
new file mode 100644
index 0000000..69564ed
--- /dev/null
+++ b/packages/server/test/ring-buffer.test.ts
@@ -0,0 +1,247 @@
+import { describe, expect, it } from 'vitest';
+import { RingBuffer } from '../src/core/ring-buffer.js';
+
+// Modèle de référence naïf : garde tout le flux et recalcule la fenêtre à la demande
+class RefBuffer {
+ private data = Buffer.alloc(0);
+ constructor(readonly capacity: number) {}
+ write(chunk: Buffer): void {
+ this.data = Buffer.concat([this.data, chunk]);
+ }
+ get total(): number {
+ return this.data.length;
+ }
+ get windowStart(): number {
+ return Math.max(0, this.data.length - this.capacity);
+ }
+ tail(count: number): Buffer {
+ const window = this.data.subarray(this.windowStart);
+ return Buffer.from(window.subarray(Math.max(0, window.length - count)));
+ }
+ readFrom(offset: number): Buffer | null {
+ if (offset > this.total) return null;
+ if (offset === this.total) return Buffer.alloc(0);
+ if (offset < this.windowStart) return null;
+ return Buffer.from(this.data.subarray(offset));
+ }
+}
+
+function mulberry32(seed: number): () => number {
+ let a = seed >>> 0;
+ return () => {
+ a = (a + 0x6d2b79f5) >>> 0;
+ let t = a;
+ t = Math.imul(t ^ (t >>> 15), t | 1);
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
+ };
+}
+
+const buf = (s: string): Buffer => Buffer.from(s, 'ascii');
+
+describe('RingBuffer — écriture simple', () => {
+ it('écrit puis relit sans wrap', () => {
+ const ring = new RingBuffer(16);
+ ring.write(buf('hello'));
+ expect(ring.totalOffset).toBe(5);
+ expect(ring.windowStart).toBe(0);
+ expect(ring.tail(5).toString()).toBe('hello');
+ expect(ring.tail(3).toString()).toBe('llo');
+ expect(ring.tail(100).toString()).toBe('hello'); // borné à la fenêtre
+ expect(ring.tail(0).length).toBe(0);
+ });
+
+ it('écritures successives sans wrap', () => {
+ const ring = new RingBuffer(16);
+ ring.write(buf('abc'));
+ ring.write(buf('def'));
+ expect(ring.totalOffset).toBe(6);
+ expect(ring.tail(6).toString()).toBe('abcdef');
+ });
+
+ it('chunk vide : no-op', () => {
+ const ring = new RingBuffer(8);
+ ring.write(buf('ab'));
+ ring.write(Buffer.alloc(0));
+ expect(ring.totalOffset).toBe(2);
+ expect(ring.tail(8).toString()).toBe('ab');
+ });
+
+ it('buffer vierge : tail vide, readFrom(0) vide', () => {
+ const ring = new RingBuffer(8);
+ expect(ring.totalOffset).toBe(0);
+ expect(ring.tail(8).length).toBe(0);
+ expect(ring.readFrom(0)?.length).toBe(0);
+ expect(ring.readFrom(1)).toBeNull();
+ });
+});
+
+describe('RingBuffer — wrap-around', () => {
+ it('wrap simple : le chunk chevauche la frontière du buffer', () => {
+ const ring = new RingBuffer(8);
+ ring.write(buf('01234')); // pos 0..5
+ ring.write(buf('56789')); // 3 octets en queue, 2 au début
+ expect(ring.totalOffset).toBe(10);
+ expect(ring.windowStart).toBe(2);
+ expect(ring.tail(8).toString()).toBe('23456789');
+ expect(ring.tail(4).toString()).toBe('6789');
+ });
+
+ it('wrap-around multiple : la fenêtre reste les N derniers octets', () => {
+ const ring = new RingBuffer(8);
+ let stream = '';
+ for (let i = 0; i < 10; i++) {
+ const chunk = String.fromCharCode(97 + i).repeat(3); // 'aaa', 'bbb'…
+ ring.write(buf(chunk));
+ stream += chunk;
+ expect(ring.totalOffset).toBe(stream.length);
+ expect(ring.tail(8).toString()).toBe(stream.slice(Math.max(0, stream.length - 8)));
+ }
+ expect(ring.windowStart).toBe(30 - 8);
+ });
+
+ it('écriture qui finit exactement sur la frontière (tailSpace == chunk.length)', () => {
+ const ring = new RingBuffer(8);
+ ring.write(buf('abcd'));
+ ring.write(buf('efgh')); // remplit exactement jusqu'à la frontière
+ expect(ring.totalOffset).toBe(8);
+ expect(ring.tail(8).toString()).toBe('abcdefgh');
+ ring.write(buf('ij')); // repart de la position 0
+ expect(ring.tail(8).toString()).toBe('cdefghij');
+ });
+
+ it('total multiple exact de la capacité (end === 0)', () => {
+ const ring = new RingBuffer(8);
+ ring.write(buf('abcdefgh'));
+ ring.write(buf('ijklmnop'));
+ expect(ring.totalOffset).toBe(16);
+ expect(ring.tail(8).toString()).toBe('ijklmnop');
+ expect(ring.tail(3).toString()).toBe('nop');
+ });
+});
+
+describe('RingBuffer — chunk >= capacité', () => {
+ it('chunk == capacité sur un ring vierge', () => {
+ const ring = new RingBuffer(8);
+ ring.write(buf('abcdefgh'));
+ expect(ring.totalOffset).toBe(8);
+ expect(ring.windowStart).toBe(0);
+ expect(ring.tail(8).toString()).toBe('abcdefgh');
+ });
+
+ it('chunk == 2x capacité sur un ring vierge (total reste aligné)', () => {
+ const ring = new RingBuffer(8);
+ ring.write(buf('0123456789abcdef'));
+ expect(ring.totalOffset).toBe(16);
+ expect(ring.windowStart).toBe(8);
+ expect(ring.tail(8).toString()).toBe('89abcdef');
+ expect(ring.readFrom(8)?.toString()).toBe('89abcdef');
+ expect(ring.readFrom(7)).toBeNull();
+ });
+
+ // Invariant : l'octet k du flux vit à la position k % capacity, même quand un
+ // chunk dépasse la capacité (les octets sautés comptent dans l'offset).
+ it('chunk > capacité non aligné : la fenêtre reste ordonnée', () => {
+ const ring = new RingBuffer(8);
+ ring.write(buf('0123456789')); // 10 octets, total final 10 % 8 != 0
+ expect(ring.totalOffset).toBe(10);
+ expect(ring.tail(8).toString()).toBe('23456789');
+ });
+
+ it('chunk == capacité après un préfixe non aligné : fenêtre ordonnée', () => {
+ const ring = new RingBuffer(8);
+ ring.write(buf('abc'));
+ ring.write(buf('ABCDEFGH'));
+ expect(ring.tail(8).toString()).toBe('ABCDEFGH');
+ });
+});
+
+describe('RingBuffer — readFrom', () => {
+ function filled(): RingBuffer {
+ // total = 10, capacité 8 → fenêtre = offsets [2, 10)
+ const ring = new RingBuffer(8);
+ ring.write(buf('01234'));
+ ring.write(buf('56789'));
+ return ring;
+ }
+
+ it('offset dans la fenêtre', () => {
+ const ring = filled();
+ expect(ring.readFrom(5)?.toString()).toBe('56789');
+ expect(ring.readFrom(9)?.toString()).toBe('9');
+ });
+
+ it('offset == windowStart : toute la fenêtre', () => {
+ const ring = filled();
+ expect(ring.readFrom(ring.windowStart)?.toString()).toBe('23456789');
+ });
+
+ it('offset == total : buffer vide (rien de nouveau)', () => {
+ const ring = filled();
+ const out = ring.readFrom(10);
+ expect(out).not.toBeNull();
+ expect(out?.length).toBe(0);
+ });
+
+ it('offset sorti de fenêtre ou au-delà du total → null', () => {
+ const ring = filled();
+ expect(ring.readFrom(1)).toBeNull(); // windowStart - 1
+ expect(ring.readFrom(0)).toBeNull();
+ expect(ring.readFrom(-1)).toBeNull();
+ expect(ring.readFrom(11)).toBeNull(); // total + 1
+ });
+});
+
+describe('RingBuffer — totalOffset monotone', () => {
+ it('croît exactement de la taille de chaque chunk, jamais ne décroît', () => {
+ const ring = new RingBuffer(8);
+ const rand = mulberry32(0x5eed);
+ let expected = 0;
+ for (let i = 0; i < 200; i++) {
+ const size = Math.floor(rand() * 7); // 0..6 (< capacité)
+ ring.write(Buffer.alloc(size, 65 + (i % 26)));
+ expected += size;
+ expect(ring.totalOffset).toBe(expected);
+ expect(ring.windowStart).toBe(Math.max(0, expected - 8));
+ }
+ });
+});
+
+describe('RingBuffer — équivalence avec le modèle de référence (property test)', () => {
+ it('tail et readFrom identiques au modèle pour 500 écritures aléatoires (chunks < capacité)', () => {
+ const capacity = 64;
+ const ring = new RingBuffer(capacity);
+ const ref = new RefBuffer(capacity);
+ const rand = mulberry32(0xc0ffee);
+ for (let i = 0; i < 500; i++) {
+ const size = Math.floor(rand() * capacity); // 0..63
+ const chunk = Buffer.alloc(size);
+ for (let j = 0; j < size; j++) chunk[j] = Math.floor(rand() * 256);
+ ring.write(chunk);
+ ref.write(chunk);
+
+ expect(ring.totalOffset).toBe(ref.total);
+ expect(ring.windowStart).toBe(ref.windowStart);
+ for (const n of [0, 1, 7, capacity - 1, capacity, capacity + 1, 500]) {
+ expect(ring.tail(n).equals(ref.tail(n)), `tail(${n}) après ${i + 1} écritures`).toBe(true);
+ }
+ const offsets = [
+ ref.windowStart - 1,
+ ref.windowStart,
+ ref.windowStart + Math.floor((ref.total - ref.windowStart) / 2),
+ ref.total - 1,
+ ref.total,
+ ref.total + 1,
+ ];
+ for (const off of offsets) {
+ const a = ring.readFrom(off);
+ const b = ref.readFrom(off);
+ if (b === null) {
+ expect(a, `readFrom(${off}) après ${i + 1} écritures`).toBeNull();
+ } else {
+ expect(a?.equals(b), `readFrom(${off}) après ${i + 1} écritures`).toBe(true);
+ }
+ }
+ }
+ });
+});
diff --git a/packages/server/tsconfig.tsbuildinfo b/packages/server/tsconfig.tsbuildinfo
index 1ef9f47..07a954e 100644
--- a/packages/server/tsconfig.tsbuildinfo
+++ b/packages/server/tsconfig.tsbuildinfo
@@ -1 +1 @@
-{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.es2021.d.ts","../../node_modules/typescript/lib/lib.es2022.d.ts","../../node_modules/typescript/lib/lib.es2023.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/typescript/lib/lib.es2021.promise.d.ts","../../node_modules/typescript/lib/lib.es2021.string.d.ts","../../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../node_modules/typescript/lib/lib.es2021.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.array.d.ts","../../node_modules/typescript/lib/lib.es2022.error.d.ts","../../node_modules/typescript/lib/lib.es2022.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.object.d.ts","../../node_modules/typescript/lib/lib.es2022.string.d.ts","../../node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../node_modules/typescript/lib/lib.es2023.array.d.ts","../../node_modules/typescript/lib/lib.es2023.collection.d.ts","../../node_modules/typescript/lib/lib.es2023.intl.d.ts","../../node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../node_modules/fast-uri/types/index.d.ts","../../node_modules/ajv/dist/compile/codegen/code.d.ts","../../node_modules/ajv/dist/compile/codegen/scope.d.ts","../../node_modules/ajv/dist/compile/codegen/index.d.ts","../../node_modules/ajv/dist/compile/rules.d.ts","../../node_modules/ajv/dist/compile/util.d.ts","../../node_modules/ajv/dist/compile/validate/subschema.d.ts","../../node_modules/ajv/dist/compile/errors.d.ts","../../node_modules/ajv/dist/compile/validate/index.d.ts","../../node_modules/ajv/dist/compile/validate/dataType.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/additionalItems.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/items2020.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/contains.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/dependencies.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/propertyNames.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/additionalProperties.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/not.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/anyOf.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/oneOf.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/if.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/index.d.ts","../../node_modules/ajv/dist/vocabularies/validation/limitNumber.d.ts","../../node_modules/ajv/dist/vocabularies/validation/multipleOf.d.ts","../../node_modules/ajv/dist/vocabularies/validation/pattern.d.ts","../../node_modules/ajv/dist/vocabularies/validation/required.d.ts","../../node_modules/ajv/dist/vocabularies/validation/uniqueItems.d.ts","../../node_modules/ajv/dist/vocabularies/validation/const.d.ts","../../node_modules/ajv/dist/vocabularies/validation/enum.d.ts","../../node_modules/ajv/dist/vocabularies/validation/index.d.ts","../../node_modules/ajv/dist/vocabularies/format/format.d.ts","../../node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedProperties.d.ts","../../node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedItems.d.ts","../../node_modules/ajv/dist/vocabularies/validation/dependentRequired.d.ts","../../node_modules/ajv/dist/vocabularies/discriminator/types.d.ts","../../node_modules/ajv/dist/vocabularies/discriminator/index.d.ts","../../node_modules/ajv/dist/vocabularies/errors.d.ts","../../node_modules/ajv/dist/types/json-schema.d.ts","../../node_modules/ajv/dist/types/jtd-schema.d.ts","../../node_modules/ajv/dist/runtime/validation_error.d.ts","../../node_modules/ajv/dist/compile/ref_error.d.ts","../../node_modules/ajv/dist/core.d.ts","../../node_modules/ajv/dist/compile/resolve.d.ts","../../node_modules/ajv/dist/compile/index.d.ts","../../node_modules/ajv/dist/types/index.d.ts","../../node_modules/ajv/dist/ajv.d.ts","../../node_modules/ajv/dist/vocabularies/jtd/error.d.ts","../../node_modules/ajv/dist/vocabularies/jtd/type.d.ts","../../node_modules/ajv/dist/vocabularies/jtd/enum.d.ts","../../node_modules/ajv/dist/vocabularies/jtd/elements.d.ts","../../node_modules/ajv/dist/vocabularies/jtd/properties.d.ts","../../node_modules/ajv/dist/vocabularies/jtd/discriminator.d.ts","../../node_modules/ajv/dist/vocabularies/jtd/values.d.ts","../../node_modules/ajv/dist/vocabularies/jtd/index.d.ts","../../node_modules/ajv/dist/jtd.d.ts","../../node_modules/@fastify/ajv-compiler/types/index.d.ts","../../node_modules/@fastify/error/types/index.d.ts","../../node_modules/fast-json-stringify/types/index.d.ts","../../node_modules/@fastify/fast-json-stringify-compiler/types/index.d.ts","../../node_modules/find-my-way/index.d.ts","../../node_modules/light-my-request/types/index.d.ts","../../node_modules/fastify/types/utils.d.ts","../../node_modules/fastify/types/schema.d.ts","../../node_modules/fastify/types/type-provider.d.ts","../../node_modules/fastify/types/reply.d.ts","../../node_modules/@types/node/compatibility/disposable.d.ts","../../node_modules/@types/node/compatibility/indexable.d.ts","../../node_modules/@types/node/compatibility/iterators.d.ts","../../node_modules/@types/node/compatibility/index.d.ts","../../node_modules/@types/node/globals.typedarray.d.ts","../../node_modules/@types/node/buffer.buffer.d.ts","../../node_modules/@types/node/globals.d.ts","../../node_modules/@types/node/web-globals/abortcontroller.d.ts","../../node_modules/@types/node/web-globals/domexception.d.ts","../../node_modules/@types/node/web-globals/events.d.ts","../../node_modules/buffer/index.d.ts","../../node_modules/undici-types/header.d.ts","../../node_modules/undici-types/readable.d.ts","../../node_modules/undici-types/file.d.ts","../../node_modules/undici-types/fetch.d.ts","../../node_modules/undici-types/formdata.d.ts","../../node_modules/undici-types/connector.d.ts","../../node_modules/undici-types/client.d.ts","../../node_modules/undici-types/errors.d.ts","../../node_modules/undici-types/dispatcher.d.ts","../../node_modules/undici-types/global-dispatcher.d.ts","../../node_modules/undici-types/global-origin.d.ts","../../node_modules/undici-types/pool-stats.d.ts","../../node_modules/undici-types/pool.d.ts","../../node_modules/undici-types/handlers.d.ts","../../node_modules/undici-types/balanced-pool.d.ts","../../node_modules/undici-types/agent.d.ts","../../node_modules/undici-types/mock-interceptor.d.ts","../../node_modules/undici-types/mock-agent.d.ts","../../node_modules/undici-types/mock-client.d.ts","../../node_modules/undici-types/mock-pool.d.ts","../../node_modules/undici-types/mock-errors.d.ts","../../node_modules/undici-types/proxy-agent.d.ts","../../node_modules/undici-types/env-http-proxy-agent.d.ts","../../node_modules/undici-types/retry-handler.d.ts","../../node_modules/undici-types/retry-agent.d.ts","../../node_modules/undici-types/api.d.ts","../../node_modules/undici-types/interceptors.d.ts","../../node_modules/undici-types/util.d.ts","../../node_modules/undici-types/cookies.d.ts","../../node_modules/undici-types/patch.d.ts","../../node_modules/undici-types/websocket.d.ts","../../node_modules/undici-types/eventsource.d.ts","../../node_modules/undici-types/filereader.d.ts","../../node_modules/undici-types/diagnostics-channel.d.ts","../../node_modules/undici-types/content-type.d.ts","../../node_modules/undici-types/cache.d.ts","../../node_modules/undici-types/index.d.ts","../../node_modules/@types/node/web-globals/fetch.d.ts","../../node_modules/@types/node/web-globals/navigator.d.ts","../../node_modules/@types/node/web-globals/storage.d.ts","../../node_modules/@types/node/assert.d.ts","../../node_modules/@types/node/assert/strict.d.ts","../../node_modules/@types/node/async_hooks.d.ts","../../node_modules/@types/node/buffer.d.ts","../../node_modules/@types/node/child_process.d.ts","../../node_modules/@types/node/cluster.d.ts","../../node_modules/@types/node/console.d.ts","../../node_modules/@types/node/constants.d.ts","../../node_modules/@types/node/crypto.d.ts","../../node_modules/@types/node/dgram.d.ts","../../node_modules/@types/node/diagnostics_channel.d.ts","../../node_modules/@types/node/dns.d.ts","../../node_modules/@types/node/dns/promises.d.ts","../../node_modules/@types/node/domain.d.ts","../../node_modules/@types/node/events.d.ts","../../node_modules/@types/node/fs.d.ts","../../node_modules/@types/node/fs/promises.d.ts","../../node_modules/@types/node/http.d.ts","../../node_modules/@types/node/http2.d.ts","../../node_modules/@types/node/https.d.ts","../../node_modules/@types/node/inspector.d.ts","../../node_modules/@types/node/inspector.generated.d.ts","../../node_modules/@types/node/module.d.ts","../../node_modules/@types/node/net.d.ts","../../node_modules/@types/node/os.d.ts","../../node_modules/@types/node/path.d.ts","../../node_modules/@types/node/perf_hooks.d.ts","../../node_modules/@types/node/process.d.ts","../../node_modules/@types/node/punycode.d.ts","../../node_modules/@types/node/querystring.d.ts","../../node_modules/@types/node/readline.d.ts","../../node_modules/@types/node/readline/promises.d.ts","../../node_modules/@types/node/repl.d.ts","../../node_modules/@types/node/sea.d.ts","../../node_modules/@types/node/sqlite.d.ts","../../node_modules/@types/node/stream.d.ts","../../node_modules/@types/node/stream/promises.d.ts","../../node_modules/@types/node/stream/consumers.d.ts","../../node_modules/@types/node/stream/web.d.ts","../../node_modules/@types/node/string_decoder.d.ts","../../node_modules/@types/node/test.d.ts","../../node_modules/@types/node/timers.d.ts","../../node_modules/@types/node/timers/promises.d.ts","../../node_modules/@types/node/tls.d.ts","../../node_modules/@types/node/trace_events.d.ts","../../node_modules/@types/node/tty.d.ts","../../node_modules/@types/node/url.d.ts","../../node_modules/@types/node/util.d.ts","../../node_modules/@types/node/v8.d.ts","../../node_modules/@types/node/vm.d.ts","../../node_modules/@types/node/wasi.d.ts","../../node_modules/@types/node/worker_threads.d.ts","../../node_modules/@types/node/zlib.d.ts","../../node_modules/@types/node/index.d.ts","../../node_modules/pino-std-serializers/index.d.ts","../../node_modules/sonic-boom/types/index.d.ts","../../node_modules/thread-stream/index.d.ts","../../node_modules/pino/pino.d.ts","../../node_modules/fastify/types/logger.d.ts","../../node_modules/fastify/types/plugin.d.ts","../../node_modules/fastify/types/register.d.ts","../../node_modules/fastify/types/instance.d.ts","../../node_modules/fastify/types/hooks.d.ts","../../node_modules/fastify/types/route.d.ts","../../node_modules/fastify/types/context.d.ts","../../node_modules/fastify/types/request.d.ts","../../node_modules/fastify/types/content-type-parser.d.ts","../../node_modules/fastify/types/errors.d.ts","../../node_modules/fastify/types/server-factory.d.ts","../../node_modules/fastify/fastify.d.ts","../../node_modules/@fastify/cookie/types/plugin.d.ts","../../node_modules/@types/ws/index.d.ts","../../node_modules/@fastify/websocket/types/index.d.ts","../../node_modules/@fastify/static/types/index.d.ts","./src/config.ts","./src/db/index.ts","./src/auth/service.ts","../../node_modules/@homebridge/node-pty-prebuilt-multiarch/typings/node-pty.d.ts","../shared/dist/protocol.d.ts","../shared/dist/api.d.ts","../shared/dist/index.d.ts","./src/core/ring-buffer.ts","./src/core/claude-launcher.ts","./src/core/pty-manager.ts","./src/routes/auth.ts","./src/routes/sessions.ts","../../node_modules/@types/ws/index.d.mts","./src/ws/gateway.ts","./src/app.ts","./src/index.ts"],"fileIdsList":[[102,106,115,131,180,197,198],[131,180,197,198,230,246,249,250,265],[131,180,197,198],[118,131,180,197,198],[131,180,192,197,198,230,246,247,249,265],[125,131,180,194,197,198,230,239,240,246,247,248,250,265],[131,177,178,180,197,198],[131,179,180,197,198],[180,197,198],[131,180,185,197,198,215],[131,180,181,186,191,197,198,200,212,223],[131,180,181,182,191,197,198,200],[126,127,128,131,180,197,198],[131,180,183,197,198,224],[131,180,184,185,192,197,198,201],[131,180,185,197,198,212,220],[131,180,186,188,191,197,198,200],[131,179,180,187,197,198],[131,180,188,189,197,198],[131,180,190,191,197,198],[131,179,180,191,197,198],[131,180,191,192,193,197,198,212,223],[131,180,191,192,193,197,198,207,212,215],[131,173,180,188,191,194,197,198,200,212,223],[131,180,191,192,194,195,197,198,200,212,220,223],[131,180,194,196,197,198,212,220,223],[129,130,131,132,133,134,135,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229],[131,180,191,197,198],[131,180,197,198,199,223],[131,180,188,191,197,198,200,212],[131,180,197,198,201],[131,180,197,198,202],[131,179,180,197,198,203],[131,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229],[131,180,197,198,205],[131,180,197,198,206],[131,180,191,197,198,207,208],[131,180,197,198,207,209,224,226],[131,180,192,197,198],[131,180,191,197,198,212,213,215],[131,180,197,198,214,215],[131,180,197,198,212,213],[131,180,197,198,215],[131,180,197,198,216],[131,177,180,197,198,212,217,223],[131,180,191,197,198,218,219],[131,180,197,198,218,219],[131,180,185,197,198,200,212,220],[131,180,197,198,221],[131,180,197,198,200,222],[131,180,194,197,198,206,223],[131,180,185,197,198,224],[131,180,197,198,212,225],[131,180,197,198,199,226],[131,180,197,198,227],[131,173,180,197,198],[131,173,180,191,193,197,198,203,212,215,223,225,226,228],[131,180,197,198,212,229],[131,180,191,194,196,197,198,200,212,220,223,229,230],[65,66,70,97,98,100,101,102,104,105,131,180,197,198],[63,64,131,180,197,198],[63,131,180,197,198],[65,105,131,180,197,198],[65,66,102,103,105,131,180,197,198],[105,131,180,197,198],[62,105,106,131,180,197,198],[65,66,104,105,131,180,197,198],[65,66,68,69,104,105,131,180,197,198],[65,66,67,104,105,131,180,197,198],[65,66,70,97,98,99,100,101,104,105,131,180,197,198],[65,70,99,100,101,102,104,105,114,131,180,197,198],[62,65,66,70,102,104,131,180,197,198],[70,105,131,180,197,198],[72,73,74,75,76,77,78,79,80,81,105,131,180,197,198],[95,105,131,180,197,198],[71,82,90,91,92,93,94,96,131,180,197,198],[95,105,107,131,180,197,198],[105,107,131,180,197,198],[105,108,109,110,111,112,113,131,180,197,198],[70,105,107,131,180,197,198],[75,105,131,180,197,198],[83,84,85,86,87,88,89,105,131,180,197,198],[106,131,180,197,198],[116,117,119,120,121,122,123,124,125,131,180,194,195,196,197,198,200,235,236,237,238,239,240,241,242,243,244,245],[122,123,124,131,180,197,198,240,242],[122,131,180,197,198,240],[117,131,180,197,198],[117,122,123,124,125,131,180,197,198,212,235,236,237,238,240,242],[117,120,121,122,123,124,125,131,180,194,197,198,200,235,237,239,240,242,243,246],[117,122,123,124,125,131,180,197,198,234,238,240,242],[122,124,131,180,197,198,235,238],[122,131,180,197,198,235,236,238,246],[122,123,124,131,180,197,198,235,238,240,242],[116,122,123,124,131,180,197,198,235,238,240,241],[117,120,122,123,124,125,131,180,197,198,235,238,239,241,242],[116,119,131,180,197,198,246],[122,131,180,194,195,196,197,198],[122,123,131,180,197,198,240],[131,180,194,195,196,197,198],[131,180,194,195,197,198],[131,180,194,197,198,212],[131,180,194,197,198,230],[131,180,191,197,198,228,231,232,233],[131,180,191,197,198,230],[131,180,191,197,198,228],[131,145,149,180,197,198,223],[131,145,180,197,198,212,223],[131,140,180,197,198],[131,142,145,180,197,198,220,223],[131,180,197,198,200,220],[131,180,197,198,230],[131,140,180,197,198,230],[131,142,145,180,197,198,200,223],[131,137,138,141,144,180,191,197,198,212,223],[131,145,152,180,197,198],[131,137,143,180,197,198],[131,145,166,167,180,197,198],[131,141,145,180,197,198,215,223,230],[131,166,180,197,198,230],[131,139,140,180,197,198,230],[131,145,180,197,198],[131,139,140,141,142,143,144,145,146,147,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,167,168,169,170,171,172,180,197,198],[131,145,160,180,197,198],[131,145,152,153,180,197,198],[131,143,145,153,154,180,197,198],[131,144,180,197,198],[131,137,140,145,180,197,198],[131,145,149,153,154,180,197,198],[131,149,180,197,198],[131,143,145,148,180,197,198,223],[131,137,142,145,152,180,197,198],[131,180,197,198,212],[131,140,145,166,180,197,198,228,230],[131,180,192,197,198,202,223,246,247,249,250,251,252,253,260,261,262,264],[131,180,185,197,198,252],[131,180,192,197,198,201,202,224],[131,180,181,197,198],[131,180,185,191,192,197,198,252,254,257,258,259],[131,180,197,198,211],[131,180,192,197,198,202,223,251,252,265],[131,180,197,198,246,253,257],[131,180,197,198,246,257,260],[131,180,197,198,246,257,260,263],[131,180,197,198,255],[131,180,197,198,255,256]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"df83c2a6c73228b625b0beb6669c7ee2a09c914637e2d35170723ad49c0f5cd4","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"84bcc7c6b06f4d643a55dc63b56be0c81d990f8d549b66ea615c553268774dc3","impliedFormat":1},{"version":"2d225e7bda2871c066a7079c88174340950fb604f624f2586d3ea27bb9e5f4ff","impliedFormat":1},{"version":"6a785f84e63234035e511817dd48ada756d984dd8f9344e56eb8b2bdcd8fd001","impliedFormat":1},{"version":"c1422d016f7df2ccd3594c06f2923199acd09898f2c42f50ea8159f1f856f618","impliedFormat":1},{"version":"2973b1b7857ca144251375b97f98474e9847a890331e27132d5a8b3aea9350a8","impliedFormat":1},{"version":"0eb6152d37c84d6119295493dfcc20c331c6fda1304a513d159cdaa599dcb78b","impliedFormat":1},{"version":"237df26f8c326ca00cd9d2deb40214a079749062156386b6d75bdcecc6988a6b","impliedFormat":1},{"version":"cd44995ee13d5d23df17a10213fed7b483fabfd5ea08f267ab52c07ce0b6b4da","impliedFormat":1},{"version":"58ce1486f851942bd2d3056b399079bc9cb978ec933fe9833ea417e33eab676e","impliedFormat":1},{"version":"7557d4d7f19f94341f4413575a3453ba7f6039c9591015bcf4282a8e75414043","impliedFormat":1},{"version":"a3b2cc16f3ce2d882eca44e1066f57a24751545f2a5e4a153d4de31b4cac9bb5","impliedFormat":1},{"version":"ac2b3b377d3068bfb6e1cb8889c99098f2c875955e2325315991882a74d92cc8","impliedFormat":1},{"version":"8deb39d89095469957f73bd194d11f01d9894b8c1f1e27fbf3f6e8122576b336","impliedFormat":1},{"version":"a38a9c41f433b608a0d37e645a31eecf7233ef3d3fffeb626988d3219f80e32f","impliedFormat":1},{"version":"8e1428dcba6a984489863935049893631170a37f9584c0479f06e1a5b1f04332","impliedFormat":1},{"version":"1fce9ecb87a2d3898941c60df617e52e50fb0c03c9b7b2ba8381972448327285","impliedFormat":1},{"version":"5ef0597b8238443908b2c4bf69149ed3894ac0ddd0515ac583d38c7595b151f1","impliedFormat":1},{"version":"ac52b775a80badff5f4ac329c5725a26bd5aaadd57afa7ad9e98b4844767312a","impliedFormat":1},{"version":"6ae5b4a63010c82bf2522b4ecfc29ffe6a8b0c5eea6b2b35120077e9ac54d7a1","impliedFormat":1},{"version":"dd7109c49f416f218915921d44f0f28975df78e04e437c62e1e1eb3be5e18a35","impliedFormat":1},{"version":"eee181112e420b345fc78422a6cc32385ede3d27e2eaf8b8c4ad8b2c29e3e52e","impliedFormat":1},{"version":"25fbe57c8ee3079e2201fe580578fab4f3a78881c98865b7c96233af00bf9624","impliedFormat":1},{"version":"62cc8477858487b4c4de7d7ae5e745a8ce0015c1592f398b63ee05d6e64ca295","impliedFormat":1},{"version":"cc2a9ec3cb10e4c0b8738b02c31798fad312d21ef20b6a2f5be1d077e9f5409d","impliedFormat":1},{"version":"4b4fadcda7d34034737598c07e2dca5d7e1e633cb3ba8dd4d2e6a7782b30b296","impliedFormat":1},{"version":"360fdc8829a51c5428636f1f83e7db36fef6c5a15ed4411b582d00a1c2bd6e97","impliedFormat":1},{"version":"1cf0d15e6ab1ecabbf329b906ae8543e6b8955133b7f6655f04d433e3a0597ab","impliedFormat":1},{"version":"7c9f98fe812643141502b30fb2b5ec56d16aaf94f98580276ae37b7924dd44a4","impliedFormat":1},{"version":"b3547893f24f59d0a644c52f55901b15a3fa1a115bc5ea9a582911469b9348b7","impliedFormat":1},{"version":"596e5b88b6ca8399076afcc22af6e6e0c4700c7cd1f420a78d637c3fb44a885e","impliedFormat":1},{"version":"adddf736e08132c7059ee572b128fdacb1c2650ace80d0f582e93d097ed4fbaf","impliedFormat":1},{"version":"d4cad9dc13e9c5348637170ddd5d95f7ed5fdfc856ddca40234fa55518bc99a6","impliedFormat":1},{"version":"d70675ba7ba7d02e52b7070a369957a70827e4b2bca2c1680c38a832e87b61fd","impliedFormat":1},{"version":"3be71f4ce8988a01e2f5368bdd58e1d60236baf511e4510ee9291c7b3729a27e","impliedFormat":1},{"version":"423d2ccc38e369a7527988d682fafc40267bcd6688a7473e59c5eea20a29b64f","impliedFormat":1},{"version":"2f9fde0868ed030277c678b435f63fcf03d27c04301299580a4017963cc04ce6","impliedFormat":1},{"version":"feeb73d48cc41c6dd23d17473521b0af877751504c30c18dc84267c8eeea429a","impliedFormat":1},{"version":"25f1159094dc0bf3a71313a74e0885426af21c5d6564a254004f2cadf9c5b052","impliedFormat":1},{"version":"cde493e09daad4bb29922fe633f760be9f0e8e2f39cdca999cce3b8690b5e13a","impliedFormat":1},{"version":"3d7f9eb12aface876f7b535cc89dcd416daf77f0b3573333f16ec0a70bcf902a","impliedFormat":1},{"version":"4304f640f7cb4724ea82441accb7c7607fa7207541182470d625adda99b2900b","impliedFormat":1},{"version":"e0205f04611bea8b5b82168065b8ef1476a8e96236201494eb8c785331c43118","impliedFormat":1},{"version":"62d26d8ba4fa15ab425c1b57a050ed76c5b0ecbffaa53f182110aa3a02405a07","impliedFormat":1},{"version":"9941cbf7ca695e95d588f5f1692ab040b078d44a95d231fa9a8f828186b7b77d","impliedFormat":1},{"version":"41b8775befd7ded7245a627e9f4de6110236688ce4c124d2d40c37bc1a3bfe05","impliedFormat":1},{"version":"a6613ee552418429af38391e37389036654a882c342a1b81f2711e8ddac597f2","impliedFormat":1},{"version":"da47cb979ae4a849f9b983f43ef34365b7050c4f5ae2ebf818195858774e1d67","impliedFormat":1},{"version":"ac3bcb82d7280fc313a967f311764258d18caf33db6d2b1a0243cde607ff01a0","impliedFormat":1},{"version":"c9b5632d6665177030428d02603aeac3e920d31ec83ac500b55d44c7da74bd84","impliedFormat":1},{"version":"46456824df16d60f243a7e386562b27bac838aaba66050b9bc0f31e1ab34c1f2","impliedFormat":1},{"version":"b91034069e217212d8dda6c92669ee9f180b4c36273b5244c3be2c657f9286c7","impliedFormat":1},{"version":"0697277dd829ac2610d68fe1b457c9e758105bb52d40e149d9c15e5e2fe6dca4","impliedFormat":1},{"version":"b0d06dbb409369169143ede5df1fb58b2fca8d44588e199bd624b6f6d966bf08","impliedFormat":1},{"version":"88dfdb2a44912a28aea3ebb657dc7fcec6ba59f7233005e3405824995b713dac","impliedFormat":1},{"version":"f38a45cbc0356dfb6455211e75ecd0f8aeaedbacd4cb8d6646c818d36a46d195","impliedFormat":1},{"version":"cc2d5d5687bdf9d7c49b6946b8769ac7abcbdcd1701d9bb9ca70a8bc1b003e8b","impliedFormat":1},{"version":"6f1fabd39b8c9a66a3232030a4b28ed4fb4f857dcffef0add3220dab4bbba77a","impliedFormat":1},{"version":"9c0623d67471ddc5b9d82b4e06252c746d54f7ae8ccff8701cd51c249f7e7694","impliedFormat":1},{"version":"71654f68a41362f16a2690ec215e583d3cfa913d49327e43c3c7f354022f67a3","impliedFormat":1},{"version":"f69b484edf398d636992757d587e7e38ea91844a66dbca9d682c9cf7858b77cf","impliedFormat":1},{"version":"37d852b3e6b30b974178674dbf2a7974a1ea4bbdbec26d0bdb8f34632cab94a2","impliedFormat":1},{"version":"c3ccf44723b474b5d7a87c53eed9711e81422f0e29b3e8f8f8d42775fa802243","impliedFormat":1},{"version":"faeaf9586ba4505448bd0fdb332509a7e3ecca5d8ae10208950a5d03e8b4ea28","impliedFormat":1},{"version":"c3a0295707fde0cb51ca784ecb799aa95ca71c40337b5ee4a81a8f7134dd7e92","impliedFormat":1},{"version":"6c7176368037af28cb72f2392010fa1cef295d6d6744bca8cfb54985f3a18c3e","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"437e20f2ba32abaeb7985e0afe0002de1917bc74e949ba585e49feba65da6ca1","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"3af97acf03cc97de58a3a4bc91f8f616408099bc4233f6d0852e72a8ffb91ac9","affectsGlobalScope":true,"impliedFormat":1},{"version":"808069bba06b6768b62fd22429b53362e7af342da4a236ed2d2e1c89fcca3b4a","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e9c23ba78aabc2e0a27033f18737a6df754067731e69dc5f52823957d60a4b6","impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"f9501cc13ce624c72b61f12b3963e84fad210fbdf0ffbc4590e08460a3f04eba","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0fa06ada475b910e2106c98c68b10483dc8811d0c14a8a8dd36efb2672485b29","impliedFormat":1},{"version":"33e5e9aba62c3193d10d1d33ae1fa75c46a1171cf76fef750777377d53b0303f","impliedFormat":1},{"version":"2b06b93fd01bcd49d1a6bd1f9b65ddcae6480b9a86e9061634d6f8e354c1468f","impliedFormat":1},{"version":"6a0cd27e5dc2cfbe039e731cf879d12b0e2dded06d1b1dedad07f7712de0d7f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"13f5c844119c43e51ce777c509267f14d6aaf31eafb2c2b002ca35584cd13b29","impliedFormat":1},{"version":"e60477649d6ad21542bd2dc7e3d9ff6853d0797ba9f689ba2f6653818999c264","impliedFormat":1},{"version":"c2510f124c0293ab80b1777c44d80f812b75612f297b9857406468c0f4dafe29","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"4c829ab315f57c5442c6667b53769975acbf92003a66aef19bce151987675bd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"b2ade7657e2db96d18315694789eff2ddd3d8aea7215b181f8a0b303277cc579","impliedFormat":1},{"version":"9855e02d837744303391e5623a531734443a5f8e6e8755e018c41d63ad797db2","impliedFormat":1},{"version":"4d631b81fa2f07a0e63a9a143d6a82c25c5f051298651a9b69176ba28930756d","impliedFormat":1},{"version":"836a356aae992ff3c28a0212e3eabcb76dd4b0cc06bcb9607aeef560661b860d","impliedFormat":1},{"version":"1e0d1f8b0adfa0b0330e028c7941b5a98c08b600efe7f14d2d2a00854fb2f393","impliedFormat":1},{"version":"41670ee38943d9cbb4924e436f56fc19ee94232bc96108562de1a734af20dc2c","affectsGlobalScope":true,"impliedFormat":1},{"version":"c906fb15bd2aabc9ed1e3f44eb6a8661199d6c320b3aa196b826121552cb3695","impliedFormat":1},{"version":"22295e8103f1d6d8ea4b5d6211e43421fe4564e34d0dd8e09e520e452d89e659","impliedFormat":1},{"version":"58647d85d0f722a1ce9de50955df60a7489f0593bf1a7015521efe901c06d770","impliedFormat":1},{"version":"9018e9493596e2d041fc531f5101a0d05f9d2e1311f4ae3c74345b1766158c08","impliedFormat":1},{"version":"a10f0e1854f3316d7ee437b79649e5a6ae3ae14ffe6322b02d4987071a95362e","impliedFormat":1},{"version":"e208f73ef6a980104304b0d2ca5f6bf1b85de6009d2c7e404028b875020fa8f2","impliedFormat":1},{"version":"d163b6bc2372b4f07260747cbc6c0a6405ab3fbcea3852305e98ac43ca59f5bc","impliedFormat":1},{"version":"e6fa9ad47c5f71ff733744a029d1dc472c618de53804eae08ffc243b936f87ff","affectsGlobalScope":true,"impliedFormat":1},{"version":"a6f137d651076822d4fe884287e68fd61785a0d3d1fdb250a5059b691fa897db","impliedFormat":1},{"version":"24826ed94a78d5c64bd857570fdbd96229ad41b5cb654c08d75a9845e3ab7dde","impliedFormat":1},{"version":"8b479a130ccb62e98f11f136d3ac80f2984fdc07616516d29881f3061f2dd472","impliedFormat":1},{"version":"928af3d90454bf656a52a48679f199f64c1435247d6189d1caf4c68f2eaf921f","affectsGlobalScope":true,"impliedFormat":1},{"version":"bceb58df66ab8fb00170df20cd813978c5ab84be1d285710c4eb005d8e9d8efb","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f16a7e4deafa527ed9995a772bb380eb7d3c2c0fd4ae178c5263ed18394db2c","impliedFormat":1},{"version":"933921f0bb0ec12ef45d1062a1fc0f27635318f4d294e4d99de9a5493e618ca2","impliedFormat":1},{"version":"71a0f3ad612c123b57239a7749770017ecfe6b66411488000aba83e4546fde25","impliedFormat":1},{"version":"77fbe5eecb6fac4b6242bbf6eebfc43e98ce5ccba8fa44e0ef6a95c945ff4d98","impliedFormat":1},{"version":"4f9d8ca0c417b67b69eeb54c7ca1bedd7b56034bb9bfd27c5d4f3bc4692daca7","impliedFormat":1},{"version":"814118df420c4e38fe5ae1b9a3bafb6e9c2aa40838e528cde908381867be6466","impliedFormat":1},{"version":"a3fc63c0d7b031693f665f5494412ba4b551fe644ededccc0ab5922401079c95","impliedFormat":1},{"version":"80523c00b8544a2000ae0143e4a90a00b47f99823eb7926c1e03c494216fc363","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"746911b62b329587939560deb5c036aca48aece03147b021fa680223255d5183","affectsGlobalScope":true,"impliedFormat":1},{"version":"18fd40412d102c5564136f29735e5d1c3b455b8a37f920da79561f1fde068208","impliedFormat":1},{"version":"c8d3e5a18ba35629954e48c4cc8f11dc88224650067a172685c736b27a34a4dc","impliedFormat":1},{"version":"f0be1b8078cd549d91f37c30c222c2a187ac1cf981d994fb476a1adc61387b14","affectsGlobalScope":true,"impliedFormat":1},{"version":"0aaed1d72199b01234152f7a60046bc947f1f37d78d182e9ae09c4289e06a592","impliedFormat":1},{"version":"2b55d426ff2b9087485e52ac4bc7cfafe1dc420fc76dad926cd46526567c501a","impliedFormat":1},{"version":"66ba1b2c3e3a3644a1011cd530fb444a96b1b2dfe2f5e837a002d41a1a799e60","impliedFormat":1},{"version":"7e514f5b852fdbc166b539fdd1f4e9114f29911592a5eb10a94bb3a13ccac3c4","impliedFormat":1},{"version":"5b7aa3c4c1a5d81b411e8cb302b45507fea9358d3569196b27eb1a27ae3a90ef","affectsGlobalScope":true,"impliedFormat":1},{"version":"5987a903da92c7462e0b35704ce7da94d7fdc4b89a984871c0e2b87a8aae9e69","affectsGlobalScope":true,"impliedFormat":1},{"version":"ea08a0345023ade2b47fbff5a76d0d0ed8bff10bc9d22b83f40858a8e941501c","impliedFormat":1},{"version":"47613031a5a31510831304405af561b0ffaedb734437c595256bb61a90f9311b","impliedFormat":1},{"version":"ae062ce7d9510060c5d7e7952ae379224fb3f8f2dd74e88959878af2057c143b","impliedFormat":1},{"version":"8a1a0d0a4a06a8d278947fcb66bf684f117bf147f89b06e50662d79a53be3e9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"358765d5ea8afd285d4fd1532e78b88273f18cb3f87403a9b16fef61ac9fdcfe","impliedFormat":1},{"version":"9f55299850d4f0921e79b6bf344b47c420ce0f507b9dcf593e532b09ea7eeea1","impliedFormat":1},{"version":"4fe80f12b1d5189384a219095c2eabadbb389c2d3703aae7c5376dbaa56061df","impliedFormat":1},{"version":"9eb1d2dceae65d1c82fc6be7e9b6b19cf3ca93c364678611107362b6ad4d2d41","impliedFormat":1},{"version":"eb81f582c0e1ae21f004b39c268c68ad13222cd7e72f45080d6cdf9b994dfecf","impliedFormat":1},{"version":"e40f9840e4686053a5d42b39fddfd6774172a3d12fa61c1edb8c35b452a5e74a","impliedFormat":1},{"version":"c034433fe3ebd5391d0078de0193306591f61e421697a217cbeb92bd5e0f6ebc","impliedFormat":1},{"version":"479a021fec9328c8ab37f9372dcdc1022d79aeedde6febf03942b4456d1591c9","impliedFormat":1},{"version":"68d25a0614893e256a9e6fe18e2b125dbdad694381b57e63320de6a7518e47fc","impliedFormat":1},{"version":"49b89a0b4b3b3e18cbf726a9c608d622b4f6d49304076fc01c10f93833c70f09","impliedFormat":1},{"version":"4f62dc739ef3b99a93779255f85e51f02ce411d63cf294ee654258ff8c1978e4","impliedFormat":1},{"version":"3fa13ab73c7307a308bba2f9ca5cea92b74cbdae348a7ba1f5ac094fde7286c2","impliedFormat":1},{"version":"64102e00cb41de7f423608037d17dff83954904383e5c45f1054c2246cf5e184","impliedFormat":1},{"version":"5cf7ebe883e54b764af73548dbbef8887c640f1fd896f4d8b935b027d9989728","impliedFormat":1},{"version":"05e29a500e59cc5697947ee0fa9390e88ff008ec76be1f859152bda8ec01f13d","impliedFormat":1},{"version":"c89237cc87fc6e004290ae667babd0fa242e87c7de1cb49d4e5aba53391923f4","impliedFormat":1},{"version":"44b227ad122395768f07a8f1b84041b096220335b34ff7af3b8caa61731b294d","impliedFormat":1},{"version":"cfac258f114785ee4591fed69b045070da61d3ea03a9fabd750d682cc501253f","impliedFormat":1},{"version":"99dd60ecf4b17a5478ddc7a3a4e41500eba9b4fdbded73fa01e0ad525f7ff90d","impliedFormat":1},{"version":"1ba59c8bbeed2cb75b239bb12041582fa3e8ef32f8d0bd0ec802e38442d3f317","impliedFormat":1},{"version":"1bf1497c5e7ced001b52ed69657f472870edf3ef34641842046fc5cc47359fd0","impliedFormat":1},{"version":"36c1793e93c8a2dff0dcc67145b992e78fef0a67dd1f69d9f689b108a3598341","impliedFormat":1},{"version":"289302cd21b99f1b34e25812ed3119af56bab4740f10726c7a5a7db80df69e33","signature":"51cc114c75ade7761ead7dbeb917357947a820feb3c4c7589d6fc859804b117b","impliedFormat":99},{"version":"837f71558d52d6517b050cec1800f278a85fcff00dba7029801efc79bdd5e978","signature":"e1f8eff69d223ca308faac04131a51f40a811ad66005ea8553acd5b0a7884273","impliedFormat":99},{"version":"7fae4e5b823a86a5f5f4d4ab3689d6fdc8934afa67ddd3a5fa4792a7689ab146","signature":"b2389a3050e376a1e5e258f76d72c5c8baf0a80bc992c9ba65c263f9257c08a8","impliedFormat":99},{"version":"e2b0a3b2ab5fa5a3f31d8150ab3b93f29828131871a898379b117a16655888b0","impliedFormat":1},{"version":"155aa8c32f0815cb7f44246680859e2724428459727e6067d0d434a666b72e5b","impliedFormat":99},{"version":"0eaa5ca815f0d1f498a205737cc71a5259671ee1ffc96eb881ee721c2d60bf30","impliedFormat":99},{"version":"8b93a53483cf3bb55c754db9dffd3cc1e3d05f87fe7158aceb4b30f5521a14fc","impliedFormat":99},{"version":"912b5bd4dd0b17d42f18df923a05df4884b2ab3ec071d02f1ec3c8bf2adc75ae","signature":"1ddb6de962eac59d919fc52f07ce8b3c0af0c61da427b94b684d925c6ad6eb71","impliedFormat":99},{"version":"9c1e42f6390d8a66639fdead79e7cf2616f7708c2d8dba1dbf8db158ffa4d63c","signature":"d8396e56d160d98eb7f4fe538cb89f7db9345afa65563b3c7c1e030f6cee1bb5","impliedFormat":99},{"version":"caa98b91e3c64ab4f3d05e27940d556bae56cae78250ef2e623281f8028c4d09","signature":"b00d1b92b69461390eaecc1805d508328e6e1238c490bb4fc7d8f95dba9150c2","impliedFormat":99},{"version":"58089e4ae3cf5595f123796153c3852ab492073a464cf16875ca6f51dd2b89e7","signature":"9a427255affacd5f9393e9540bcdea7258ad97021bde8044493990e8dd2fd2ab","impliedFormat":99},{"version":"0aa5e375691619ba68ba72383a1641d5e8cb6781903b6cb714233a7a04f82787","signature":"d036d9eed1f11cdee9a3bb9a4ffe86cbeee21d03500f6c42de7219d53b660fa4","impliedFormat":99},{"version":"316f1486e15cbf7896425f0a16dfe12d447dd57cfb3244b8b119c77df870858f","impliedFormat":99},{"version":"303ab2a1b823b7ee483a89ab4ae9d720dc94a65886f6f0430577c63dfefb78f0","signature":"7f23a8041aff693c3a2cf7ff178e0f514a9dfa7d565e80fe5113ab60a575884e","impliedFormat":99},{"version":"4dd46bebc997d2d9de283ac20766fdfaaccb05baaab6e59bfd51ca0b0b1e3c57","signature":"3c903ff9ca7961c70214302108a0dd27044b7130b8d551ac891744fdb4bee60d","impliedFormat":99},{"version":"b0fef4d6b65da4b34876e3dc008d9b157c4f094a17855fcff92a18464c677179","signature":"43e818adf60173644896298637f47b01d5819b17eda46eaa32d0c7d64724d012","impliedFormat":99}],"root":[[251,253],[258,262],[264,266]],"options":{"composite":true,"declaration":true,"declarationMap":true,"esModuleInterop":true,"exactOptionalPropertyTypes":true,"module":199,"noImplicitOverride":true,"noUncheckedIndexedAccess":true,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"sourceMap":true,"strict":true,"target":10,"verbatimModuleSyntax":true},"referencedMap":[[116,1],[247,2],[117,3],[119,4],[250,5],[249,6],[254,3],[177,7],[178,7],[179,8],[131,9],[180,10],[181,11],[182,12],[126,3],[129,13],[127,3],[128,3],[183,14],[184,15],[185,16],[186,17],[187,18],[188,19],[189,19],[190,20],[191,21],[192,22],[193,23],[132,3],[130,3],[194,24],[195,25],[196,26],[230,27],[197,28],[198,3],[199,29],[200,30],[201,31],[202,32],[203,33],[204,34],[205,35],[206,36],[207,37],[208,37],[209,38],[210,3],[211,39],[212,40],[214,41],[213,42],[215,43],[216,44],[217,45],[218,46],[219,47],[220,48],[221,49],[222,50],[223,51],[224,52],[225,53],[226,54],[227,55],[133,3],[134,3],[135,3],[174,56],[175,3],[176,3],[228,57],[229,58],[263,59],[248,59],[106,60],[63,3],[65,61],[64,62],[69,63],[104,64],[101,65],[103,66],[66,65],[67,67],[71,67],[70,68],[68,69],[102,70],[115,71],[100,65],[105,72],[98,3],[99,3],[72,73],[77,65],[79,65],[74,65],[75,73],[81,65],[82,74],[73,65],[78,65],[80,65],[76,65],[96,75],[95,65],[97,76],[91,65],[112,77],[110,78],[109,65],[107,63],[114,79],[111,80],[108,78],[113,78],[93,65],[92,65],[88,65],[94,81],[89,65],[90,82],[83,65],[84,65],[85,65],[86,65],[87,65],[136,3],[118,83],[62,3],[246,84],[243,85],[241,86],[244,87],[239,88],[238,89],[235,90],[236,91],[237,92],[125,93],[242,94],[240,95],[123,96],[245,97],[124,98],[122,99],[120,100],[121,101],[231,102],[234,103],[232,104],[233,105],[60,3],[61,3],[12,3],[11,3],[2,3],[13,3],[14,3],[15,3],[16,3],[17,3],[18,3],[19,3],[20,3],[3,3],[21,3],[22,3],[4,3],[23,3],[27,3],[24,3],[25,3],[26,3],[28,3],[29,3],[30,3],[5,3],[31,3],[32,3],[33,3],[34,3],[6,3],[38,3],[35,3],[36,3],[37,3],[39,3],[7,3],[40,3],[45,3],[46,3],[41,3],[42,3],[43,3],[44,3],[8,3],[50,3],[47,3],[48,3],[49,3],[51,3],[9,3],[52,3],[53,3],[54,3],[56,3],[55,3],[57,3],[58,3],[10,3],[59,3],[1,3],[152,106],[162,107],[151,106],[172,108],[143,109],[142,110],[171,111],[165,112],[170,113],[145,114],[159,115],[144,116],[168,117],[140,118],[139,111],[169,119],[141,120],[146,121],[147,3],[150,121],[137,3],[173,122],[163,123],[154,124],[155,125],[157,126],[153,127],[156,128],[166,111],[148,129],[149,130],[158,131],[138,132],[161,123],[160,121],[164,3],[167,133],[265,134],[253,135],[251,136],[259,137],[260,138],[258,3],[252,139],[266,140],[261,141],[262,142],[264,143],[256,144],[257,145],[255,3]],"latestChangedDtsFile":"./dist/index.d.ts","version":"5.9.3"}
\ No newline at end of file
+{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.es2021.d.ts","../../node_modules/typescript/lib/lib.es2022.d.ts","../../node_modules/typescript/lib/lib.es2023.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/typescript/lib/lib.es2021.promise.d.ts","../../node_modules/typescript/lib/lib.es2021.string.d.ts","../../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../node_modules/typescript/lib/lib.es2021.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.array.d.ts","../../node_modules/typescript/lib/lib.es2022.error.d.ts","../../node_modules/typescript/lib/lib.es2022.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.object.d.ts","../../node_modules/typescript/lib/lib.es2022.string.d.ts","../../node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../node_modules/typescript/lib/lib.es2023.array.d.ts","../../node_modules/typescript/lib/lib.es2023.collection.d.ts","../../node_modules/typescript/lib/lib.es2023.intl.d.ts","../../node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../node_modules/fast-uri/types/index.d.ts","../../node_modules/ajv/dist/compile/codegen/code.d.ts","../../node_modules/ajv/dist/compile/codegen/scope.d.ts","../../node_modules/ajv/dist/compile/codegen/index.d.ts","../../node_modules/ajv/dist/compile/rules.d.ts","../../node_modules/ajv/dist/compile/util.d.ts","../../node_modules/ajv/dist/compile/validate/subschema.d.ts","../../node_modules/ajv/dist/compile/errors.d.ts","../../node_modules/ajv/dist/compile/validate/index.d.ts","../../node_modules/ajv/dist/compile/validate/dataType.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/additionalItems.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/items2020.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/contains.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/dependencies.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/propertyNames.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/additionalProperties.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/not.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/anyOf.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/oneOf.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/if.d.ts","../../node_modules/ajv/dist/vocabularies/applicator/index.d.ts","../../node_modules/ajv/dist/vocabularies/validation/limitNumber.d.ts","../../node_modules/ajv/dist/vocabularies/validation/multipleOf.d.ts","../../node_modules/ajv/dist/vocabularies/validation/pattern.d.ts","../../node_modules/ajv/dist/vocabularies/validation/required.d.ts","../../node_modules/ajv/dist/vocabularies/validation/uniqueItems.d.ts","../../node_modules/ajv/dist/vocabularies/validation/const.d.ts","../../node_modules/ajv/dist/vocabularies/validation/enum.d.ts","../../node_modules/ajv/dist/vocabularies/validation/index.d.ts","../../node_modules/ajv/dist/vocabularies/format/format.d.ts","../../node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedProperties.d.ts","../../node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedItems.d.ts","../../node_modules/ajv/dist/vocabularies/validation/dependentRequired.d.ts","../../node_modules/ajv/dist/vocabularies/discriminator/types.d.ts","../../node_modules/ajv/dist/vocabularies/discriminator/index.d.ts","../../node_modules/ajv/dist/vocabularies/errors.d.ts","../../node_modules/ajv/dist/types/json-schema.d.ts","../../node_modules/ajv/dist/types/jtd-schema.d.ts","../../node_modules/ajv/dist/runtime/validation_error.d.ts","../../node_modules/ajv/dist/compile/ref_error.d.ts","../../node_modules/ajv/dist/core.d.ts","../../node_modules/ajv/dist/compile/resolve.d.ts","../../node_modules/ajv/dist/compile/index.d.ts","../../node_modules/ajv/dist/types/index.d.ts","../../node_modules/ajv/dist/ajv.d.ts","../../node_modules/ajv/dist/vocabularies/jtd/error.d.ts","../../node_modules/ajv/dist/vocabularies/jtd/type.d.ts","../../node_modules/ajv/dist/vocabularies/jtd/enum.d.ts","../../node_modules/ajv/dist/vocabularies/jtd/elements.d.ts","../../node_modules/ajv/dist/vocabularies/jtd/properties.d.ts","../../node_modules/ajv/dist/vocabularies/jtd/discriminator.d.ts","../../node_modules/ajv/dist/vocabularies/jtd/values.d.ts","../../node_modules/ajv/dist/vocabularies/jtd/index.d.ts","../../node_modules/ajv/dist/jtd.d.ts","../../node_modules/@fastify/ajv-compiler/types/index.d.ts","../../node_modules/@fastify/error/types/index.d.ts","../../node_modules/fast-json-stringify/types/index.d.ts","../../node_modules/@fastify/fast-json-stringify-compiler/types/index.d.ts","../../node_modules/find-my-way/index.d.ts","../../node_modules/light-my-request/types/index.d.ts","../../node_modules/fastify/types/utils.d.ts","../../node_modules/fastify/types/schema.d.ts","../../node_modules/fastify/types/type-provider.d.ts","../../node_modules/fastify/types/reply.d.ts","../../node_modules/@types/node/compatibility/disposable.d.ts","../../node_modules/@types/node/compatibility/indexable.d.ts","../../node_modules/@types/node/compatibility/iterators.d.ts","../../node_modules/@types/node/compatibility/index.d.ts","../../node_modules/@types/node/globals.typedarray.d.ts","../../node_modules/@types/node/buffer.buffer.d.ts","../../node_modules/@types/node/globals.d.ts","../../node_modules/@types/node/web-globals/abortcontroller.d.ts","../../node_modules/@types/node/web-globals/domexception.d.ts","../../node_modules/@types/node/web-globals/events.d.ts","../../node_modules/buffer/index.d.ts","../../node_modules/undici-types/header.d.ts","../../node_modules/undici-types/readable.d.ts","../../node_modules/undici-types/file.d.ts","../../node_modules/undici-types/fetch.d.ts","../../node_modules/undici-types/formdata.d.ts","../../node_modules/undici-types/connector.d.ts","../../node_modules/undici-types/client.d.ts","../../node_modules/undici-types/errors.d.ts","../../node_modules/undici-types/dispatcher.d.ts","../../node_modules/undici-types/global-dispatcher.d.ts","../../node_modules/undici-types/global-origin.d.ts","../../node_modules/undici-types/pool-stats.d.ts","../../node_modules/undici-types/pool.d.ts","../../node_modules/undici-types/handlers.d.ts","../../node_modules/undici-types/balanced-pool.d.ts","../../node_modules/undici-types/agent.d.ts","../../node_modules/undici-types/mock-interceptor.d.ts","../../node_modules/undici-types/mock-agent.d.ts","../../node_modules/undici-types/mock-client.d.ts","../../node_modules/undici-types/mock-pool.d.ts","../../node_modules/undici-types/mock-errors.d.ts","../../node_modules/undici-types/proxy-agent.d.ts","../../node_modules/undici-types/env-http-proxy-agent.d.ts","../../node_modules/undici-types/retry-handler.d.ts","../../node_modules/undici-types/retry-agent.d.ts","../../node_modules/undici-types/api.d.ts","../../node_modules/undici-types/interceptors.d.ts","../../node_modules/undici-types/util.d.ts","../../node_modules/undici-types/cookies.d.ts","../../node_modules/undici-types/patch.d.ts","../../node_modules/undici-types/websocket.d.ts","../../node_modules/undici-types/eventsource.d.ts","../../node_modules/undici-types/filereader.d.ts","../../node_modules/undici-types/diagnostics-channel.d.ts","../../node_modules/undici-types/content-type.d.ts","../../node_modules/undici-types/cache.d.ts","../../node_modules/undici-types/index.d.ts","../../node_modules/@types/node/web-globals/fetch.d.ts","../../node_modules/@types/node/web-globals/navigator.d.ts","../../node_modules/@types/node/web-globals/storage.d.ts","../../node_modules/@types/node/assert.d.ts","../../node_modules/@types/node/assert/strict.d.ts","../../node_modules/@types/node/async_hooks.d.ts","../../node_modules/@types/node/buffer.d.ts","../../node_modules/@types/node/child_process.d.ts","../../node_modules/@types/node/cluster.d.ts","../../node_modules/@types/node/console.d.ts","../../node_modules/@types/node/constants.d.ts","../../node_modules/@types/node/crypto.d.ts","../../node_modules/@types/node/dgram.d.ts","../../node_modules/@types/node/diagnostics_channel.d.ts","../../node_modules/@types/node/dns.d.ts","../../node_modules/@types/node/dns/promises.d.ts","../../node_modules/@types/node/domain.d.ts","../../node_modules/@types/node/events.d.ts","../../node_modules/@types/node/fs.d.ts","../../node_modules/@types/node/fs/promises.d.ts","../../node_modules/@types/node/http.d.ts","../../node_modules/@types/node/http2.d.ts","../../node_modules/@types/node/https.d.ts","../../node_modules/@types/node/inspector.d.ts","../../node_modules/@types/node/inspector.generated.d.ts","../../node_modules/@types/node/module.d.ts","../../node_modules/@types/node/net.d.ts","../../node_modules/@types/node/os.d.ts","../../node_modules/@types/node/path.d.ts","../../node_modules/@types/node/perf_hooks.d.ts","../../node_modules/@types/node/process.d.ts","../../node_modules/@types/node/punycode.d.ts","../../node_modules/@types/node/querystring.d.ts","../../node_modules/@types/node/readline.d.ts","../../node_modules/@types/node/readline/promises.d.ts","../../node_modules/@types/node/repl.d.ts","../../node_modules/@types/node/sea.d.ts","../../node_modules/@types/node/sqlite.d.ts","../../node_modules/@types/node/stream.d.ts","../../node_modules/@types/node/stream/promises.d.ts","../../node_modules/@types/node/stream/consumers.d.ts","../../node_modules/@types/node/stream/web.d.ts","../../node_modules/@types/node/string_decoder.d.ts","../../node_modules/@types/node/test.d.ts","../../node_modules/@types/node/timers.d.ts","../../node_modules/@types/node/timers/promises.d.ts","../../node_modules/@types/node/tls.d.ts","../../node_modules/@types/node/trace_events.d.ts","../../node_modules/@types/node/tty.d.ts","../../node_modules/@types/node/url.d.ts","../../node_modules/@types/node/util.d.ts","../../node_modules/@types/node/v8.d.ts","../../node_modules/@types/node/vm.d.ts","../../node_modules/@types/node/wasi.d.ts","../../node_modules/@types/node/worker_threads.d.ts","../../node_modules/@types/node/zlib.d.ts","../../node_modules/@types/node/index.d.ts","../../node_modules/pino-std-serializers/index.d.ts","../../node_modules/sonic-boom/types/index.d.ts","../../node_modules/thread-stream/index.d.ts","../../node_modules/pino/pino.d.ts","../../node_modules/fastify/types/logger.d.ts","../../node_modules/fastify/types/plugin.d.ts","../../node_modules/fastify/types/register.d.ts","../../node_modules/fastify/types/instance.d.ts","../../node_modules/fastify/types/hooks.d.ts","../../node_modules/fastify/types/route.d.ts","../../node_modules/fastify/types/context.d.ts","../../node_modules/fastify/types/request.d.ts","../../node_modules/fastify/types/content-type-parser.d.ts","../../node_modules/fastify/types/errors.d.ts","../../node_modules/fastify/types/server-factory.d.ts","../../node_modules/fastify/fastify.d.ts","../../node_modules/@fastify/cookie/types/plugin.d.ts","../../node_modules/@types/ws/index.d.ts","../../node_modules/@fastify/websocket/types/index.d.ts","../../node_modules/@fastify/static/types/index.d.ts","./src/config.ts","./src/db/index.ts","./src/auth/service.ts","../../node_modules/@homebridge/node-pty-prebuilt-multiarch/typings/node-pty.d.ts","../shared/dist/protocol.d.ts","../shared/dist/api.d.ts","../shared/dist/index.d.ts","./src/core/ring-buffer.ts","./src/core/claude-launcher.ts","./src/core/pty-manager.ts","./src/routes/auth.ts","./src/routes/sessions.ts","../../node_modules/@types/ws/index.d.mts","./src/ws/gateway.ts","./src/app.ts","./src/index.ts"],"fileIdsList":[[102,106,115,131,180,197,198],[131,180,197,198,230,246,249,250,265],[131,180,197,198],[118,131,180,197,198],[131,180,192,197,198,230,246,247,249,265],[125,131,180,194,197,198,230,239,240,246,247,248,250,265],[131,177,178,180,197,198],[131,179,180,197,198],[180,197,198],[131,180,185,197,198,215],[131,180,181,186,191,197,198,200,212,223],[131,180,181,182,191,197,198,200],[126,127,128,131,180,197,198],[131,180,183,197,198,224],[131,180,184,185,192,197,198,201],[131,180,185,197,198,212,220],[131,180,186,188,191,197,198,200],[131,179,180,187,197,198],[131,180,188,189,197,198],[131,180,190,191,197,198],[131,179,180,191,197,198],[131,180,191,192,193,197,198,212,223],[131,180,191,192,193,197,198,207,212,215],[131,173,180,188,191,194,197,198,200,212,223],[131,180,191,192,194,195,197,198,200,212,220,223],[131,180,194,196,197,198,212,220,223],[129,130,131,132,133,134,135,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229],[131,180,191,197,198],[131,180,197,198,199,223],[131,180,188,191,197,198,200,212],[131,180,197,198,201],[131,180,197,198,202],[131,179,180,197,198,203],[131,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229],[131,180,197,198,205],[131,180,197,198,206],[131,180,191,197,198,207,208],[131,180,197,198,207,209,224,226],[131,180,192,197,198],[131,180,191,197,198,212,213,215],[131,180,197,198,214,215],[131,180,197,198,212,213],[131,180,197,198,215],[131,180,197,198,216],[131,177,180,197,198,212,217,223],[131,180,191,197,198,218,219],[131,180,197,198,218,219],[131,180,185,197,198,200,212,220],[131,180,197,198,221],[131,180,197,198,200,222],[131,180,194,197,198,206,223],[131,180,185,197,198,224],[131,180,197,198,212,225],[131,180,197,198,199,226],[131,180,197,198,227],[131,173,180,197,198],[131,173,180,191,193,197,198,203,212,215,223,225,226,228],[131,180,197,198,212,229],[131,180,191,194,196,197,198,200,212,220,223,229,230],[65,66,70,97,98,100,101,102,104,105,131,180,197,198],[63,64,131,180,197,198],[63,131,180,197,198],[65,105,131,180,197,198],[65,66,102,103,105,131,180,197,198],[105,131,180,197,198],[62,105,106,131,180,197,198],[65,66,104,105,131,180,197,198],[65,66,68,69,104,105,131,180,197,198],[65,66,67,104,105,131,180,197,198],[65,66,70,97,98,99,100,101,104,105,131,180,197,198],[65,70,99,100,101,102,104,105,114,131,180,197,198],[62,65,66,70,102,104,131,180,197,198],[70,105,131,180,197,198],[72,73,74,75,76,77,78,79,80,81,105,131,180,197,198],[95,105,131,180,197,198],[71,82,90,91,92,93,94,96,131,180,197,198],[95,105,107,131,180,197,198],[105,107,131,180,197,198],[105,108,109,110,111,112,113,131,180,197,198],[70,105,107,131,180,197,198],[75,105,131,180,197,198],[83,84,85,86,87,88,89,105,131,180,197,198],[106,131,180,197,198],[116,117,119,120,121,122,123,124,125,131,180,194,195,196,197,198,200,235,236,237,238,239,240,241,242,243,244,245],[122,123,124,131,180,197,198,240,242],[122,131,180,197,198,240],[117,131,180,197,198],[117,122,123,124,125,131,180,197,198,212,235,236,237,238,240,242],[117,120,121,122,123,124,125,131,180,194,197,198,200,235,237,239,240,242,243,246],[117,122,123,124,125,131,180,197,198,234,238,240,242],[122,124,131,180,197,198,235,238],[122,131,180,197,198,235,236,238,246],[122,123,124,131,180,197,198,235,238,240,242],[116,122,123,124,131,180,197,198,235,238,240,241],[117,120,122,123,124,125,131,180,197,198,235,238,239,241,242],[116,119,131,180,197,198,246],[122,131,180,194,195,196,197,198],[122,123,131,180,197,198,240],[131,180,194,195,196,197,198],[131,180,194,195,197,198],[131,180,194,197,198,212],[131,180,194,197,198,230],[131,180,191,197,198,228,231,232,233],[131,180,191,197,198,230],[131,180,191,197,198,228],[131,145,149,180,197,198,223],[131,145,180,197,198,212,223],[131,140,180,197,198],[131,142,145,180,197,198,220,223],[131,180,197,198,200,220],[131,180,197,198,230],[131,140,180,197,198,230],[131,142,145,180,197,198,200,223],[131,137,138,141,144,180,191,197,198,212,223],[131,145,152,180,197,198],[131,137,143,180,197,198],[131,145,166,167,180,197,198],[131,141,145,180,197,198,215,223,230],[131,166,180,197,198,230],[131,139,140,180,197,198,230],[131,145,180,197,198],[131,139,140,141,142,143,144,145,146,147,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,167,168,169,170,171,172,180,197,198],[131,145,160,180,197,198],[131,145,152,153,180,197,198],[131,143,145,153,154,180,197,198],[131,144,180,197,198],[131,137,140,145,180,197,198],[131,145,149,153,154,180,197,198],[131,149,180,197,198],[131,143,145,148,180,197,198,223],[131,137,142,145,152,180,197,198],[131,180,197,198,212],[131,140,145,166,180,197,198,228,230],[131,180,192,197,198,202,223,246,247,249,250,251,252,253,260,261,262,264],[131,180,185,197,198,252],[131,180,192,197,198,201,202,224],[131,180,181,197,198],[131,180,185,191,192,197,198,252,254,257,258,259],[131,180,197,198,211],[131,180,192,197,198,202,223,251,252,265],[131,180,197,198,246,253,257],[131,180,197,198,246,257,260],[131,180,197,198,246,257,260,263],[131,180,197,198,255],[131,180,197,198,255,256]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"df83c2a6c73228b625b0beb6669c7ee2a09c914637e2d35170723ad49c0f5cd4","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"84bcc7c6b06f4d643a55dc63b56be0c81d990f8d549b66ea615c553268774dc3","impliedFormat":1},{"version":"2d225e7bda2871c066a7079c88174340950fb604f624f2586d3ea27bb9e5f4ff","impliedFormat":1},{"version":"6a785f84e63234035e511817dd48ada756d984dd8f9344e56eb8b2bdcd8fd001","impliedFormat":1},{"version":"c1422d016f7df2ccd3594c06f2923199acd09898f2c42f50ea8159f1f856f618","impliedFormat":1},{"version":"2973b1b7857ca144251375b97f98474e9847a890331e27132d5a8b3aea9350a8","impliedFormat":1},{"version":"0eb6152d37c84d6119295493dfcc20c331c6fda1304a513d159cdaa599dcb78b","impliedFormat":1},{"version":"237df26f8c326ca00cd9d2deb40214a079749062156386b6d75bdcecc6988a6b","impliedFormat":1},{"version":"cd44995ee13d5d23df17a10213fed7b483fabfd5ea08f267ab52c07ce0b6b4da","impliedFormat":1},{"version":"58ce1486f851942bd2d3056b399079bc9cb978ec933fe9833ea417e33eab676e","impliedFormat":1},{"version":"7557d4d7f19f94341f4413575a3453ba7f6039c9591015bcf4282a8e75414043","impliedFormat":1},{"version":"a3b2cc16f3ce2d882eca44e1066f57a24751545f2a5e4a153d4de31b4cac9bb5","impliedFormat":1},{"version":"ac2b3b377d3068bfb6e1cb8889c99098f2c875955e2325315991882a74d92cc8","impliedFormat":1},{"version":"8deb39d89095469957f73bd194d11f01d9894b8c1f1e27fbf3f6e8122576b336","impliedFormat":1},{"version":"a38a9c41f433b608a0d37e645a31eecf7233ef3d3fffeb626988d3219f80e32f","impliedFormat":1},{"version":"8e1428dcba6a984489863935049893631170a37f9584c0479f06e1a5b1f04332","impliedFormat":1},{"version":"1fce9ecb87a2d3898941c60df617e52e50fb0c03c9b7b2ba8381972448327285","impliedFormat":1},{"version":"5ef0597b8238443908b2c4bf69149ed3894ac0ddd0515ac583d38c7595b151f1","impliedFormat":1},{"version":"ac52b775a80badff5f4ac329c5725a26bd5aaadd57afa7ad9e98b4844767312a","impliedFormat":1},{"version":"6ae5b4a63010c82bf2522b4ecfc29ffe6a8b0c5eea6b2b35120077e9ac54d7a1","impliedFormat":1},{"version":"dd7109c49f416f218915921d44f0f28975df78e04e437c62e1e1eb3be5e18a35","impliedFormat":1},{"version":"eee181112e420b345fc78422a6cc32385ede3d27e2eaf8b8c4ad8b2c29e3e52e","impliedFormat":1},{"version":"25fbe57c8ee3079e2201fe580578fab4f3a78881c98865b7c96233af00bf9624","impliedFormat":1},{"version":"62cc8477858487b4c4de7d7ae5e745a8ce0015c1592f398b63ee05d6e64ca295","impliedFormat":1},{"version":"cc2a9ec3cb10e4c0b8738b02c31798fad312d21ef20b6a2f5be1d077e9f5409d","impliedFormat":1},{"version":"4b4fadcda7d34034737598c07e2dca5d7e1e633cb3ba8dd4d2e6a7782b30b296","impliedFormat":1},{"version":"360fdc8829a51c5428636f1f83e7db36fef6c5a15ed4411b582d00a1c2bd6e97","impliedFormat":1},{"version":"1cf0d15e6ab1ecabbf329b906ae8543e6b8955133b7f6655f04d433e3a0597ab","impliedFormat":1},{"version":"7c9f98fe812643141502b30fb2b5ec56d16aaf94f98580276ae37b7924dd44a4","impliedFormat":1},{"version":"b3547893f24f59d0a644c52f55901b15a3fa1a115bc5ea9a582911469b9348b7","impliedFormat":1},{"version":"596e5b88b6ca8399076afcc22af6e6e0c4700c7cd1f420a78d637c3fb44a885e","impliedFormat":1},{"version":"adddf736e08132c7059ee572b128fdacb1c2650ace80d0f582e93d097ed4fbaf","impliedFormat":1},{"version":"d4cad9dc13e9c5348637170ddd5d95f7ed5fdfc856ddca40234fa55518bc99a6","impliedFormat":1},{"version":"d70675ba7ba7d02e52b7070a369957a70827e4b2bca2c1680c38a832e87b61fd","impliedFormat":1},{"version":"3be71f4ce8988a01e2f5368bdd58e1d60236baf511e4510ee9291c7b3729a27e","impliedFormat":1},{"version":"423d2ccc38e369a7527988d682fafc40267bcd6688a7473e59c5eea20a29b64f","impliedFormat":1},{"version":"2f9fde0868ed030277c678b435f63fcf03d27c04301299580a4017963cc04ce6","impliedFormat":1},{"version":"feeb73d48cc41c6dd23d17473521b0af877751504c30c18dc84267c8eeea429a","impliedFormat":1},{"version":"25f1159094dc0bf3a71313a74e0885426af21c5d6564a254004f2cadf9c5b052","impliedFormat":1},{"version":"cde493e09daad4bb29922fe633f760be9f0e8e2f39cdca999cce3b8690b5e13a","impliedFormat":1},{"version":"3d7f9eb12aface876f7b535cc89dcd416daf77f0b3573333f16ec0a70bcf902a","impliedFormat":1},{"version":"4304f640f7cb4724ea82441accb7c7607fa7207541182470d625adda99b2900b","impliedFormat":1},{"version":"e0205f04611bea8b5b82168065b8ef1476a8e96236201494eb8c785331c43118","impliedFormat":1},{"version":"62d26d8ba4fa15ab425c1b57a050ed76c5b0ecbffaa53f182110aa3a02405a07","impliedFormat":1},{"version":"9941cbf7ca695e95d588f5f1692ab040b078d44a95d231fa9a8f828186b7b77d","impliedFormat":1},{"version":"41b8775befd7ded7245a627e9f4de6110236688ce4c124d2d40c37bc1a3bfe05","impliedFormat":1},{"version":"a6613ee552418429af38391e37389036654a882c342a1b81f2711e8ddac597f2","impliedFormat":1},{"version":"da47cb979ae4a849f9b983f43ef34365b7050c4f5ae2ebf818195858774e1d67","impliedFormat":1},{"version":"ac3bcb82d7280fc313a967f311764258d18caf33db6d2b1a0243cde607ff01a0","impliedFormat":1},{"version":"c9b5632d6665177030428d02603aeac3e920d31ec83ac500b55d44c7da74bd84","impliedFormat":1},{"version":"46456824df16d60f243a7e386562b27bac838aaba66050b9bc0f31e1ab34c1f2","impliedFormat":1},{"version":"b91034069e217212d8dda6c92669ee9f180b4c36273b5244c3be2c657f9286c7","impliedFormat":1},{"version":"0697277dd829ac2610d68fe1b457c9e758105bb52d40e149d9c15e5e2fe6dca4","impliedFormat":1},{"version":"b0d06dbb409369169143ede5df1fb58b2fca8d44588e199bd624b6f6d966bf08","impliedFormat":1},{"version":"88dfdb2a44912a28aea3ebb657dc7fcec6ba59f7233005e3405824995b713dac","impliedFormat":1},{"version":"f38a45cbc0356dfb6455211e75ecd0f8aeaedbacd4cb8d6646c818d36a46d195","impliedFormat":1},{"version":"cc2d5d5687bdf9d7c49b6946b8769ac7abcbdcd1701d9bb9ca70a8bc1b003e8b","impliedFormat":1},{"version":"6f1fabd39b8c9a66a3232030a4b28ed4fb4f857dcffef0add3220dab4bbba77a","impliedFormat":1},{"version":"9c0623d67471ddc5b9d82b4e06252c746d54f7ae8ccff8701cd51c249f7e7694","impliedFormat":1},{"version":"71654f68a41362f16a2690ec215e583d3cfa913d49327e43c3c7f354022f67a3","impliedFormat":1},{"version":"f69b484edf398d636992757d587e7e38ea91844a66dbca9d682c9cf7858b77cf","impliedFormat":1},{"version":"37d852b3e6b30b974178674dbf2a7974a1ea4bbdbec26d0bdb8f34632cab94a2","impliedFormat":1},{"version":"c3ccf44723b474b5d7a87c53eed9711e81422f0e29b3e8f8f8d42775fa802243","impliedFormat":1},{"version":"faeaf9586ba4505448bd0fdb332509a7e3ecca5d8ae10208950a5d03e8b4ea28","impliedFormat":1},{"version":"c3a0295707fde0cb51ca784ecb799aa95ca71c40337b5ee4a81a8f7134dd7e92","impliedFormat":1},{"version":"6c7176368037af28cb72f2392010fa1cef295d6d6744bca8cfb54985f3a18c3e","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"437e20f2ba32abaeb7985e0afe0002de1917bc74e949ba585e49feba65da6ca1","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"3af97acf03cc97de58a3a4bc91f8f616408099bc4233f6d0852e72a8ffb91ac9","affectsGlobalScope":true,"impliedFormat":1},{"version":"808069bba06b6768b62fd22429b53362e7af342da4a236ed2d2e1c89fcca3b4a","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e9c23ba78aabc2e0a27033f18737a6df754067731e69dc5f52823957d60a4b6","impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"f9501cc13ce624c72b61f12b3963e84fad210fbdf0ffbc4590e08460a3f04eba","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0fa06ada475b910e2106c98c68b10483dc8811d0c14a8a8dd36efb2672485b29","impliedFormat":1},{"version":"33e5e9aba62c3193d10d1d33ae1fa75c46a1171cf76fef750777377d53b0303f","impliedFormat":1},{"version":"2b06b93fd01bcd49d1a6bd1f9b65ddcae6480b9a86e9061634d6f8e354c1468f","impliedFormat":1},{"version":"6a0cd27e5dc2cfbe039e731cf879d12b0e2dded06d1b1dedad07f7712de0d7f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"13f5c844119c43e51ce777c509267f14d6aaf31eafb2c2b002ca35584cd13b29","impliedFormat":1},{"version":"e60477649d6ad21542bd2dc7e3d9ff6853d0797ba9f689ba2f6653818999c264","impliedFormat":1},{"version":"c2510f124c0293ab80b1777c44d80f812b75612f297b9857406468c0f4dafe29","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"4c829ab315f57c5442c6667b53769975acbf92003a66aef19bce151987675bd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"b2ade7657e2db96d18315694789eff2ddd3d8aea7215b181f8a0b303277cc579","impliedFormat":1},{"version":"9855e02d837744303391e5623a531734443a5f8e6e8755e018c41d63ad797db2","impliedFormat":1},{"version":"4d631b81fa2f07a0e63a9a143d6a82c25c5f051298651a9b69176ba28930756d","impliedFormat":1},{"version":"836a356aae992ff3c28a0212e3eabcb76dd4b0cc06bcb9607aeef560661b860d","impliedFormat":1},{"version":"1e0d1f8b0adfa0b0330e028c7941b5a98c08b600efe7f14d2d2a00854fb2f393","impliedFormat":1},{"version":"41670ee38943d9cbb4924e436f56fc19ee94232bc96108562de1a734af20dc2c","affectsGlobalScope":true,"impliedFormat":1},{"version":"c906fb15bd2aabc9ed1e3f44eb6a8661199d6c320b3aa196b826121552cb3695","impliedFormat":1},{"version":"22295e8103f1d6d8ea4b5d6211e43421fe4564e34d0dd8e09e520e452d89e659","impliedFormat":1},{"version":"58647d85d0f722a1ce9de50955df60a7489f0593bf1a7015521efe901c06d770","impliedFormat":1},{"version":"9018e9493596e2d041fc531f5101a0d05f9d2e1311f4ae3c74345b1766158c08","impliedFormat":1},{"version":"a10f0e1854f3316d7ee437b79649e5a6ae3ae14ffe6322b02d4987071a95362e","impliedFormat":1},{"version":"e208f73ef6a980104304b0d2ca5f6bf1b85de6009d2c7e404028b875020fa8f2","impliedFormat":1},{"version":"d163b6bc2372b4f07260747cbc6c0a6405ab3fbcea3852305e98ac43ca59f5bc","impliedFormat":1},{"version":"e6fa9ad47c5f71ff733744a029d1dc472c618de53804eae08ffc243b936f87ff","affectsGlobalScope":true,"impliedFormat":1},{"version":"a6f137d651076822d4fe884287e68fd61785a0d3d1fdb250a5059b691fa897db","impliedFormat":1},{"version":"24826ed94a78d5c64bd857570fdbd96229ad41b5cb654c08d75a9845e3ab7dde","impliedFormat":1},{"version":"8b479a130ccb62e98f11f136d3ac80f2984fdc07616516d29881f3061f2dd472","impliedFormat":1},{"version":"928af3d90454bf656a52a48679f199f64c1435247d6189d1caf4c68f2eaf921f","affectsGlobalScope":true,"impliedFormat":1},{"version":"bceb58df66ab8fb00170df20cd813978c5ab84be1d285710c4eb005d8e9d8efb","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f16a7e4deafa527ed9995a772bb380eb7d3c2c0fd4ae178c5263ed18394db2c","impliedFormat":1},{"version":"933921f0bb0ec12ef45d1062a1fc0f27635318f4d294e4d99de9a5493e618ca2","impliedFormat":1},{"version":"71a0f3ad612c123b57239a7749770017ecfe6b66411488000aba83e4546fde25","impliedFormat":1},{"version":"77fbe5eecb6fac4b6242bbf6eebfc43e98ce5ccba8fa44e0ef6a95c945ff4d98","impliedFormat":1},{"version":"4f9d8ca0c417b67b69eeb54c7ca1bedd7b56034bb9bfd27c5d4f3bc4692daca7","impliedFormat":1},{"version":"814118df420c4e38fe5ae1b9a3bafb6e9c2aa40838e528cde908381867be6466","impliedFormat":1},{"version":"a3fc63c0d7b031693f665f5494412ba4b551fe644ededccc0ab5922401079c95","impliedFormat":1},{"version":"80523c00b8544a2000ae0143e4a90a00b47f99823eb7926c1e03c494216fc363","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"746911b62b329587939560deb5c036aca48aece03147b021fa680223255d5183","affectsGlobalScope":true,"impliedFormat":1},{"version":"18fd40412d102c5564136f29735e5d1c3b455b8a37f920da79561f1fde068208","impliedFormat":1},{"version":"c8d3e5a18ba35629954e48c4cc8f11dc88224650067a172685c736b27a34a4dc","impliedFormat":1},{"version":"f0be1b8078cd549d91f37c30c222c2a187ac1cf981d994fb476a1adc61387b14","affectsGlobalScope":true,"impliedFormat":1},{"version":"0aaed1d72199b01234152f7a60046bc947f1f37d78d182e9ae09c4289e06a592","impliedFormat":1},{"version":"2b55d426ff2b9087485e52ac4bc7cfafe1dc420fc76dad926cd46526567c501a","impliedFormat":1},{"version":"66ba1b2c3e3a3644a1011cd530fb444a96b1b2dfe2f5e837a002d41a1a799e60","impliedFormat":1},{"version":"7e514f5b852fdbc166b539fdd1f4e9114f29911592a5eb10a94bb3a13ccac3c4","impliedFormat":1},{"version":"5b7aa3c4c1a5d81b411e8cb302b45507fea9358d3569196b27eb1a27ae3a90ef","affectsGlobalScope":true,"impliedFormat":1},{"version":"5987a903da92c7462e0b35704ce7da94d7fdc4b89a984871c0e2b87a8aae9e69","affectsGlobalScope":true,"impliedFormat":1},{"version":"ea08a0345023ade2b47fbff5a76d0d0ed8bff10bc9d22b83f40858a8e941501c","impliedFormat":1},{"version":"47613031a5a31510831304405af561b0ffaedb734437c595256bb61a90f9311b","impliedFormat":1},{"version":"ae062ce7d9510060c5d7e7952ae379224fb3f8f2dd74e88959878af2057c143b","impliedFormat":1},{"version":"8a1a0d0a4a06a8d278947fcb66bf684f117bf147f89b06e50662d79a53be3e9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"358765d5ea8afd285d4fd1532e78b88273f18cb3f87403a9b16fef61ac9fdcfe","impliedFormat":1},{"version":"9f55299850d4f0921e79b6bf344b47c420ce0f507b9dcf593e532b09ea7eeea1","impliedFormat":1},{"version":"4fe80f12b1d5189384a219095c2eabadbb389c2d3703aae7c5376dbaa56061df","impliedFormat":1},{"version":"9eb1d2dceae65d1c82fc6be7e9b6b19cf3ca93c364678611107362b6ad4d2d41","impliedFormat":1},{"version":"eb81f582c0e1ae21f004b39c268c68ad13222cd7e72f45080d6cdf9b994dfecf","impliedFormat":1},{"version":"e40f9840e4686053a5d42b39fddfd6774172a3d12fa61c1edb8c35b452a5e74a","impliedFormat":1},{"version":"c034433fe3ebd5391d0078de0193306591f61e421697a217cbeb92bd5e0f6ebc","impliedFormat":1},{"version":"479a021fec9328c8ab37f9372dcdc1022d79aeedde6febf03942b4456d1591c9","impliedFormat":1},{"version":"68d25a0614893e256a9e6fe18e2b125dbdad694381b57e63320de6a7518e47fc","impliedFormat":1},{"version":"49b89a0b4b3b3e18cbf726a9c608d622b4f6d49304076fc01c10f93833c70f09","impliedFormat":1},{"version":"4f62dc739ef3b99a93779255f85e51f02ce411d63cf294ee654258ff8c1978e4","impliedFormat":1},{"version":"3fa13ab73c7307a308bba2f9ca5cea92b74cbdae348a7ba1f5ac094fde7286c2","impliedFormat":1},{"version":"64102e00cb41de7f423608037d17dff83954904383e5c45f1054c2246cf5e184","impliedFormat":1},{"version":"5cf7ebe883e54b764af73548dbbef8887c640f1fd896f4d8b935b027d9989728","impliedFormat":1},{"version":"05e29a500e59cc5697947ee0fa9390e88ff008ec76be1f859152bda8ec01f13d","impliedFormat":1},{"version":"c89237cc87fc6e004290ae667babd0fa242e87c7de1cb49d4e5aba53391923f4","impliedFormat":1},{"version":"44b227ad122395768f07a8f1b84041b096220335b34ff7af3b8caa61731b294d","impliedFormat":1},{"version":"cfac258f114785ee4591fed69b045070da61d3ea03a9fabd750d682cc501253f","impliedFormat":1},{"version":"99dd60ecf4b17a5478ddc7a3a4e41500eba9b4fdbded73fa01e0ad525f7ff90d","impliedFormat":1},{"version":"1ba59c8bbeed2cb75b239bb12041582fa3e8ef32f8d0bd0ec802e38442d3f317","impliedFormat":1},{"version":"1bf1497c5e7ced001b52ed69657f472870edf3ef34641842046fc5cc47359fd0","impliedFormat":1},{"version":"36c1793e93c8a2dff0dcc67145b992e78fef0a67dd1f69d9f689b108a3598341","impliedFormat":1},{"version":"289302cd21b99f1b34e25812ed3119af56bab4740f10726c7a5a7db80df69e33","signature":"51cc114c75ade7761ead7dbeb917357947a820feb3c4c7589d6fc859804b117b","impliedFormat":99},{"version":"837f71558d52d6517b050cec1800f278a85fcff00dba7029801efc79bdd5e978","signature":"e1f8eff69d223ca308faac04131a51f40a811ad66005ea8553acd5b0a7884273","impliedFormat":99},{"version":"44374830e8530b42058a2b9a713f0e94b0171cd2887b53a19343ae80d6b0230b","signature":"b2389a3050e376a1e5e258f76d72c5c8baf0a80bc992c9ba65c263f9257c08a8","impliedFormat":99},{"version":"e2b0a3b2ab5fa5a3f31d8150ab3b93f29828131871a898379b117a16655888b0","impliedFormat":1},{"version":"155aa8c32f0815cb7f44246680859e2724428459727e6067d0d434a666b72e5b","impliedFormat":99},{"version":"0eaa5ca815f0d1f498a205737cc71a5259671ee1ffc96eb881ee721c2d60bf30","impliedFormat":99},{"version":"8b93a53483cf3bb55c754db9dffd3cc1e3d05f87fe7158aceb4b30f5521a14fc","impliedFormat":99},{"version":"94df841d67dcf657c26c5aa60fe3b3a9daca4bd156baae524d5a4ecd9e90b401","signature":"1ddb6de962eac59d919fc52f07ce8b3c0af0c61da427b94b684d925c6ad6eb71","impliedFormat":99},{"version":"9c1e42f6390d8a66639fdead79e7cf2616f7708c2d8dba1dbf8db158ffa4d63c","signature":"d8396e56d160d98eb7f4fe538cb89f7db9345afa65563b3c7c1e030f6cee1bb5","impliedFormat":99},{"version":"f38ed4bcd27c8054ccbfca5c93217ffc805f11c9421a9867851f8d77ea9525f1","signature":"b00d1b92b69461390eaecc1805d508328e6e1238c490bb4fc7d8f95dba9150c2","impliedFormat":99},{"version":"58089e4ae3cf5595f123796153c3852ab492073a464cf16875ca6f51dd2b89e7","signature":"9a427255affacd5f9393e9540bcdea7258ad97021bde8044493990e8dd2fd2ab","impliedFormat":99},{"version":"0aa5e375691619ba68ba72383a1641d5e8cb6781903b6cb714233a7a04f82787","signature":"d036d9eed1f11cdee9a3bb9a4ffe86cbeee21d03500f6c42de7219d53b660fa4","impliedFormat":99},{"version":"316f1486e15cbf7896425f0a16dfe12d447dd57cfb3244b8b119c77df870858f","impliedFormat":99},{"version":"303ab2a1b823b7ee483a89ab4ae9d720dc94a65886f6f0430577c63dfefb78f0","signature":"7f23a8041aff693c3a2cf7ff178e0f514a9dfa7d565e80fe5113ab60a575884e","impliedFormat":99},{"version":"c72437f83e5f35d3c6bc42889a22438fa8cb3f837d3a75e12bd45c7010f109e0","signature":"3c903ff9ca7961c70214302108a0dd27044b7130b8d551ac891744fdb4bee60d","impliedFormat":99},{"version":"b0fef4d6b65da4b34876e3dc008d9b157c4f094a17855fcff92a18464c677179","signature":"43e818adf60173644896298637f47b01d5819b17eda46eaa32d0c7d64724d012","impliedFormat":99}],"root":[[251,253],[258,262],[264,266]],"options":{"composite":true,"declaration":true,"declarationMap":true,"esModuleInterop":true,"exactOptionalPropertyTypes":true,"module":199,"noImplicitOverride":true,"noUncheckedIndexedAccess":true,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"sourceMap":true,"strict":true,"target":10,"verbatimModuleSyntax":true},"referencedMap":[[116,1],[247,2],[117,3],[119,4],[250,5],[249,6],[254,3],[177,7],[178,7],[179,8],[131,9],[180,10],[181,11],[182,12],[126,3],[129,13],[127,3],[128,3],[183,14],[184,15],[185,16],[186,17],[187,18],[188,19],[189,19],[190,20],[191,21],[192,22],[193,23],[132,3],[130,3],[194,24],[195,25],[196,26],[230,27],[197,28],[198,3],[199,29],[200,30],[201,31],[202,32],[203,33],[204,34],[205,35],[206,36],[207,37],[208,37],[209,38],[210,3],[211,39],[212,40],[214,41],[213,42],[215,43],[216,44],[217,45],[218,46],[219,47],[220,48],[221,49],[222,50],[223,51],[224,52],[225,53],[226,54],[227,55],[133,3],[134,3],[135,3],[174,56],[175,3],[176,3],[228,57],[229,58],[263,59],[248,59],[106,60],[63,3],[65,61],[64,62],[69,63],[104,64],[101,65],[103,66],[66,65],[67,67],[71,67],[70,68],[68,69],[102,70],[115,71],[100,65],[105,72],[98,3],[99,3],[72,73],[77,65],[79,65],[74,65],[75,73],[81,65],[82,74],[73,65],[78,65],[80,65],[76,65],[96,75],[95,65],[97,76],[91,65],[112,77],[110,78],[109,65],[107,63],[114,79],[111,80],[108,78],[113,78],[93,65],[92,65],[88,65],[94,81],[89,65],[90,82],[83,65],[84,65],[85,65],[86,65],[87,65],[136,3],[118,83],[62,3],[246,84],[243,85],[241,86],[244,87],[239,88],[238,89],[235,90],[236,91],[237,92],[125,93],[242,94],[240,95],[123,96],[245,97],[124,98],[122,99],[120,100],[121,101],[231,102],[234,103],[232,104],[233,105],[60,3],[61,3],[12,3],[11,3],[2,3],[13,3],[14,3],[15,3],[16,3],[17,3],[18,3],[19,3],[20,3],[3,3],[21,3],[22,3],[4,3],[23,3],[27,3],[24,3],[25,3],[26,3],[28,3],[29,3],[30,3],[5,3],[31,3],[32,3],[33,3],[34,3],[6,3],[38,3],[35,3],[36,3],[37,3],[39,3],[7,3],[40,3],[45,3],[46,3],[41,3],[42,3],[43,3],[44,3],[8,3],[50,3],[47,3],[48,3],[49,3],[51,3],[9,3],[52,3],[53,3],[54,3],[56,3],[55,3],[57,3],[58,3],[10,3],[59,3],[1,3],[152,106],[162,107],[151,106],[172,108],[143,109],[142,110],[171,111],[165,112],[170,113],[145,114],[159,115],[144,116],[168,117],[140,118],[139,111],[169,119],[141,120],[146,121],[147,3],[150,121],[137,3],[173,122],[163,123],[154,124],[155,125],[157,126],[153,127],[156,128],[166,111],[148,129],[149,130],[158,131],[138,132],[161,123],[160,121],[164,3],[167,133],[265,134],[253,135],[251,136],[259,137],[260,138],[258,3],[252,139],[266,140],[261,141],[262,142],[264,143],[256,144],[257,145],[255,3]],"latestChangedDtsFile":"./dist/index.d.ts","version":"5.9.3"}
\ No newline at end of file
diff --git a/packages/shared/src/protocol.ts b/packages/shared/src/protocol.ts
index 3455aa4..3f4a069 100644
--- a/packages/shared/src/protocol.ts
+++ b/packages/shared/src/protocol.ts
@@ -34,9 +34,15 @@ export function decodeBinaryFrame(data: Uint8Array): { type: number; channel: nu
}
// ---- Flow control (pattern officiel xterm.js, watermarks du design) ----
+// INVARIANT anti-deadlock : ACK_EVERY_BYTES <= LOW_WATERMARK. Le client n'ACK
+// qu'à réception de données ; si le serveur pouvait se mettre en pause avec un
+// reliquat non-ACKé > LOW mais < pas d'ACK, plus aucun des deux ne progresserait.
+// Avec ACK_EVERY <= LOW, une fois tout le flux en vol traité, le reliquat est
+// < LOW et le serveur reprend toujours. (Complété côté client par un ACK
+// traînant débouncé.)
export const FLOW = {
/** le client ACK tous les N octets réellement traités par xterm.write */
- ACK_EVERY_BYTES: 256 * 1024,
+ ACK_EVERY_BYTES: 64 * 1024,
/** pause du PTY quand TOUS les clients interactifs dépassent HIGH */
HIGH_WATERMARK: 384 * 1024,
/** reprise quand le min repasse sous LOW */
@@ -107,11 +113,12 @@ export function parseClientMessage(raw: string): ClientMessage | null {
}
if (typeof obj !== 'object' || obj === null || typeof (obj as { type?: unknown }).type !== 'string') return null;
const m = obj as Record;
- const isU32 = (v: unknown): v is number => typeof v === 'number' && Number.isInteger(v) && v >= 0 && v <= 0xffffffff;
- const isDim = (v: unknown): v is number => typeof v === 'number' && Number.isInteger(v) && v >= 2 && v <= 1000;
+ const isU32 = (v: unknown): v is number => typeof v === 'number' && Number.isSafeInteger(v) && v >= 0 && v <= 0xffffffff;
+ const isCount = (v: unknown): v is number => typeof v === 'number' && Number.isSafeInteger(v) && v >= 0;
+ const isDim = (v: unknown): v is number => typeof v === 'number' && Number.isSafeInteger(v) && v >= 2 && v <= 1000;
switch (m.type) {
case 'hello':
- return typeof m.protocol === 'number' ? { type: 'hello', protocol: m.protocol } : null;
+ return isCount(m.protocol) ? { type: 'hello', protocol: m.protocol } : null;
case 'attach':
return typeof m.sessionId === 'string' && (m.mode === 'interactive' || m.mode === 'observer') && isDim(m.cols) && isDim(m.rows)
? { type: 'attach', sessionId: m.sessionId, mode: m.mode, cols: m.cols, rows: m.rows }
@@ -127,7 +134,7 @@ export function parseClientMessage(raw: string): ClientMessage | null {
? { type: 'resize', channel: m.channel, cols: m.cols, rows: m.rows }
: null;
case 'ack':
- return isU32(m.channel) && typeof m.bytes === 'number' && m.bytes >= 0
+ return isU32(m.channel) && isCount(m.bytes)
? { type: 'ack', channel: m.channel, bytes: m.bytes }
: null;
case 'sub':
diff --git a/packages/shared/test/flow-invariants.test.ts b/packages/shared/test/flow-invariants.test.ts
new file mode 100644
index 0000000..0d8a6d1
--- /dev/null
+++ b/packages/shared/test/flow-invariants.test.ts
@@ -0,0 +1,21 @@
+import { describe, expect, it } from 'vitest';
+import { FLOW, REPLAY_TAIL_BYTES } from '../src/index.js';
+
+describe('invariants du flow control', () => {
+ // Le client n'ACK qu'à réception de données : si le serveur pouvait se mettre
+ // en pause avec un reliquat non-ACKé > LOW mais < pas d'ACK, plus rien ne
+ // progresserait (deadlock observé en acceptation P1 avec ACK_EVERY = 256 Kio).
+ it('ACK_EVERY_BYTES <= LOW_WATERMARK (anti-deadlock)', () => {
+ expect(FLOW.ACK_EVERY_BYTES).toBeLessThanOrEqual(FLOW.LOW_WATERMARK);
+ });
+
+ it('LOW < HIGH < LAGGING, tous strictement positifs', () => {
+ expect(FLOW.LOW_WATERMARK).toBeGreaterThan(0);
+ expect(FLOW.HIGH_WATERMARK).toBeGreaterThan(FLOW.LOW_WATERMARK);
+ expect(FLOW.LAGGING_BYTES).toBeGreaterThan(FLOW.HIGH_WATERMARK);
+ });
+
+ it('le replay tient dans la fenêtre lagging (resync ne re-déclenche pas lagging)', () => {
+ expect(REPLAY_TAIL_BYTES).toBeLessThan(FLOW.LAGGING_BYTES);
+ });
+});
diff --git a/packages/shared/test/protocol.test.ts b/packages/shared/test/protocol.test.ts
new file mode 100644
index 0000000..f0d3d61
--- /dev/null
+++ b/packages/shared/test/protocol.test.ts
@@ -0,0 +1,241 @@
+import { describe, expect, it } from 'vitest';
+import {
+ BINARY_FRAME,
+ decodeBinaryFrame,
+ encodeBinaryFrame,
+ parseClientMessage,
+ type ClientMessage,
+} from '../src/protocol.js';
+
+// PRNG déterministe pour le fuzz (reproductible d'un run à l'autre)
+function mulberry32(seed: number): () => number {
+ let a = seed >>> 0;
+ return () => {
+ a = (a + 0x6d2b79f5) >>> 0;
+ let t = a;
+ t = Math.imul(t ^ (t >>> 15), t | 1);
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
+ };
+}
+
+const isU32 = (v: number): boolean => Number.isInteger(v) && v >= 0 && v <= 0xffffffff;
+const isDim = (v: number): boolean => Number.isInteger(v) && v >= 2 && v <= 1000;
+
+// Invariants que tout message accepté DOIT respecter, quel que soit l'input
+function assertInvariants(msg: ClientMessage): void {
+ switch (msg.type) {
+ case 'hello':
+ expect(typeof msg.protocol).toBe('number');
+ break;
+ case 'attach':
+ expect(typeof msg.sessionId).toBe('string');
+ expect(['interactive', 'observer']).toContain(msg.mode);
+ expect(isDim(msg.cols)).toBe(true);
+ expect(isDim(msg.rows)).toBe(true);
+ break;
+ case 'detach':
+ expect(isU32(msg.channel)).toBe(true);
+ break;
+ case 'stdin':
+ expect(isU32(msg.channel)).toBe(true);
+ expect(typeof msg.data).toBe('string');
+ expect(msg.data.length).toBeLessThanOrEqual(65536);
+ break;
+ case 'resize':
+ expect(isU32(msg.channel)).toBe(true);
+ expect(isDim(msg.cols)).toBe(true);
+ expect(isDim(msg.rows)).toBe(true);
+ break;
+ case 'ack':
+ expect(isU32(msg.channel)).toBe(true);
+ expect(typeof msg.bytes).toBe('number');
+ expect(msg.bytes).toBeGreaterThanOrEqual(0);
+ break;
+ case 'sub':
+ expect(msg.topics.every((t) => t === 'sessions')).toBe(true);
+ break;
+ case 'ping':
+ break;
+ }
+}
+
+describe('parseClientMessage — cas valides', () => {
+ it('hello', () => {
+ expect(parseClientMessage('{"type":"hello","protocol":1}')).toEqual({ type: 'hello', protocol: 1 });
+ });
+
+ it('attach avec dimensions aux bornes', () => {
+ const make = (cols: number, rows: number): string =>
+ JSON.stringify({ type: 'attach', sessionId: 's1', mode: 'interactive', cols, rows });
+ expect(parseClientMessage(make(2, 2))).toEqual({ type: 'attach', sessionId: 's1', mode: 'interactive', cols: 2, rows: 2 });
+ expect(parseClientMessage(make(1000, 1000))).toEqual({ type: 'attach', sessionId: 's1', mode: 'interactive', cols: 1000, rows: 1000 });
+ });
+
+ it('attach en mode observer', () => {
+ expect(parseClientMessage(JSON.stringify({ type: 'attach', sessionId: 's1', mode: 'observer', cols: 80, rows: 24 })))
+ .toEqual({ type: 'attach', sessionId: 's1', mode: 'observer', cols: 80, rows: 24 });
+ });
+
+ it('detach avec channel aux bornes u32', () => {
+ expect(parseClientMessage('{"type":"detach","channel":0}')).toEqual({ type: 'detach', channel: 0 });
+ expect(parseClientMessage(`{"type":"detach","channel":${0xffffffff}}`)).toEqual({ type: 'detach', channel: 0xffffffff });
+ });
+
+ it('stdin avec data à la taille max', () => {
+ const data = 'x'.repeat(65536);
+ expect(parseClientMessage(JSON.stringify({ type: 'stdin', channel: 1, data }))).toEqual({ type: 'stdin', channel: 1, data });
+ });
+
+ it('resize', () => {
+ expect(parseClientMessage('{"type":"resize","channel":3,"cols":120,"rows":32}'))
+ .toEqual({ type: 'resize', channel: 3, cols: 120, rows: 32 });
+ });
+
+ it('ack à zéro octet', () => {
+ expect(parseClientMessage('{"type":"ack","channel":1,"bytes":0}')).toEqual({ type: 'ack', channel: 1, bytes: 0 });
+ });
+
+ it('sub avec topics sessions (et tableau vide accepté)', () => {
+ expect(parseClientMessage('{"type":"sub","topics":["sessions"]}')).toEqual({ type: 'sub', topics: ['sessions'] });
+ expect(parseClientMessage('{"type":"sub","topics":[]}')).toEqual({ type: 'sub', topics: [] });
+ });
+
+ it('ping (champs superflus ignorés)', () => {
+ expect(parseClientMessage('{"type":"ping","extra":42}')).toEqual({ type: 'ping' });
+ });
+});
+
+describe('parseClientMessage — cas malformés', () => {
+ it('JSON invalide ou non-objet', () => {
+ for (const raw of ['', 'not json', '{', '42', '"str"', 'null', 'true', '[]', '[1,2]']) {
+ expect(parseClientMessage(raw)).toBeNull();
+ }
+ });
+
+ it('type absent, non-string ou inconnu', () => {
+ expect(parseClientMessage('{}')).toBeNull();
+ expect(parseClientMessage('{"type":42}')).toBeNull();
+ expect(parseClientMessage('{"type":null}')).toBeNull();
+ expect(parseClientMessage('{"type":"unknown"}')).toBeNull();
+ });
+
+ it('hello sans protocol ou protocol non numérique', () => {
+ expect(parseClientMessage('{"type":"hello"}')).toBeNull();
+ expect(parseClientMessage('{"type":"hello","protocol":"1"}')).toBeNull();
+ });
+
+ it('attach : dimensions hors bornes ou non entières', () => {
+ const make = (over: Record): string =>
+ JSON.stringify({ type: 'attach', sessionId: 's1', mode: 'interactive', cols: 80, rows: 24, ...over });
+ expect(parseClientMessage(make({ cols: 1 }))).toBeNull();
+ expect(parseClientMessage(make({ cols: 1001 }))).toBeNull();
+ expect(parseClientMessage(make({ rows: 0 }))).toBeNull();
+ expect(parseClientMessage(make({ rows: 1001 }))).toBeNull();
+ expect(parseClientMessage(make({ cols: 80.5 }))).toBeNull();
+ expect(parseClientMessage(make({ cols: -80 }))).toBeNull();
+ expect(parseClientMessage(make({ mode: 'admin' }))).toBeNull();
+ expect(parseClientMessage(make({ sessionId: 42 }))).toBeNull();
+ });
+
+ it('channel négatif, flottant ou > u32', () => {
+ expect(parseClientMessage('{"type":"detach","channel":-1}')).toBeNull();
+ expect(parseClientMessage('{"type":"detach","channel":1.5}')).toBeNull();
+ expect(parseClientMessage(`{"type":"detach","channel":${0x100000000}}`)).toBeNull();
+ expect(parseClientMessage('{"type":"stdin","channel":-1,"data":"x"}')).toBeNull();
+ expect(parseClientMessage('{"type":"ack","channel":-1,"bytes":0}')).toBeNull();
+ });
+
+ it('stdin : data trop longue ou non-string', () => {
+ expect(parseClientMessage(JSON.stringify({ type: 'stdin', channel: 1, data: 'x'.repeat(65537) }))).toBeNull();
+ expect(parseClientMessage('{"type":"stdin","channel":1,"data":42}')).toBeNull();
+ expect(parseClientMessage('{"type":"stdin","channel":1}')).toBeNull();
+ });
+
+ it('ack : bytes négatif ou non numérique', () => {
+ expect(parseClientMessage('{"type":"ack","channel":1,"bytes":-1}')).toBeNull();
+ expect(parseClientMessage('{"type":"ack","channel":1,"bytes":"0"}')).toBeNull();
+ });
+
+ it('sub : topics inconnu ou non-tableau', () => {
+ expect(parseClientMessage('{"type":"sub","topics":["other"]}')).toBeNull();
+ expect(parseClientMessage('{"type":"sub","topics":["sessions","other"]}')).toBeNull();
+ expect(parseClientMessage('{"type":"sub","topics":"sessions"}')).toBeNull();
+ });
+});
+
+describe('parseClientMessage — fuzz rapide', () => {
+ it('ne lève jamais et tout message accepté respecte les invariants', () => {
+ const rand = mulberry32(0xa5b0e7);
+ const types = ['hello', 'attach', 'detach', 'stdin', 'resize', 'ack', 'sub', 'ping', 'unknown', '', 'HELLO', 42, null];
+ const values: unknown[] = [
+ undefined, null, true, false, 0, -1, 1, 1.5, 2, 999, 1000, 1001, 0xffffffff, 0x100000000,
+ -0.0001, 1e21, '', 'x', '42', 'interactive', 'observer', 'sessions', {}, [], ['sessions'],
+ ['sessions', 'sessions'], ['sessions', 'other'], [42], 'a'.repeat(70000),
+ ];
+ const pick = (arr: T[]): T => arr[Math.floor(rand() * arr.length)] as T;
+ for (let i = 0; i < 1000; i++) {
+ const obj: Record = { type: pick(types) };
+ for (const key of ['protocol', 'sessionId', 'mode', 'cols', 'rows', 'channel', 'data', 'bytes', 'topics']) {
+ if (rand() < 0.7) obj[key] = pick(values);
+ }
+ const raw = JSON.stringify(obj);
+ let msg: ClientMessage | null = null;
+ expect(() => {
+ msg = parseClientMessage(raw);
+ }).not.toThrow();
+ if (msg !== null) assertInvariants(msg);
+ }
+ });
+});
+
+describe('encode/decodeBinaryFrame', () => {
+ it('round-trip payload vide', () => {
+ const frame = encodeBinaryFrame(BINARY_FRAME.OUTPUT, 0, new Uint8Array(0));
+ expect(frame.byteLength).toBe(BINARY_FRAME.HEADER_BYTES);
+ const decoded = decodeBinaryFrame(frame);
+ expect(decoded.type).toBe(BINARY_FRAME.OUTPUT);
+ expect(decoded.channel).toBe(0);
+ expect(decoded.payload.byteLength).toBe(0);
+ });
+
+ it('round-trip gros payload (1 Mio) et channel max u32', () => {
+ const rand = mulberry32(0xdeadbe);
+ const payload = new Uint8Array(1024 * 1024);
+ for (let i = 0; i < payload.length; i++) payload[i] = Math.floor(rand() * 256);
+ const frame = encodeBinaryFrame(BINARY_FRAME.RESYNC, 0xffffffff, payload);
+ const decoded = decodeBinaryFrame(frame);
+ expect(decoded.type).toBe(BINARY_FRAME.RESYNC);
+ expect(decoded.channel).toBe(0xffffffff);
+ expect(Buffer.from(decoded.payload).equals(Buffer.from(payload))).toBe(true);
+ });
+
+ it('layout : type u8 puis channel u32 little-endian', () => {
+ const frame = encodeBinaryFrame(BINARY_FRAME.OUTPUT, 0x01020304, new Uint8Array([0xff]));
+ expect([...frame]).toEqual([0x01, 0x04, 0x03, 0x02, 0x01, 0xff]);
+ });
+
+ it('octets UTF-8 coupés en frontière de frame : le transport préserve les octets bruts', () => {
+ const text = Buffer.from('héllo 😀 wörld', 'utf8');
+ const cut = 8; // coupe au milieu d'une séquence multi-octets
+ const part1 = new Uint8Array(text.subarray(0, cut));
+ const part2 = new Uint8Array(text.subarray(cut));
+ const d1 = decodeBinaryFrame(encodeBinaryFrame(BINARY_FRAME.OUTPUT, 7, part1));
+ const d2 = decodeBinaryFrame(encodeBinaryFrame(BINARY_FRAME.OUTPUT, 7, part2));
+ const reassembled = Buffer.concat([d1.payload, d2.payload]);
+ expect(reassembled.equals(text)).toBe(true);
+ expect(reassembled.toString('utf8')).toBe('héllo 😀 wörld');
+ });
+
+ it('decode sur une vue avec byteOffset non nul (subarray d’un buffer plus large)', () => {
+ const payload = new Uint8Array([1, 2, 3]);
+ const frame = encodeBinaryFrame(BINARY_FRAME.OUTPUT, 42, payload);
+ const padded = new Uint8Array(frame.byteLength + 11);
+ padded.set(frame, 11);
+ const view = padded.subarray(11);
+ const decoded = decodeBinaryFrame(view);
+ expect(decoded.type).toBe(BINARY_FRAME.OUTPUT);
+ expect(decoded.channel).toBe(42);
+ expect([...decoded.payload]).toEqual([1, 2, 3]);
+ });
+});
diff --git a/packages/shared/tsconfig.tsbuildinfo b/packages/shared/tsconfig.tsbuildinfo
index 9ab0d1d..68f838d 100644
--- a/packages/shared/tsconfig.tsbuildinfo
+++ b/packages/shared/tsconfig.tsbuildinfo
@@ -1 +1 @@
-{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.es2021.d.ts","../../node_modules/typescript/lib/lib.es2022.d.ts","../../node_modules/typescript/lib/lib.es2023.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/typescript/lib/lib.es2021.promise.d.ts","../../node_modules/typescript/lib/lib.es2021.string.d.ts","../../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../node_modules/typescript/lib/lib.es2021.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.array.d.ts","../../node_modules/typescript/lib/lib.es2022.error.d.ts","../../node_modules/typescript/lib/lib.es2022.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.object.d.ts","../../node_modules/typescript/lib/lib.es2022.string.d.ts","../../node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../node_modules/typescript/lib/lib.es2023.array.d.ts","../../node_modules/typescript/lib/lib.es2023.collection.d.ts","../../node_modules/typescript/lib/lib.es2023.intl.d.ts","../../node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/typescript/lib/lib.decorators.legacy.d.ts","./src/protocol.ts","./src/api.ts","./src/index.ts","../../node_modules/@types/deep-eql/index.d.ts","../../node_modules/assertion-error/index.d.ts","../../node_modules/@types/chai/index.d.ts","../../node_modules/@types/estree/index.d.ts","../../node_modules/@types/node/compatibility/disposable.d.ts","../../node_modules/@types/node/compatibility/indexable.d.ts","../../node_modules/@types/node/compatibility/iterators.d.ts","../../node_modules/@types/node/compatibility/index.d.ts","../../node_modules/@types/node/globals.typedarray.d.ts","../../node_modules/@types/node/buffer.buffer.d.ts","../../node_modules/@types/node/globals.d.ts","../../node_modules/@types/node/web-globals/abortcontroller.d.ts","../../node_modules/@types/node/web-globals/domexception.d.ts","../../node_modules/@types/node/web-globals/events.d.ts","../../node_modules/buffer/index.d.ts","../../node_modules/undici-types/header.d.ts","../../node_modules/undici-types/readable.d.ts","../../node_modules/undici-types/file.d.ts","../../node_modules/undici-types/fetch.d.ts","../../node_modules/undici-types/formdata.d.ts","../../node_modules/undici-types/connector.d.ts","../../node_modules/undici-types/client.d.ts","../../node_modules/undici-types/errors.d.ts","../../node_modules/undici-types/dispatcher.d.ts","../../node_modules/undici-types/global-dispatcher.d.ts","../../node_modules/undici-types/global-origin.d.ts","../../node_modules/undici-types/pool-stats.d.ts","../../node_modules/undici-types/pool.d.ts","../../node_modules/undici-types/handlers.d.ts","../../node_modules/undici-types/balanced-pool.d.ts","../../node_modules/undici-types/agent.d.ts","../../node_modules/undici-types/mock-interceptor.d.ts","../../node_modules/undici-types/mock-agent.d.ts","../../node_modules/undici-types/mock-client.d.ts","../../node_modules/undici-types/mock-pool.d.ts","../../node_modules/undici-types/mock-errors.d.ts","../../node_modules/undici-types/proxy-agent.d.ts","../../node_modules/undici-types/env-http-proxy-agent.d.ts","../../node_modules/undici-types/retry-handler.d.ts","../../node_modules/undici-types/retry-agent.d.ts","../../node_modules/undici-types/api.d.ts","../../node_modules/undici-types/interceptors.d.ts","../../node_modules/undici-types/util.d.ts","../../node_modules/undici-types/cookies.d.ts","../../node_modules/undici-types/patch.d.ts","../../node_modules/undici-types/websocket.d.ts","../../node_modules/undici-types/eventsource.d.ts","../../node_modules/undici-types/filereader.d.ts","../../node_modules/undici-types/diagnostics-channel.d.ts","../../node_modules/undici-types/content-type.d.ts","../../node_modules/undici-types/cache.d.ts","../../node_modules/undici-types/index.d.ts","../../node_modules/@types/node/web-globals/fetch.d.ts","../../node_modules/@types/node/web-globals/navigator.d.ts","../../node_modules/@types/node/web-globals/storage.d.ts","../../node_modules/@types/node/assert.d.ts","../../node_modules/@types/node/assert/strict.d.ts","../../node_modules/@types/node/async_hooks.d.ts","../../node_modules/@types/node/buffer.d.ts","../../node_modules/@types/node/child_process.d.ts","../../node_modules/@types/node/cluster.d.ts","../../node_modules/@types/node/console.d.ts","../../node_modules/@types/node/constants.d.ts","../../node_modules/@types/node/crypto.d.ts","../../node_modules/@types/node/dgram.d.ts","../../node_modules/@types/node/diagnostics_channel.d.ts","../../node_modules/@types/node/dns.d.ts","../../node_modules/@types/node/dns/promises.d.ts","../../node_modules/@types/node/domain.d.ts","../../node_modules/@types/node/events.d.ts","../../node_modules/@types/node/fs.d.ts","../../node_modules/@types/node/fs/promises.d.ts","../../node_modules/@types/node/http.d.ts","../../node_modules/@types/node/http2.d.ts","../../node_modules/@types/node/https.d.ts","../../node_modules/@types/node/inspector.d.ts","../../node_modules/@types/node/inspector.generated.d.ts","../../node_modules/@types/node/module.d.ts","../../node_modules/@types/node/net.d.ts","../../node_modules/@types/node/os.d.ts","../../node_modules/@types/node/path.d.ts","../../node_modules/@types/node/perf_hooks.d.ts","../../node_modules/@types/node/process.d.ts","../../node_modules/@types/node/punycode.d.ts","../../node_modules/@types/node/querystring.d.ts","../../node_modules/@types/node/readline.d.ts","../../node_modules/@types/node/readline/promises.d.ts","../../node_modules/@types/node/repl.d.ts","../../node_modules/@types/node/sea.d.ts","../../node_modules/@types/node/sqlite.d.ts","../../node_modules/@types/node/stream.d.ts","../../node_modules/@types/node/stream/promises.d.ts","../../node_modules/@types/node/stream/consumers.d.ts","../../node_modules/@types/node/stream/web.d.ts","../../node_modules/@types/node/string_decoder.d.ts","../../node_modules/@types/node/test.d.ts","../../node_modules/@types/node/timers.d.ts","../../node_modules/@types/node/timers/promises.d.ts","../../node_modules/@types/node/tls.d.ts","../../node_modules/@types/node/trace_events.d.ts","../../node_modules/@types/node/tty.d.ts","../../node_modules/@types/node/url.d.ts","../../node_modules/@types/node/util.d.ts","../../node_modules/@types/node/v8.d.ts","../../node_modules/@types/node/vm.d.ts","../../node_modules/@types/node/wasi.d.ts","../../node_modules/@types/node/worker_threads.d.ts","../../node_modules/@types/node/zlib.d.ts","../../node_modules/@types/node/index.d.ts","../../node_modules/@types/ws/index.d.ts"],"fileIdsList":[[65,66,74,123,140,141],[74,123,140,141],[74,120,121,123,140,141],[74,122,123,140,141],[123,140,141],[74,123,128,140,141,158],[74,123,124,129,134,140,141,143,155,166],[74,123,124,125,134,140,141,143],[69,70,71,74,123,140,141],[74,123,126,140,141,167],[74,123,127,128,135,140,141,144],[74,123,128,140,141,155,163],[74,123,129,131,134,140,141,143],[74,122,123,130,140,141],[74,123,131,132,140,141],[74,123,133,134,140,141],[74,122,123,134,140,141],[74,123,134,135,136,140,141,155,166],[74,123,134,135,136,140,141,150,155,158],[74,116,123,131,134,137,140,141,143,155,166],[74,123,134,135,137,138,140,141,143,155,163,166],[74,123,137,139,140,141,155,163,166],[72,73,74,75,76,77,78,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172],[74,123,134,140,141],[74,123,140,141,142,166],[74,123,131,134,140,141,143,155],[74,123,140,141,144],[74,123,140,141,145],[74,122,123,140,141,146],[74,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172],[74,123,140,141,148],[74,123,140,141,149],[74,123,134,140,141,150,151],[74,123,140,141,150,152,167,169],[74,123,135,140,141],[74,123,134,140,141,155,156,158],[74,123,140,141,157,158],[74,123,140,141,155,156],[74,123,140,141,158],[74,123,140,141,159],[74,120,123,140,141,155,160,166],[74,123,134,140,141,161,162],[74,123,140,141,161,162],[74,123,128,140,141,143,155,163],[74,123,140,141,164],[74,123,140,141,143,165],[74,123,137,140,141,149,166],[74,123,128,140,141,167],[74,123,140,141,155,168],[74,123,140,141,142,169],[74,123,140,141,170],[74,116,123,140,141],[74,116,123,134,136,140,141,146,155,158,166,168,169,171],[74,123,140,141,155,172],[74,123,134,137,139,140,141,143,155,163,166,172,173],[74,88,92,123,140,141,166],[74,88,123,140,141,155,166],[74,83,123,140,141],[74,85,88,123,140,141,163,166],[74,123,140,141,143,163],[74,123,140,141,173],[74,83,123,140,141,173],[74,85,88,123,140,141,143,166],[74,80,81,84,87,123,134,140,141,155,166],[74,88,95,123,140,141],[74,80,86,123,140,141],[74,88,109,110,123,140,141],[74,84,88,123,140,141,158,166,173],[74,109,123,140,141,173],[74,82,83,123,140,141,173],[74,88,123,140,141],[74,82,83,84,85,86,87,88,89,90,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,110,111,112,113,114,115,123,140,141],[74,88,103,123,140,141],[74,88,95,96,123,140,141],[74,86,88,96,97,123,140,141],[74,87,123,140,141],[74,80,83,88,123,140,141],[74,88,92,96,97,123,140,141],[74,92,123,140,141],[74,86,88,91,123,140,141,166],[74,80,85,88,95,123,140,141],[74,123,140,141,155],[74,83,88,109,123,140,141,171,173],[62,74,123,140,141],[62,63,74,123,140,141]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"df83c2a6c73228b625b0beb6669c7ee2a09c914637e2d35170723ad49c0f5cd4","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"997f21102c89aff9cb1655113576e653940a290f1534d5e4957503c9052c9f9b","signature":"155aa8c32f0815cb7f44246680859e2724428459727e6067d0d434a666b72e5b","impliedFormat":99},{"version":"a12d76034419dd99ec4b201711bce3870bc140f3c1fa7a9eb9efbc22295cfa32","signature":"0eaa5ca815f0d1f498a205737cc71a5259671ee1ffc96eb881ee721c2d60bf30","impliedFormat":99},{"version":"8b93a53483cf3bb55c754db9dffd3cc1e3d05f87fe7158aceb4b30f5521a14fc","impliedFormat":99},{"version":"427fe2004642504828c1476d0af4270e6ad4db6de78c0b5da3e4c5ca95052a99","impliedFormat":1},{"version":"2eeffcee5c1661ddca53353929558037b8cf305ffb86a803512982f99bcab50d","impliedFormat":99},{"version":"9afb4cb864d297e4092a79ee2871b5d3143ea14153f62ef0bb04ede25f432030","affectsGlobalScope":true,"impliedFormat":99},{"version":"751764bb94219b4ce8f5475dc35d3de2e432fea01a0c9610cd7f69ad05e398c6","impliedFormat":1},{"version":"6c7176368037af28cb72f2392010fa1cef295d6d6744bca8cfb54985f3a18c3e","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"437e20f2ba32abaeb7985e0afe0002de1917bc74e949ba585e49feba65da6ca1","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"3af97acf03cc97de58a3a4bc91f8f616408099bc4233f6d0852e72a8ffb91ac9","affectsGlobalScope":true,"impliedFormat":1},{"version":"808069bba06b6768b62fd22429b53362e7af342da4a236ed2d2e1c89fcca3b4a","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e9c23ba78aabc2e0a27033f18737a6df754067731e69dc5f52823957d60a4b6","impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"f9501cc13ce624c72b61f12b3963e84fad210fbdf0ffbc4590e08460a3f04eba","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0fa06ada475b910e2106c98c68b10483dc8811d0c14a8a8dd36efb2672485b29","impliedFormat":1},{"version":"33e5e9aba62c3193d10d1d33ae1fa75c46a1171cf76fef750777377d53b0303f","impliedFormat":1},{"version":"2b06b93fd01bcd49d1a6bd1f9b65ddcae6480b9a86e9061634d6f8e354c1468f","impliedFormat":1},{"version":"6a0cd27e5dc2cfbe039e731cf879d12b0e2dded06d1b1dedad07f7712de0d7f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"13f5c844119c43e51ce777c509267f14d6aaf31eafb2c2b002ca35584cd13b29","impliedFormat":1},{"version":"e60477649d6ad21542bd2dc7e3d9ff6853d0797ba9f689ba2f6653818999c264","impliedFormat":1},{"version":"c2510f124c0293ab80b1777c44d80f812b75612f297b9857406468c0f4dafe29","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"4c829ab315f57c5442c6667b53769975acbf92003a66aef19bce151987675bd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"b2ade7657e2db96d18315694789eff2ddd3d8aea7215b181f8a0b303277cc579","impliedFormat":1},{"version":"9855e02d837744303391e5623a531734443a5f8e6e8755e018c41d63ad797db2","impliedFormat":1},{"version":"4d631b81fa2f07a0e63a9a143d6a82c25c5f051298651a9b69176ba28930756d","impliedFormat":1},{"version":"836a356aae992ff3c28a0212e3eabcb76dd4b0cc06bcb9607aeef560661b860d","impliedFormat":1},{"version":"1e0d1f8b0adfa0b0330e028c7941b5a98c08b600efe7f14d2d2a00854fb2f393","impliedFormat":1},{"version":"41670ee38943d9cbb4924e436f56fc19ee94232bc96108562de1a734af20dc2c","affectsGlobalScope":true,"impliedFormat":1},{"version":"c906fb15bd2aabc9ed1e3f44eb6a8661199d6c320b3aa196b826121552cb3695","impliedFormat":1},{"version":"22295e8103f1d6d8ea4b5d6211e43421fe4564e34d0dd8e09e520e452d89e659","impliedFormat":1},{"version":"58647d85d0f722a1ce9de50955df60a7489f0593bf1a7015521efe901c06d770","impliedFormat":1},{"version":"9018e9493596e2d041fc531f5101a0d05f9d2e1311f4ae3c74345b1766158c08","impliedFormat":1},{"version":"a10f0e1854f3316d7ee437b79649e5a6ae3ae14ffe6322b02d4987071a95362e","impliedFormat":1},{"version":"e208f73ef6a980104304b0d2ca5f6bf1b85de6009d2c7e404028b875020fa8f2","impliedFormat":1},{"version":"d163b6bc2372b4f07260747cbc6c0a6405ab3fbcea3852305e98ac43ca59f5bc","impliedFormat":1},{"version":"e6fa9ad47c5f71ff733744a029d1dc472c618de53804eae08ffc243b936f87ff","affectsGlobalScope":true,"impliedFormat":1},{"version":"a6f137d651076822d4fe884287e68fd61785a0d3d1fdb250a5059b691fa897db","impliedFormat":1},{"version":"24826ed94a78d5c64bd857570fdbd96229ad41b5cb654c08d75a9845e3ab7dde","impliedFormat":1},{"version":"8b479a130ccb62e98f11f136d3ac80f2984fdc07616516d29881f3061f2dd472","impliedFormat":1},{"version":"928af3d90454bf656a52a48679f199f64c1435247d6189d1caf4c68f2eaf921f","affectsGlobalScope":true,"impliedFormat":1},{"version":"bceb58df66ab8fb00170df20cd813978c5ab84be1d285710c4eb005d8e9d8efb","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f16a7e4deafa527ed9995a772bb380eb7d3c2c0fd4ae178c5263ed18394db2c","impliedFormat":1},{"version":"933921f0bb0ec12ef45d1062a1fc0f27635318f4d294e4d99de9a5493e618ca2","impliedFormat":1},{"version":"71a0f3ad612c123b57239a7749770017ecfe6b66411488000aba83e4546fde25","impliedFormat":1},{"version":"77fbe5eecb6fac4b6242bbf6eebfc43e98ce5ccba8fa44e0ef6a95c945ff4d98","impliedFormat":1},{"version":"4f9d8ca0c417b67b69eeb54c7ca1bedd7b56034bb9bfd27c5d4f3bc4692daca7","impliedFormat":1},{"version":"814118df420c4e38fe5ae1b9a3bafb6e9c2aa40838e528cde908381867be6466","impliedFormat":1},{"version":"a3fc63c0d7b031693f665f5494412ba4b551fe644ededccc0ab5922401079c95","impliedFormat":1},{"version":"80523c00b8544a2000ae0143e4a90a00b47f99823eb7926c1e03c494216fc363","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"746911b62b329587939560deb5c036aca48aece03147b021fa680223255d5183","affectsGlobalScope":true,"impliedFormat":1},{"version":"18fd40412d102c5564136f29735e5d1c3b455b8a37f920da79561f1fde068208","impliedFormat":1},{"version":"c8d3e5a18ba35629954e48c4cc8f11dc88224650067a172685c736b27a34a4dc","impliedFormat":1},{"version":"f0be1b8078cd549d91f37c30c222c2a187ac1cf981d994fb476a1adc61387b14","affectsGlobalScope":true,"impliedFormat":1},{"version":"0aaed1d72199b01234152f7a60046bc947f1f37d78d182e9ae09c4289e06a592","impliedFormat":1},{"version":"2b55d426ff2b9087485e52ac4bc7cfafe1dc420fc76dad926cd46526567c501a","impliedFormat":1},{"version":"66ba1b2c3e3a3644a1011cd530fb444a96b1b2dfe2f5e837a002d41a1a799e60","impliedFormat":1},{"version":"7e514f5b852fdbc166b539fdd1f4e9114f29911592a5eb10a94bb3a13ccac3c4","impliedFormat":1},{"version":"5b7aa3c4c1a5d81b411e8cb302b45507fea9358d3569196b27eb1a27ae3a90ef","affectsGlobalScope":true,"impliedFormat":1},{"version":"5987a903da92c7462e0b35704ce7da94d7fdc4b89a984871c0e2b87a8aae9e69","affectsGlobalScope":true,"impliedFormat":1},{"version":"ea08a0345023ade2b47fbff5a76d0d0ed8bff10bc9d22b83f40858a8e941501c","impliedFormat":1},{"version":"47613031a5a31510831304405af561b0ffaedb734437c595256bb61a90f9311b","impliedFormat":1},{"version":"ae062ce7d9510060c5d7e7952ae379224fb3f8f2dd74e88959878af2057c143b","impliedFormat":1},{"version":"8a1a0d0a4a06a8d278947fcb66bf684f117bf147f89b06e50662d79a53be3e9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"358765d5ea8afd285d4fd1532e78b88273f18cb3f87403a9b16fef61ac9fdcfe","impliedFormat":1},{"version":"9f55299850d4f0921e79b6bf344b47c420ce0f507b9dcf593e532b09ea7eeea1","impliedFormat":1},{"version":"1ba59c8bbeed2cb75b239bb12041582fa3e8ef32f8d0bd0ec802e38442d3f317","impliedFormat":1}],"root":[[62,64]],"options":{"composite":true,"declaration":true,"declarationMap":true,"esModuleInterop":true,"exactOptionalPropertyTypes":true,"module":199,"noImplicitOverride":true,"noUncheckedIndexedAccess":true,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"sourceMap":true,"strict":true,"target":10,"verbatimModuleSyntax":true},"referencedMap":[[67,1],[65,2],[68,2],[120,3],[121,3],[122,4],[74,5],[123,6],[124,7],[125,8],[69,2],[72,9],[70,2],[71,2],[126,10],[127,11],[128,12],[129,13],[130,14],[131,15],[132,15],[133,16],[134,17],[135,18],[136,19],[75,2],[73,2],[137,20],[138,21],[139,22],[173,23],[140,24],[141,2],[142,25],[143,26],[144,27],[145,28],[146,29],[147,30],[148,31],[149,32],[150,33],[151,33],[152,34],[153,2],[154,35],[155,36],[157,37],[156,38],[158,39],[159,40],[160,41],[161,42],[162,43],[163,44],[164,45],[165,46],[166,47],[167,48],[168,49],[169,50],[170,51],[76,2],[77,2],[78,2],[117,52],[118,2],[119,2],[171,53],[172,54],[174,55],[66,2],[79,2],[60,2],[61,2],[12,2],[11,2],[2,2],[13,2],[14,2],[15,2],[16,2],[17,2],[18,2],[19,2],[20,2],[3,2],[21,2],[22,2],[4,2],[23,2],[27,2],[24,2],[25,2],[26,2],[28,2],[29,2],[30,2],[5,2],[31,2],[32,2],[33,2],[34,2],[6,2],[38,2],[35,2],[36,2],[37,2],[39,2],[7,2],[40,2],[45,2],[46,2],[41,2],[42,2],[43,2],[44,2],[8,2],[50,2],[47,2],[48,2],[49,2],[51,2],[9,2],[52,2],[53,2],[54,2],[56,2],[55,2],[57,2],[58,2],[10,2],[59,2],[1,2],[95,56],[105,57],[94,56],[115,58],[86,59],[85,60],[114,61],[108,62],[113,63],[88,64],[102,65],[87,66],[111,67],[83,68],[82,61],[112,69],[84,70],[89,71],[90,2],[93,71],[80,2],[116,72],[106,73],[97,74],[98,75],[100,76],[96,77],[99,78],[109,61],[91,79],[92,80],[101,81],[81,82],[104,73],[103,71],[107,2],[110,83],[63,84],[64,85],[62,2]],"latestChangedDtsFile":"./dist/index.d.ts","version":"5.9.3"}
\ No newline at end of file
+{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.es2021.d.ts","../../node_modules/typescript/lib/lib.es2022.d.ts","../../node_modules/typescript/lib/lib.es2023.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/typescript/lib/lib.es2021.promise.d.ts","../../node_modules/typescript/lib/lib.es2021.string.d.ts","../../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../node_modules/typescript/lib/lib.es2021.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.array.d.ts","../../node_modules/typescript/lib/lib.es2022.error.d.ts","../../node_modules/typescript/lib/lib.es2022.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.object.d.ts","../../node_modules/typescript/lib/lib.es2022.string.d.ts","../../node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../node_modules/typescript/lib/lib.es2023.array.d.ts","../../node_modules/typescript/lib/lib.es2023.collection.d.ts","../../node_modules/typescript/lib/lib.es2023.intl.d.ts","../../node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/typescript/lib/lib.decorators.legacy.d.ts","./src/protocol.ts","./src/api.ts","./src/index.ts","../../node_modules/@types/deep-eql/index.d.ts","../../node_modules/assertion-error/index.d.ts","../../node_modules/@types/chai/index.d.ts","../../node_modules/@types/estree/index.d.ts","../../node_modules/@types/node/compatibility/disposable.d.ts","../../node_modules/@types/node/compatibility/indexable.d.ts","../../node_modules/@types/node/compatibility/iterators.d.ts","../../node_modules/@types/node/compatibility/index.d.ts","../../node_modules/@types/node/globals.typedarray.d.ts","../../node_modules/@types/node/buffer.buffer.d.ts","../../node_modules/@types/node/globals.d.ts","../../node_modules/@types/node/web-globals/abortcontroller.d.ts","../../node_modules/@types/node/web-globals/domexception.d.ts","../../node_modules/@types/node/web-globals/events.d.ts","../../node_modules/buffer/index.d.ts","../../node_modules/undici-types/header.d.ts","../../node_modules/undici-types/readable.d.ts","../../node_modules/undici-types/file.d.ts","../../node_modules/undici-types/fetch.d.ts","../../node_modules/undici-types/formdata.d.ts","../../node_modules/undici-types/connector.d.ts","../../node_modules/undici-types/client.d.ts","../../node_modules/undici-types/errors.d.ts","../../node_modules/undici-types/dispatcher.d.ts","../../node_modules/undici-types/global-dispatcher.d.ts","../../node_modules/undici-types/global-origin.d.ts","../../node_modules/undici-types/pool-stats.d.ts","../../node_modules/undici-types/pool.d.ts","../../node_modules/undici-types/handlers.d.ts","../../node_modules/undici-types/balanced-pool.d.ts","../../node_modules/undici-types/agent.d.ts","../../node_modules/undici-types/mock-interceptor.d.ts","../../node_modules/undici-types/mock-agent.d.ts","../../node_modules/undici-types/mock-client.d.ts","../../node_modules/undici-types/mock-pool.d.ts","../../node_modules/undici-types/mock-errors.d.ts","../../node_modules/undici-types/proxy-agent.d.ts","../../node_modules/undici-types/env-http-proxy-agent.d.ts","../../node_modules/undici-types/retry-handler.d.ts","../../node_modules/undici-types/retry-agent.d.ts","../../node_modules/undici-types/api.d.ts","../../node_modules/undici-types/interceptors.d.ts","../../node_modules/undici-types/util.d.ts","../../node_modules/undici-types/cookies.d.ts","../../node_modules/undici-types/patch.d.ts","../../node_modules/undici-types/websocket.d.ts","../../node_modules/undici-types/eventsource.d.ts","../../node_modules/undici-types/filereader.d.ts","../../node_modules/undici-types/diagnostics-channel.d.ts","../../node_modules/undici-types/content-type.d.ts","../../node_modules/undici-types/cache.d.ts","../../node_modules/undici-types/index.d.ts","../../node_modules/@types/node/web-globals/fetch.d.ts","../../node_modules/@types/node/web-globals/navigator.d.ts","../../node_modules/@types/node/web-globals/storage.d.ts","../../node_modules/@types/node/assert.d.ts","../../node_modules/@types/node/assert/strict.d.ts","../../node_modules/@types/node/async_hooks.d.ts","../../node_modules/@types/node/buffer.d.ts","../../node_modules/@types/node/child_process.d.ts","../../node_modules/@types/node/cluster.d.ts","../../node_modules/@types/node/console.d.ts","../../node_modules/@types/node/constants.d.ts","../../node_modules/@types/node/crypto.d.ts","../../node_modules/@types/node/dgram.d.ts","../../node_modules/@types/node/diagnostics_channel.d.ts","../../node_modules/@types/node/dns.d.ts","../../node_modules/@types/node/dns/promises.d.ts","../../node_modules/@types/node/domain.d.ts","../../node_modules/@types/node/events.d.ts","../../node_modules/@types/node/fs.d.ts","../../node_modules/@types/node/fs/promises.d.ts","../../node_modules/@types/node/http.d.ts","../../node_modules/@types/node/http2.d.ts","../../node_modules/@types/node/https.d.ts","../../node_modules/@types/node/inspector.d.ts","../../node_modules/@types/node/inspector.generated.d.ts","../../node_modules/@types/node/module.d.ts","../../node_modules/@types/node/net.d.ts","../../node_modules/@types/node/os.d.ts","../../node_modules/@types/node/path.d.ts","../../node_modules/@types/node/perf_hooks.d.ts","../../node_modules/@types/node/process.d.ts","../../node_modules/@types/node/punycode.d.ts","../../node_modules/@types/node/querystring.d.ts","../../node_modules/@types/node/readline.d.ts","../../node_modules/@types/node/readline/promises.d.ts","../../node_modules/@types/node/repl.d.ts","../../node_modules/@types/node/sea.d.ts","../../node_modules/@types/node/sqlite.d.ts","../../node_modules/@types/node/stream.d.ts","../../node_modules/@types/node/stream/promises.d.ts","../../node_modules/@types/node/stream/consumers.d.ts","../../node_modules/@types/node/stream/web.d.ts","../../node_modules/@types/node/string_decoder.d.ts","../../node_modules/@types/node/test.d.ts","../../node_modules/@types/node/timers.d.ts","../../node_modules/@types/node/timers/promises.d.ts","../../node_modules/@types/node/tls.d.ts","../../node_modules/@types/node/trace_events.d.ts","../../node_modules/@types/node/tty.d.ts","../../node_modules/@types/node/url.d.ts","../../node_modules/@types/node/util.d.ts","../../node_modules/@types/node/v8.d.ts","../../node_modules/@types/node/vm.d.ts","../../node_modules/@types/node/wasi.d.ts","../../node_modules/@types/node/worker_threads.d.ts","../../node_modules/@types/node/zlib.d.ts","../../node_modules/@types/node/index.d.ts","../../node_modules/@types/ws/index.d.ts"],"fileIdsList":[[65,66,74,123,140,141],[74,123,140,141],[74,120,121,123,140,141],[74,122,123,140,141],[123,140,141],[74,123,128,140,141,158],[74,123,124,129,134,140,141,143,155,166],[74,123,124,125,134,140,141,143],[69,70,71,74,123,140,141],[74,123,126,140,141,167],[74,123,127,128,135,140,141,144],[74,123,128,140,141,155,163],[74,123,129,131,134,140,141,143],[74,122,123,130,140,141],[74,123,131,132,140,141],[74,123,133,134,140,141],[74,122,123,134,140,141],[74,123,134,135,136,140,141,155,166],[74,123,134,135,136,140,141,150,155,158],[74,116,123,131,134,137,140,141,143,155,166],[74,123,134,135,137,138,140,141,143,155,163,166],[74,123,137,139,140,141,155,163,166],[72,73,74,75,76,77,78,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172],[74,123,134,140,141],[74,123,140,141,142,166],[74,123,131,134,140,141,143,155],[74,123,140,141,144],[74,123,140,141,145],[74,122,123,140,141,146],[74,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172],[74,123,140,141,148],[74,123,140,141,149],[74,123,134,140,141,150,151],[74,123,140,141,150,152,167,169],[74,123,135,140,141],[74,123,134,140,141,155,156,158],[74,123,140,141,157,158],[74,123,140,141,155,156],[74,123,140,141,158],[74,123,140,141,159],[74,120,123,140,141,155,160,166],[74,123,134,140,141,161,162],[74,123,140,141,161,162],[74,123,128,140,141,143,155,163],[74,123,140,141,164],[74,123,140,141,143,165],[74,123,137,140,141,149,166],[74,123,128,140,141,167],[74,123,140,141,155,168],[74,123,140,141,142,169],[74,123,140,141,170],[74,116,123,140,141],[74,116,123,134,136,140,141,146,155,158,166,168,169,171],[74,123,140,141,155,172],[74,123,134,137,139,140,141,143,155,163,166,172,173],[74,88,92,123,140,141,166],[74,88,123,140,141,155,166],[74,83,123,140,141],[74,85,88,123,140,141,163,166],[74,123,140,141,143,163],[74,123,140,141,173],[74,83,123,140,141,173],[74,85,88,123,140,141,143,166],[74,80,81,84,87,123,134,140,141,155,166],[74,88,95,123,140,141],[74,80,86,123,140,141],[74,88,109,110,123,140,141],[74,84,88,123,140,141,158,166,173],[74,109,123,140,141,173],[74,82,83,123,140,141,173],[74,88,123,140,141],[74,82,83,84,85,86,87,88,89,90,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,110,111,112,113,114,115,123,140,141],[74,88,103,123,140,141],[74,88,95,96,123,140,141],[74,86,88,96,97,123,140,141],[74,87,123,140,141],[74,80,83,88,123,140,141],[74,88,92,96,97,123,140,141],[74,92,123,140,141],[74,86,88,91,123,140,141,166],[74,80,85,88,95,123,140,141],[74,123,140,141,155],[74,83,88,109,123,140,141,171,173],[62,74,123,140,141],[62,63,74,123,140,141]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"df83c2a6c73228b625b0beb6669c7ee2a09c914637e2d35170723ad49c0f5cd4","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"cd3f497a20129961ff2798627df44b34b84b54b3f45f18d286a0398a379cbc17","signature":"155aa8c32f0815cb7f44246680859e2724428459727e6067d0d434a666b72e5b","impliedFormat":99},{"version":"a12d76034419dd99ec4b201711bce3870bc140f3c1fa7a9eb9efbc22295cfa32","signature":"0eaa5ca815f0d1f498a205737cc71a5259671ee1ffc96eb881ee721c2d60bf30","impliedFormat":99},{"version":"8b93a53483cf3bb55c754db9dffd3cc1e3d05f87fe7158aceb4b30f5521a14fc","impliedFormat":99},{"version":"427fe2004642504828c1476d0af4270e6ad4db6de78c0b5da3e4c5ca95052a99","impliedFormat":1},{"version":"2eeffcee5c1661ddca53353929558037b8cf305ffb86a803512982f99bcab50d","impliedFormat":99},{"version":"9afb4cb864d297e4092a79ee2871b5d3143ea14153f62ef0bb04ede25f432030","affectsGlobalScope":true,"impliedFormat":99},{"version":"751764bb94219b4ce8f5475dc35d3de2e432fea01a0c9610cd7f69ad05e398c6","impliedFormat":1},{"version":"6c7176368037af28cb72f2392010fa1cef295d6d6744bca8cfb54985f3a18c3e","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"437e20f2ba32abaeb7985e0afe0002de1917bc74e949ba585e49feba65da6ca1","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"3af97acf03cc97de58a3a4bc91f8f616408099bc4233f6d0852e72a8ffb91ac9","affectsGlobalScope":true,"impliedFormat":1},{"version":"808069bba06b6768b62fd22429b53362e7af342da4a236ed2d2e1c89fcca3b4a","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e9c23ba78aabc2e0a27033f18737a6df754067731e69dc5f52823957d60a4b6","impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"f9501cc13ce624c72b61f12b3963e84fad210fbdf0ffbc4590e08460a3f04eba","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0fa06ada475b910e2106c98c68b10483dc8811d0c14a8a8dd36efb2672485b29","impliedFormat":1},{"version":"33e5e9aba62c3193d10d1d33ae1fa75c46a1171cf76fef750777377d53b0303f","impliedFormat":1},{"version":"2b06b93fd01bcd49d1a6bd1f9b65ddcae6480b9a86e9061634d6f8e354c1468f","impliedFormat":1},{"version":"6a0cd27e5dc2cfbe039e731cf879d12b0e2dded06d1b1dedad07f7712de0d7f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"13f5c844119c43e51ce777c509267f14d6aaf31eafb2c2b002ca35584cd13b29","impliedFormat":1},{"version":"e60477649d6ad21542bd2dc7e3d9ff6853d0797ba9f689ba2f6653818999c264","impliedFormat":1},{"version":"c2510f124c0293ab80b1777c44d80f812b75612f297b9857406468c0f4dafe29","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"4c829ab315f57c5442c6667b53769975acbf92003a66aef19bce151987675bd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"b2ade7657e2db96d18315694789eff2ddd3d8aea7215b181f8a0b303277cc579","impliedFormat":1},{"version":"9855e02d837744303391e5623a531734443a5f8e6e8755e018c41d63ad797db2","impliedFormat":1},{"version":"4d631b81fa2f07a0e63a9a143d6a82c25c5f051298651a9b69176ba28930756d","impliedFormat":1},{"version":"836a356aae992ff3c28a0212e3eabcb76dd4b0cc06bcb9607aeef560661b860d","impliedFormat":1},{"version":"1e0d1f8b0adfa0b0330e028c7941b5a98c08b600efe7f14d2d2a00854fb2f393","impliedFormat":1},{"version":"41670ee38943d9cbb4924e436f56fc19ee94232bc96108562de1a734af20dc2c","affectsGlobalScope":true,"impliedFormat":1},{"version":"c906fb15bd2aabc9ed1e3f44eb6a8661199d6c320b3aa196b826121552cb3695","impliedFormat":1},{"version":"22295e8103f1d6d8ea4b5d6211e43421fe4564e34d0dd8e09e520e452d89e659","impliedFormat":1},{"version":"58647d85d0f722a1ce9de50955df60a7489f0593bf1a7015521efe901c06d770","impliedFormat":1},{"version":"9018e9493596e2d041fc531f5101a0d05f9d2e1311f4ae3c74345b1766158c08","impliedFormat":1},{"version":"a10f0e1854f3316d7ee437b79649e5a6ae3ae14ffe6322b02d4987071a95362e","impliedFormat":1},{"version":"e208f73ef6a980104304b0d2ca5f6bf1b85de6009d2c7e404028b875020fa8f2","impliedFormat":1},{"version":"d163b6bc2372b4f07260747cbc6c0a6405ab3fbcea3852305e98ac43ca59f5bc","impliedFormat":1},{"version":"e6fa9ad47c5f71ff733744a029d1dc472c618de53804eae08ffc243b936f87ff","affectsGlobalScope":true,"impliedFormat":1},{"version":"a6f137d651076822d4fe884287e68fd61785a0d3d1fdb250a5059b691fa897db","impliedFormat":1},{"version":"24826ed94a78d5c64bd857570fdbd96229ad41b5cb654c08d75a9845e3ab7dde","impliedFormat":1},{"version":"8b479a130ccb62e98f11f136d3ac80f2984fdc07616516d29881f3061f2dd472","impliedFormat":1},{"version":"928af3d90454bf656a52a48679f199f64c1435247d6189d1caf4c68f2eaf921f","affectsGlobalScope":true,"impliedFormat":1},{"version":"bceb58df66ab8fb00170df20cd813978c5ab84be1d285710c4eb005d8e9d8efb","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f16a7e4deafa527ed9995a772bb380eb7d3c2c0fd4ae178c5263ed18394db2c","impliedFormat":1},{"version":"933921f0bb0ec12ef45d1062a1fc0f27635318f4d294e4d99de9a5493e618ca2","impliedFormat":1},{"version":"71a0f3ad612c123b57239a7749770017ecfe6b66411488000aba83e4546fde25","impliedFormat":1},{"version":"77fbe5eecb6fac4b6242bbf6eebfc43e98ce5ccba8fa44e0ef6a95c945ff4d98","impliedFormat":1},{"version":"4f9d8ca0c417b67b69eeb54c7ca1bedd7b56034bb9bfd27c5d4f3bc4692daca7","impliedFormat":1},{"version":"814118df420c4e38fe5ae1b9a3bafb6e9c2aa40838e528cde908381867be6466","impliedFormat":1},{"version":"a3fc63c0d7b031693f665f5494412ba4b551fe644ededccc0ab5922401079c95","impliedFormat":1},{"version":"80523c00b8544a2000ae0143e4a90a00b47f99823eb7926c1e03c494216fc363","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"746911b62b329587939560deb5c036aca48aece03147b021fa680223255d5183","affectsGlobalScope":true,"impliedFormat":1},{"version":"18fd40412d102c5564136f29735e5d1c3b455b8a37f920da79561f1fde068208","impliedFormat":1},{"version":"c8d3e5a18ba35629954e48c4cc8f11dc88224650067a172685c736b27a34a4dc","impliedFormat":1},{"version":"f0be1b8078cd549d91f37c30c222c2a187ac1cf981d994fb476a1adc61387b14","affectsGlobalScope":true,"impliedFormat":1},{"version":"0aaed1d72199b01234152f7a60046bc947f1f37d78d182e9ae09c4289e06a592","impliedFormat":1},{"version":"2b55d426ff2b9087485e52ac4bc7cfafe1dc420fc76dad926cd46526567c501a","impliedFormat":1},{"version":"66ba1b2c3e3a3644a1011cd530fb444a96b1b2dfe2f5e837a002d41a1a799e60","impliedFormat":1},{"version":"7e514f5b852fdbc166b539fdd1f4e9114f29911592a5eb10a94bb3a13ccac3c4","impliedFormat":1},{"version":"5b7aa3c4c1a5d81b411e8cb302b45507fea9358d3569196b27eb1a27ae3a90ef","affectsGlobalScope":true,"impliedFormat":1},{"version":"5987a903da92c7462e0b35704ce7da94d7fdc4b89a984871c0e2b87a8aae9e69","affectsGlobalScope":true,"impliedFormat":1},{"version":"ea08a0345023ade2b47fbff5a76d0d0ed8bff10bc9d22b83f40858a8e941501c","impliedFormat":1},{"version":"47613031a5a31510831304405af561b0ffaedb734437c595256bb61a90f9311b","impliedFormat":1},{"version":"ae062ce7d9510060c5d7e7952ae379224fb3f8f2dd74e88959878af2057c143b","impliedFormat":1},{"version":"8a1a0d0a4a06a8d278947fcb66bf684f117bf147f89b06e50662d79a53be3e9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"358765d5ea8afd285d4fd1532e78b88273f18cb3f87403a9b16fef61ac9fdcfe","impliedFormat":1},{"version":"9f55299850d4f0921e79b6bf344b47c420ce0f507b9dcf593e532b09ea7eeea1","impliedFormat":1},{"version":"1ba59c8bbeed2cb75b239bb12041582fa3e8ef32f8d0bd0ec802e38442d3f317","impliedFormat":1}],"root":[[62,64]],"options":{"composite":true,"declaration":true,"declarationMap":true,"esModuleInterop":true,"exactOptionalPropertyTypes":true,"module":199,"noImplicitOverride":true,"noUncheckedIndexedAccess":true,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"sourceMap":true,"strict":true,"target":10,"verbatimModuleSyntax":true},"referencedMap":[[67,1],[65,2],[68,2],[120,3],[121,3],[122,4],[74,5],[123,6],[124,7],[125,8],[69,2],[72,9],[70,2],[71,2],[126,10],[127,11],[128,12],[129,13],[130,14],[131,15],[132,15],[133,16],[134,17],[135,18],[136,19],[75,2],[73,2],[137,20],[138,21],[139,22],[173,23],[140,24],[141,2],[142,25],[143,26],[144,27],[145,28],[146,29],[147,30],[148,31],[149,32],[150,33],[151,33],[152,34],[153,2],[154,35],[155,36],[157,37],[156,38],[158,39],[159,40],[160,41],[161,42],[162,43],[163,44],[164,45],[165,46],[166,47],[167,48],[168,49],[169,50],[170,51],[76,2],[77,2],[78,2],[117,52],[118,2],[119,2],[171,53],[172,54],[174,55],[66,2],[79,2],[60,2],[61,2],[12,2],[11,2],[2,2],[13,2],[14,2],[15,2],[16,2],[17,2],[18,2],[19,2],[20,2],[3,2],[21,2],[22,2],[4,2],[23,2],[27,2],[24,2],[25,2],[26,2],[28,2],[29,2],[30,2],[5,2],[31,2],[32,2],[33,2],[34,2],[6,2],[38,2],[35,2],[36,2],[37,2],[39,2],[7,2],[40,2],[45,2],[46,2],[41,2],[42,2],[43,2],[44,2],[8,2],[50,2],[47,2],[48,2],[49,2],[51,2],[9,2],[52,2],[53,2],[54,2],[56,2],[55,2],[57,2],[58,2],[10,2],[59,2],[1,2],[95,56],[105,57],[94,56],[115,58],[86,59],[85,60],[114,61],[108,62],[113,63],[88,64],[102,65],[87,66],[111,67],[83,68],[82,61],[112,69],[84,70],[89,71],[90,2],[93,71],[80,2],[116,72],[106,73],[97,74],[98,75],[100,76],[96,77],[99,78],[109,61],[91,79],[92,80],[101,81],[81,82],[104,73],[103,71],[107,2],[110,83],[63,84],[64,85],[62,2]],"latestChangedDtsFile":"./dist/index.d.ts","version":"5.9.3"}
\ No newline at end of file
diff --git a/packages/web/index.html b/packages/web/index.html
new file mode 100644
index 0000000..b60b18f
--- /dev/null
+++ b/packages/web/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Arboretum
+
+
+
+
+
+
diff --git a/packages/web/package.json b/packages/web/package.json
new file mode 100644
index 0000000..d8d7814
--- /dev/null
+++ b/packages/web/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "@arboretum/web",
+ "private": true,
+ "version": "0.1.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vue-tsc --noEmit && vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@arboretum/shared": "*",
+ "@xterm/addon-fit": "^0.11.0",
+ "@xterm/addon-webgl": "^0.19.0",
+ "@xterm/xterm": "^6.0.0",
+ "pinia": "^3.0.4",
+ "vue": "^3.5.38",
+ "vue-i18n": "^11.4.5",
+ "vue-router": "^4.6.4"
+ },
+ "devDependencies": {
+ "@tailwindcss/vite": "^4.3.0",
+ "@vitejs/plugin-vue": "^6.0.7",
+ "tailwindcss": "^4.3.0",
+ "typescript": "^5.7.0",
+ "vite": "^8.0.16",
+ "vue-tsc": "^3.3.4"
+ }
+}
diff --git a/packages/web/src/App.vue b/packages/web/src/App.vue
new file mode 100644
index 0000000..563e28e
--- /dev/null
+++ b/packages/web/src/App.vue
@@ -0,0 +1,20 @@
+
+
+
+ {{ t('ws.reconnecting') }}
+
+
+
+
+
+
diff --git a/packages/web/src/components/LanguageSwitcher.vue b/packages/web/src/components/LanguageSwitcher.vue
new file mode 100644
index 0000000..5f3b8df
--- /dev/null
+++ b/packages/web/src/components/LanguageSwitcher.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
diff --git a/packages/web/src/components/TerminalView.vue b/packages/web/src/components/TerminalView.vue
new file mode 100644
index 0000000..89e1ad3
--- /dev/null
+++ b/packages/web/src/components/TerminalView.vue
@@ -0,0 +1,118 @@
+
+
+
+
+ {{ t('terminal.observer') }}
+
+
+ {{ t('terminal.sessionEnded') }}
+
+
+
+
+
diff --git a/packages/web/src/i18n/en.ts b/packages/web/src/i18n/en.ts
new file mode 100644
index 0000000..762d124
--- /dev/null
+++ b/packages/web/src/i18n/en.ts
@@ -0,0 +1,49 @@
+export default {
+ common: {
+ appName: 'Arboretum',
+ loading: 'Loading…',
+ cancel: 'Cancel',
+ logout: 'Log out',
+ language: 'Language',
+ },
+ login: {
+ title: 'Sign in with your access token',
+ tokenLabel: 'Access token',
+ tokenPlaceholder: 'arb_…',
+ submit: 'Sign in',
+ submitting: 'Signing in…',
+ invalidToken: 'Invalid token',
+ rateLimited: 'Too many attempts — please wait before retrying',
+ genericError: 'Sign-in failed ({status})',
+ },
+ sessions: {
+ title: 'Sessions',
+ refresh: 'Refresh',
+ empty: 'No sessions yet — start one below.',
+ newSession: 'New session',
+ cwdLabel: 'Working directory',
+ cwdPlaceholder: '/absolute/path/to/project',
+ commandLabel: 'Command',
+ creating: 'Creating…',
+ open: 'Open',
+ observe: 'Observe',
+ kill: 'Kill',
+ confirmKill: 'Confirm kill',
+ live: 'live',
+ clients: 'no client | 1 client | {n} clients',
+ exitCode: 'exit code {code}',
+ statusLabel: {
+ starting: 'starting',
+ running: 'running',
+ exited: 'exited',
+ },
+ },
+ terminal: {
+ observer: 'observer (read-only)',
+ sessionEnded: 'Session ended',
+ back: 'Sessions',
+ },
+ ws: {
+ reconnecting: 'Connection lost — reconnecting…',
+ },
+};
diff --git a/packages/web/src/i18n/fr.ts b/packages/web/src/i18n/fr.ts
new file mode 100644
index 0000000..4c75c46
--- /dev/null
+++ b/packages/web/src/i18n/fr.ts
@@ -0,0 +1,53 @@
+import type en from './en';
+
+const fr: typeof en = {
+ common: {
+ appName: 'Arboretum',
+ loading: 'Chargement…',
+ cancel: 'Annuler',
+ logout: 'Se déconnecter',
+ language: 'Langue',
+ },
+ login: {
+ title: 'Connectez-vous avec votre jeton d’accès',
+ tokenLabel: 'Jeton d’accès',
+ tokenPlaceholder: 'arb_…',
+ submit: 'Se connecter',
+ submitting: 'Connexion…',
+ invalidToken: 'Jeton invalide',
+ rateLimited: 'Trop de tentatives — patientez avant de réessayer',
+ genericError: 'Échec de la connexion ({status})',
+ },
+ sessions: {
+ title: 'Sessions',
+ refresh: 'Rafraîchir',
+ empty: 'Aucune session — démarrez-en une ci-dessous.',
+ newSession: 'Nouvelle session',
+ cwdLabel: 'Répertoire de travail',
+ cwdPlaceholder: '/chemin/absolu/du/projet',
+ commandLabel: 'Commande',
+ creating: 'Création…',
+ open: 'Ouvrir',
+ observe: 'Observer',
+ kill: 'Tuer',
+ confirmKill: 'Confirmer',
+ live: 'en cours',
+ clients: 'aucun client | 1 client | {n} clients',
+ exitCode: 'code de sortie {code}',
+ statusLabel: {
+ starting: 'démarrage',
+ running: 'en cours',
+ exited: 'terminée',
+ },
+ },
+ terminal: {
+ observer: 'observateur (lecture seule)',
+ sessionEnded: 'Session terminée',
+ back: 'Sessions',
+ },
+ ws: {
+ reconnecting: 'Connexion perdue — reconnexion…',
+ },
+};
+
+export default fr;
diff --git a/packages/web/src/i18n/index.ts b/packages/web/src/i18n/index.ts
new file mode 100644
index 0000000..6c1ac9f
--- /dev/null
+++ b/packages/web/src/i18n/index.ts
@@ -0,0 +1,24 @@
+import { createI18n } from 'vue-i18n';
+import en from './en';
+import fr from './fr';
+
+export type AppLocale = 'en' | 'fr';
+
+const STORAGE_KEY = 'arboretum.locale';
+
+function initialLocale(): AppLocale {
+ const saved = localStorage.getItem(STORAGE_KEY);
+ return saved === 'fr' || saved === 'en' ? saved : 'en';
+}
+
+export const i18n = createI18n({
+ legacy: false,
+ locale: initialLocale(),
+ fallbackLocale: 'en',
+ messages: { en, fr },
+});
+
+export function setLocale(locale: AppLocale): void {
+ i18n.global.locale.value = locale;
+ localStorage.setItem(STORAGE_KEY, locale);
+}
diff --git a/packages/web/src/lib/api.ts b/packages/web/src/lib/api.ts
new file mode 100644
index 0000000..c8efc13
--- /dev/null
+++ b/packages/web/src/lib/api.ts
@@ -0,0 +1,38 @@
+// Mini client REST : JSON, cookies de session, erreurs API normalisées.
+import type { ApiError as ApiErrorBody } from '@arboretum/shared';
+
+export class ApiError extends Error {
+ constructor(
+ readonly status: number,
+ readonly code: string,
+ message: string,
+ ) {
+ super(message);
+ this.name = 'ApiError';
+ }
+}
+
+async function request(path: string, method: string, body?: unknown): Promise {
+ const init: RequestInit = { method, credentials: 'same-origin' };
+ if (body !== undefined) {
+ init.headers = { 'content-type': 'application/json' };
+ init.body = JSON.stringify(body);
+ }
+ const res = await fetch(path, init);
+ if (!res.ok) {
+ let parsed: ApiErrorBody | null = null;
+ try {
+ parsed = (await res.json()) as ApiErrorBody;
+ } catch {
+ // réponse non-JSON : on retombe sur le statusText
+ }
+ throw new ApiError(res.status, parsed?.error.code ?? 'UNKNOWN', parsed?.error.message ?? res.statusText);
+ }
+ return (await res.json()) as T;
+}
+
+export const api = {
+ get: (path: string): Promise => request(path, 'GET'),
+ post: (path: string, body?: unknown): Promise => request(path, 'POST', body),
+ delete: (path: string): Promise => request(path, 'DELETE'),
+};
diff --git a/packages/web/src/lib/ws-client.ts b/packages/web/src/lib/ws-client.ts
new file mode 100644
index 0000000..ea90289
--- /dev/null
+++ b/packages/web/src/lib/ws-client.ts
@@ -0,0 +1,370 @@
+// Client WebSocket multiplexé — UNE seule connexion pour toute l'app.
+// Handshake hello/hello_ok, reconnexion avec backoff (ré-attache des terminaux
+// ouverts + re-sub), corrélation FIFO des 'attached', flow control par ACK
+// (cumul d'octets traités PAR CANAL, remis à zéro à chaque RESYNC).
+import { ref, type Ref } from 'vue';
+import {
+ BINARY_FRAME,
+ FLOW,
+ PROTOCOL_VERSION,
+ decodeBinaryFrame,
+ type ClientMessage,
+ type ServerMessage,
+} from '@arboretum/shared';
+
+export type WsStatus = 'idle' | 'connecting' | 'open' | 'reconnecting';
+
+export type DetachReason = 'client' | 'session_exit' | 'replaced';
+
+/** Interface minimale attendue côté terminal (implémentée par xterm). */
+export interface TerminalSink {
+ /** le callback est invoqué quand xterm a réellement traité le chunk (base du flow control) */
+ write(data: Uint8Array, callback?: () => void): void;
+ /** reset complet avant le replay d'une frame RESYNC */
+ reset(): void;
+ onDetached(reason: DetachReason): void;
+ onControlChanged(controlling: boolean): void;
+}
+
+export type SessionEvent = Extract;
+
+export interface AttachOptions {
+ sessionId: string;
+ mode: 'interactive' | 'observer';
+ cols: number;
+ rows: number;
+ sink: TerminalSink;
+}
+
+const BACKOFF_MIN_MS = 500;
+const BACKOFF_MAX_MS = 10_000;
+
+export class Attachment {
+ /** canal courant — réassigné à chaque reconnexion, -1 tant que non attaché */
+ channel = -1;
+ controlling = false;
+ closed = false;
+ cols: number;
+ rows: number;
+ /** époque de resync : invalide les callbacks d'écritures encore en vol après un reset */
+ epoch = 0;
+ /** octets de payload réellement traités par le terminal depuis le dernier resync */
+ processedBytes = 0;
+ /** dernier cumul envoyé en ack (repart à 0 à chaque resync, comme côté serveur) */
+ lastAckBytes = 0;
+ /** ACK traînant : garantit l'envoi du reliquat même quand le flux s'arrête (anti-deadlock) */
+ trailingAckTimer: ReturnType | null = null;
+ /** résolution du premier 'attached' de ce canal (corrélation FIFO) */
+ pending: { resolve: (a: Attachment) => void; reject: (err: Error) => void } | null = null;
+
+ constructor(
+ private readonly client: WsClient,
+ readonly sessionId: string,
+ readonly mode: 'interactive' | 'observer',
+ readonly sink: TerminalSink,
+ cols: number,
+ rows: number,
+ ) {
+ this.cols = cols;
+ this.rows = rows;
+ }
+
+ sendStdin(data: string): void {
+ if (this.closed || this.channel < 0) return;
+ this.client.sendControl({ type: 'stdin', channel: this.channel, data });
+ }
+
+ /** mémorise toujours la taille (réutilisée à la ré-attache) ; n'envoie que si controlling */
+ resize(cols: number, rows: number): void {
+ this.cols = cols;
+ this.rows = rows;
+ if (this.closed || this.channel < 0 || !this.controlling) return;
+ this.client.sendControl({ type: 'resize', channel: this.channel, cols, rows });
+ }
+
+ detach(): void {
+ if (this.closed) return;
+ this.closed = true;
+ this.pending = null;
+ this.client.releaseAttachment(this);
+ }
+}
+
+export class WsClient {
+ readonly status: Ref = ref('idle');
+
+ private socket: WebSocket | null = null;
+ private ready = false;
+ private stopped = true;
+ private backoffMs = BACKOFF_MIN_MS;
+ private reconnectTimer: ReturnType | null = null;
+ /** terminaux ouverts, ré-attachés à chaque reconnexion (ordre d'insertion = ordre FIFO) */
+ private readonly attachments = new Set();
+ private readonly byChannel = new Map();
+ /** FIFO des attaches en attente : le serveur répond aux 'attach' dans l'ordre */
+ private awaitingAttached: Attachment[] = [];
+ private readonly sessionListeners = new Set<(e: SessionEvent) => void>();
+
+ connect(): void {
+ this.stopped = false;
+ if (this.socket || this.reconnectTimer) return;
+ if (this.status.value === 'idle') this.status.value = 'connecting';
+ this.openSocket();
+ }
+
+ disconnect(): void {
+ this.stopped = true;
+ this.clearReconnectTimer();
+ const socket = this.socket;
+ this.socket = null;
+ this.ready = false;
+ this.status.value = 'idle';
+ for (const att of this.attachments) {
+ att.closed = true;
+ att.pending?.reject(new Error('disconnected'));
+ att.pending = null;
+ att.sink.onDetached('client');
+ }
+ this.attachments.clear();
+ this.byChannel.clear();
+ this.awaitingAttached = [];
+ socket?.close();
+ }
+
+ /** résout sur le prochain 'attached' corrélé ; rejette si la session est introuvable/terminée */
+ attach(opts: AttachOptions): Promise {
+ const att = new Attachment(this, opts.sessionId, opts.mode, opts.sink, opts.cols, opts.rows);
+ const promise = new Promise((resolve, reject) => {
+ att.pending = { resolve, reject };
+ });
+ this.attachments.add(att);
+ this.connect();
+ if (this.ready) this.sendAttach(att);
+ return promise;
+ }
+
+ subscribeSessions(listener: (e: SessionEvent) => void): () => void {
+ this.sessionListeners.add(listener);
+ this.connect();
+ if (this.sessionListeners.size === 1) this.sendControl({ type: 'sub', topics: ['sessions'] });
+ return () => {
+ this.sessionListeners.delete(listener);
+ if (this.sessionListeners.size === 0) this.sendControl({ type: 'sub', topics: [] });
+ };
+ }
+
+ sendControl(msg: ClientMessage): void {
+ if (!this.ready || this.socket?.readyState !== WebSocket.OPEN) return;
+ this.socket.send(JSON.stringify(msg));
+ }
+
+ /** détache côté serveur et oublie l'attache (appelé par Attachment.detach) */
+ releaseAttachment(att: Attachment): void {
+ this.attachments.delete(att);
+ if (att.channel >= 0) {
+ this.byChannel.delete(att.channel);
+ this.sendControl({ type: 'detach', channel: att.channel });
+ att.channel = -1;
+ }
+ // si l'attache attend encore son 'attached', elle reste dans la FIFO pour
+ // préserver la corrélation : closed=true la fera détacher à la réception
+ }
+
+ // ---- interne ----
+
+ private openSocket(): void {
+ const socket = new WebSocket(`${location.origin.replace(/^http/, 'ws')}/ws`);
+ socket.binaryType = 'arraybuffer';
+ this.socket = socket;
+ socket.onopen = () => {
+ const hello: ClientMessage = { type: 'hello', protocol: PROTOCOL_VERSION };
+ socket.send(JSON.stringify(hello));
+ };
+ socket.onmessage = (ev: MessageEvent) => this.handleMessage(ev);
+ socket.onclose = () => this.handleClose(socket);
+ socket.onerror = () => {
+ // onclose suit systématiquement : la reconnexion se joue là-bas
+ };
+ }
+
+ private handleClose(socket: WebSocket): void {
+ if (this.socket !== socket) return;
+ this.socket = null;
+ this.ready = false;
+ this.byChannel.clear();
+ this.awaitingAttached = [];
+ for (const att of this.attachments) {
+ att.channel = -1;
+ att.epoch += 1;
+ }
+ if (this.stopped) {
+ this.status.value = 'idle';
+ return;
+ }
+ this.status.value = 'reconnecting';
+ this.scheduleReconnect();
+ }
+
+ private scheduleReconnect(): void {
+ this.clearReconnectTimer();
+ this.reconnectTimer = setTimeout(() => {
+ this.reconnectTimer = null;
+ if (!this.stopped) this.openSocket();
+ }, this.backoffMs);
+ this.backoffMs = Math.min(this.backoffMs * 2, BACKOFF_MAX_MS);
+ }
+
+ private clearReconnectTimer(): void {
+ if (this.reconnectTimer !== null) {
+ clearTimeout(this.reconnectTimer);
+ this.reconnectTimer = null;
+ }
+ }
+
+ private sendAttach(att: Attachment): void {
+ this.awaitingAttached.push(att);
+ this.sendControl({ type: 'attach', sessionId: att.sessionId, mode: att.mode, cols: att.cols, rows: att.rows });
+ }
+
+ private handleMessage(ev: MessageEvent): void {
+ if (ev.data instanceof ArrayBuffer) {
+ this.handleBinary(new Uint8Array(ev.data));
+ return;
+ }
+ let msg: ServerMessage;
+ try {
+ msg = JSON.parse(String(ev.data)) as ServerMessage;
+ } catch {
+ return;
+ }
+ this.handleServerMessage(msg);
+ }
+
+ private handleBinary(data: Uint8Array): void {
+ if (data.byteLength < BINARY_FRAME.HEADER_BYTES) return;
+ const frame = decodeBinaryFrame(data);
+ const att = this.byChannel.get(frame.channel);
+ if (!att || att.closed) return;
+
+ if (frame.type === BINARY_FRAME.RESYNC) {
+ // le serveur repart de sentBytes=0 après un resync : reset terminal, replay
+ // du ring, et compteurs d'octets remis à zéro (le payload du replay ne compte pas)
+ att.epoch += 1;
+ att.processedBytes = 0;
+ att.lastAckBytes = 0;
+ att.sink.reset();
+ if (frame.payload.byteLength > 0) att.sink.write(frame.payload);
+ return;
+ }
+ if (frame.type !== BINARY_FRAME.OUTPUT) return;
+
+ const epoch = att.epoch;
+ const length = frame.payload.byteLength;
+ att.sink.write(frame.payload, () => {
+ // compté seulement si aucun resync/reconnexion n'est passé entre temps
+ if (att.closed || att.epoch !== epoch || att.channel < 0) return;
+ att.processedBytes += length;
+ if (att.processedBytes - att.lastAckBytes >= FLOW.ACK_EVERY_BYTES) {
+ att.lastAckBytes = att.processedBytes;
+ this.sendControl({ type: 'ack', channel: att.channel, bytes: att.processedBytes });
+ }
+ // ACK traînant débouncé : si le flux s'arrête avec un reliquat non-ACKé,
+ // le serveur ne doit jamais rester en pause faute d'ACK suivant.
+ if (att.trailingAckTimer) clearTimeout(att.trailingAckTimer);
+ att.trailingAckTimer = setTimeout(() => {
+ att.trailingAckTimer = null;
+ if (att.closed || att.epoch !== epoch || att.channel < 0) return;
+ if (att.processedBytes > att.lastAckBytes) {
+ att.lastAckBytes = att.processedBytes;
+ this.sendControl({ type: 'ack', channel: att.channel, bytes: att.processedBytes });
+ }
+ }, 200);
+ });
+ }
+
+ private handleServerMessage(msg: ServerMessage): void {
+ switch (msg.type) {
+ case 'hello_ok': {
+ this.ready = true;
+ this.backoffMs = BACKOFF_MIN_MS;
+ this.status.value = 'open';
+ if (this.sessionListeners.size > 0) this.sendControl({ type: 'sub', topics: ['sessions'] });
+ // ré-attache des terminaux encore ouverts (nouveaux canaux côté serveur)
+ for (const att of this.attachments) {
+ if (!att.closed) this.sendAttach(att);
+ }
+ return;
+ }
+ case 'attached': {
+ const att = this.awaitingAttached.shift();
+ if (!att) return;
+ if (att.closed) {
+ // détaché pendant l'attente : on libère le canal côté serveur
+ this.sendControl({ type: 'detach', channel: msg.channel });
+ return;
+ }
+ const wasControlling = att.controlling;
+ att.channel = msg.channel;
+ att.controlling = msg.controlling;
+ this.byChannel.set(msg.channel, att);
+ if (att.pending) {
+ att.pending.resolve(att);
+ att.pending = null;
+ } else if (wasControlling !== msg.controlling) {
+ // ré-attache après reconnexion : le statut controlling peut avoir changé
+ att.sink.onControlChanged(msg.controlling);
+ }
+ return;
+ }
+ case 'detached': {
+ const att = this.byChannel.get(msg.channel);
+ if (!att) return;
+ this.byChannel.delete(msg.channel);
+ att.channel = -1;
+ if (msg.reason === 'client') return;
+ att.closed = true;
+ this.attachments.delete(att);
+ att.sink.onDetached(msg.reason);
+ return;
+ }
+ case 'control_changed': {
+ const att = this.byChannel.get(msg.channel);
+ if (!att || att.closed) return;
+ att.controlling = msg.controlling;
+ att.sink.onControlChanged(msg.controlling);
+ return;
+ }
+ case 'session_update':
+ case 'session_exit': {
+ for (const cb of this.sessionListeners) cb(msg);
+ return;
+ }
+ case 'error': {
+ if (msg.channel !== undefined) {
+ console.warn(`[ws] channel ${msg.channel}: ${msg.code} — ${msg.message}`);
+ return;
+ }
+ // un échec d'attach est la seule erreur sans canal corrélable à une requête
+ if ((msg.code === 'NOT_FOUND' || msg.code === 'SESSION_EXITED') && this.awaitingAttached.length > 0) {
+ const att = this.awaitingAttached.shift();
+ if (!att) return;
+ this.attachments.delete(att);
+ if (att.pending) {
+ att.pending.reject(new Error(msg.code));
+ att.pending = null;
+ } else if (!att.closed) {
+ att.closed = true;
+ att.sink.onDetached('session_exit');
+ }
+ return;
+ }
+ console.warn(`[ws] ${msg.code} — ${msg.message}`);
+ return;
+ }
+ case 'pong':
+ return;
+ }
+ }
+}
+
+export const wsClient = new WsClient();
diff --git a/packages/web/src/main.ts b/packages/web/src/main.ts
new file mode 100644
index 0000000..7664ab4
--- /dev/null
+++ b/packages/web/src/main.ts
@@ -0,0 +1,8 @@
+import { createApp } from 'vue';
+import { createPinia } from 'pinia';
+import App from './App.vue';
+import { router } from './router';
+import { i18n } from './i18n';
+import './style.css';
+
+createApp(App).use(createPinia()).use(router).use(i18n).mount('#app');
diff --git a/packages/web/src/router/index.ts b/packages/web/src/router/index.ts
new file mode 100644
index 0000000..7ddf64b
--- /dev/null
+++ b/packages/web/src/router/index.ts
@@ -0,0 +1,20 @@
+import { createRouter, createWebHistory } from 'vue-router';
+import { useAuthStore } from '../stores/auth';
+
+export const router = createRouter({
+ history: createWebHistory(),
+ routes: [
+ { path: '/login', name: 'login', component: () => import('../views/LoginView.vue') },
+ { path: '/', name: 'sessions', component: () => import('../views/SessionsListView.vue') },
+ { path: '/sessions/:id', name: 'session', component: () => import('../views/SessionView.vue') },
+ { path: '/:pathMatch(.*)*', redirect: '/' },
+ ],
+});
+
+// Garde globale : tout sauf /login exige une session valide (GET /api/v1/auth/me, 401 → /login)
+router.beforeEach(async (to) => {
+ const auth = useAuthStore();
+ if (auth.authenticated === null) await auth.check();
+ if (to.name === 'login') return auth.authenticated ? { name: 'sessions' } : true;
+ return auth.authenticated ? true : { name: 'login', query: { redirect: to.fullPath } };
+});
diff --git a/packages/web/src/stores/auth.ts b/packages/web/src/stores/auth.ts
new file mode 100644
index 0000000..bd898e3
--- /dev/null
+++ b/packages/web/src/stores/auth.ts
@@ -0,0 +1,42 @@
+import { defineStore } from 'pinia';
+import { ref } from 'vue';
+import type { LoginResponse, MeResponse } from '@arboretum/shared';
+import { api } from '../lib/api';
+import { wsClient } from '../lib/ws-client';
+
+export const useAuthStore = defineStore('auth', () => {
+ /** null = pas encore vérifié auprès du serveur */
+ const authenticated = ref(null);
+ const tokenLabel = ref(null);
+ const serverVersion = ref(null);
+
+ async function check(): Promise {
+ try {
+ const me = await api.get('/api/v1/auth/me');
+ tokenLabel.value = me.tokenLabel;
+ serverVersion.value = me.serverVersion;
+ authenticated.value = true;
+ } catch {
+ authenticated.value = false;
+ }
+ return authenticated.value === true;
+ }
+
+ async function login(token: string): Promise {
+ const res = await api.post('/api/v1/auth/login', { token });
+ tokenLabel.value = res.label;
+ authenticated.value = true;
+ }
+
+ async function logout(): Promise {
+ try {
+ await api.post<{ ok: true }>('/api/v1/auth/logout');
+ } finally {
+ wsClient.disconnect();
+ authenticated.value = false;
+ tokenLabel.value = null;
+ }
+ }
+
+ return { authenticated, tokenLabel, serverVersion, check, login, logout };
+});
diff --git a/packages/web/src/stores/sessions.ts b/packages/web/src/stores/sessions.ts
new file mode 100644
index 0000000..98dea77
--- /dev/null
+++ b/packages/web/src/stores/sessions.ts
@@ -0,0 +1,76 @@
+import { defineStore } from 'pinia';
+import { ref } from 'vue';
+import type { CreateSessionRequest, SessionResponse, SessionSummary, SessionsListResponse } from '@arboretum/shared';
+import { api } from '../lib/api';
+import { wsClient, type SessionEvent } from '../lib/ws-client';
+
+export const useSessionsStore = defineStore('sessions', () => {
+ const sessions = ref([]);
+ const loading = ref(false);
+ const loadError = ref(null);
+ let unsubscribe: (() => void) | null = null;
+
+ // vivantes d'abord, puis par date de création décroissante (même ordre que le serveur)
+ function sorted(list: SessionSummary[]): SessionSummary[] {
+ return [...list].sort((a, b) => Number(b.live) - Number(a.live) || b.createdAt.localeCompare(a.createdAt));
+ }
+
+ function upsert(session: SessionSummary): void {
+ const idx = sessions.value.findIndex((s) => s.id === session.id);
+ if (idx >= 0) sessions.value.splice(idx, 1, session);
+ else sessions.value.push(session);
+ sessions.value = sorted(sessions.value);
+ }
+
+ function onEvent(e: SessionEvent): void {
+ if (e.type === 'session_update') {
+ upsert(e.session);
+ return;
+ }
+ const current = sessions.value.find((s) => s.id === e.sessionId);
+ if (!current) return;
+ upsert({
+ ...current,
+ status: 'exited',
+ live: false,
+ exitCode: e.exitCode,
+ endedAt: new Date().toISOString(),
+ clients: 0,
+ });
+ }
+
+ async function fetchSessions(): Promise {
+ loading.value = true;
+ loadError.value = null;
+ try {
+ const res = await api.get('/api/v1/sessions');
+ sessions.value = sorted(res.sessions);
+ } catch (err) {
+ loadError.value = err instanceof Error ? err.message : String(err);
+ } finally {
+ loading.value = false;
+ }
+ }
+
+ function startRealtime(): void {
+ unsubscribe ??= wsClient.subscribeSessions(onEvent);
+ }
+
+ function stopRealtime(): void {
+ unsubscribe?.();
+ unsubscribe = null;
+ }
+
+ async function createSession(cwd: string, command: NonNullable): Promise {
+ const body: CreateSessionRequest = { cwd, command };
+ const res = await api.post('/api/v1/sessions', body);
+ upsert(res.session);
+ return res.session;
+ }
+
+ async function killSession(id: string): Promise {
+ await api.delete<{ ok: true }>(`/api/v1/sessions/${id}`);
+ }
+
+ return { sessions, loading, loadError, fetchSessions, startRealtime, stopRealtime, createSession, killSession };
+});
diff --git a/packages/web/src/style.css b/packages/web/src/style.css
new file mode 100644
index 0000000..1cafe1a
--- /dev/null
+++ b/packages/web/src/style.css
@@ -0,0 +1,31 @@
+@import 'tailwindcss';
+
+html,
+body,
+#app {
+ height: 100%;
+}
+
+body {
+ background-color: var(--color-zinc-950);
+ color: var(--color-zinc-200);
+ -webkit-font-smoothing: antialiased;
+}
+
+@layer components {
+ .input {
+ @apply w-full rounded-md border border-zinc-700 bg-zinc-950 px-2.5 py-1.5 text-sm text-zinc-100 outline-none transition-colors placeholder:text-zinc-600 focus:border-zinc-400;
+ }
+ .btn {
+ @apply inline-flex items-center justify-center gap-1.5 rounded-md border border-zinc-700 bg-zinc-800 px-3 py-1.5 text-sm font-medium text-zinc-200 transition-colors hover:bg-zinc-700 disabled:cursor-not-allowed disabled:opacity-50;
+ }
+ .btn-primary {
+ @apply inline-flex items-center justify-center gap-1.5 rounded-md bg-emerald-600 px-3 py-1.5 text-sm font-medium text-white transition-colors hover:bg-emerald-500 disabled:cursor-not-allowed disabled:opacity-50;
+ }
+ .btn-danger {
+ @apply inline-flex items-center justify-center gap-1.5 rounded-md border border-red-900 bg-red-950 px-3 py-1.5 text-sm font-medium text-red-300 transition-colors hover:bg-red-900 disabled:cursor-not-allowed disabled:opacity-50;
+ }
+ .badge {
+ @apply inline-flex items-center rounded px-1.5 py-0.5 text-[11px] font-medium;
+ }
+}
diff --git a/packages/web/src/views/LoginView.vue b/packages/web/src/views/LoginView.vue
new file mode 100644
index 0000000..c9bf9df
--- /dev/null
+++ b/packages/web/src/views/LoginView.vue
@@ -0,0 +1,64 @@
+
+
+
+
+
diff --git a/packages/web/src/views/SessionView.vue b/packages/web/src/views/SessionView.vue
new file mode 100644
index 0000000..e5a1c95
--- /dev/null
+++ b/packages/web/src/views/SessionView.vue
@@ -0,0 +1,40 @@
+
+
+
+
+ ← {{ t('terminal.back') }}
+
+ {{ session.command }}
+
+ {{ session?.cwd ?? sessionId }}
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/web/src/views/SessionsListView.vue b/packages/web/src/views/SessionsListView.vue
new file mode 100644
index 0000000..0ebf00f
--- /dev/null
+++ b/packages/web/src/views/SessionsListView.vue
@@ -0,0 +1,151 @@
+
+
+
+
+
+
+
{{ createError }}
+
+
{{ store.loadError }}
+
+ {{ t('common.loading') }}
+
+
{{ t('sessions.empty') }}
+
+
+
+
+
+
+
diff --git a/packages/web/tsconfig.json b/packages/web/tsconfig.json
new file mode 100644
index 0000000..eed9011
--- /dev/null
+++ b/packages/web/tsconfig.json
@@ -0,0 +1,16 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "composite": false,
+ "noEmit": true,
+ "declaration": false,
+ "declarationMap": false,
+ "sourceMap": false,
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "lib": ["ES2023", "DOM", "DOM.Iterable"],
+ "types": ["vite/client"],
+ "useDefineForClassFields": true
+ },
+ "include": ["src/**/*.ts", "src/**/*.vue", "vite.config.ts"]
+}
diff --git a/packages/web/vite.config.ts b/packages/web/vite.config.ts
new file mode 100644
index 0000000..89e7f0d
--- /dev/null
+++ b/packages/web/vite.config.ts
@@ -0,0 +1,29 @@
+import { defineConfig } from 'vite';
+import vue from '@vitejs/plugin-vue';
+import tailwindcss from '@tailwindcss/vite';
+
+const DAEMON_HTTP = 'http://127.0.0.1:7317';
+const DAEMON_WS = 'ws://127.0.0.1:7317';
+
+export default defineConfig({
+ plugins: [vue(), tailwindcss()],
+ server: {
+ proxy: {
+ // L'Origin est réécrite vers celle du daemon : son check Origin strict
+ // rejetterait sinon l'origine du serveur de dev Vite.
+ '/api': {
+ target: DAEMON_HTTP,
+ configure: (proxy) => {
+ proxy.on('proxyReq', (proxyReq) => proxyReq.setHeader('origin', DAEMON_HTTP));
+ },
+ },
+ '/ws': {
+ target: DAEMON_WS,
+ ws: true,
+ configure: (proxy) => {
+ proxy.on('proxyReqWs', (proxyReq) => proxyReq.setHeader('origin', DAEMON_HTTP));
+ },
+ },
+ },
+ },
+});
diff --git a/vitest.config.ts b/vitest.config.ts
new file mode 100644
index 0000000..48d2fbb
--- /dev/null
+++ b/vitest.config.ts
@@ -0,0 +1,9 @@
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+ test: {
+ environment: 'node',
+ include: ['packages/*/test/**/*.test.ts'],
+ testTimeout: 10_000,
+ },
+});