diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..da196c0 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,50 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + +jobs: + format: + name: Format + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version-file: .nvmrc + cache: npm + + - name: Install Packages + run: npm ci + shell: bash + + - name: Prettier + run: npm run format:check + shell: bash + + build: + name: Build + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version-file: .nvmrc + cache: npm + + - name: Install Packages + run: npm ci + shell: bash + + - name: Build + run: npm run build + shell: bash diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..af87033 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,57 @@ +name: Deploy + +on: + push: + branches: + - main + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version-file: .nvmrc + cache: npm + + - name: Install Packages + run: npm ci + shell: bash + + - name: Build + env: + BASE_URL: "/${{ github.event.repository.name }}/" + VITE_API_ENDPOINT: ${{ vars.VITE_API_ENDPOINT }} + run: npm run build + shell: bash + + - name: Upload artifact + uses: actions/upload-pages-artifact@v4 + with: + path: ./packages/frontend/dist + + deploy: + name: Deploy + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..68b21f0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/node_modules +.DS_Store diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..2bd5a0a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/.prettierrc @@ -0,0 +1 @@ +{} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..fb54c4d --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6335 @@ +{ + "name": "sentence-structure-diagram-app", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sentence-structure-diagram-app", + "version": "0.1.0", + "workspaces": [ + "packages/backend", + "packages/frontend", + "packages/sentence-structure-data", + "packages/sentence-structure-diagram-data", + "packages/sentence-structure-diagram-svg", + "packages/evaluation", + "packages/sentence-structure-diagram-configurations", + "packages/sentence-structure-tree", + "packages/sentence-structure-diagram-tree" + ], + "devDependencies": { + "prettier": "^3.7.4" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", + "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz", + "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz", + "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz", + "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz", + "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz", + "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz", + "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz", + "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz", + "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz", + "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz", + "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz", + "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz", + "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz", + "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz", + "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz", + "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz", + "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz", + "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz", + "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz", + "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz", + "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz", + "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz", + "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz", + "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz", + "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz", + "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz", + "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", + "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@google/genai": { + "version": "1.34.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.34.0.tgz", + "integrity": "sha512-vu53UMPvjmb7PGzlYu6Tzxso8Dfhn+a7eQFaS2uNemVtDZKwzSpJ5+ikqBbXplF7RGB1STcVDqCkPvquiwb2sw==", + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^10.3.0", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.24.0" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@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==", + "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==", + "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==", + "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==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.6.tgz", + "integrity": "sha512-QaYtTHlr8kDFN5mE1wbvVARRKH7Fdw1ZuOjBJcFdVpfNfRYKF3QLT4rt+WaB6CKJvpqxRsmEo0kpYinhH5GeHg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/icons-material": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.3.6.tgz", + "integrity": "sha512-0FfkXEj22ysIq5pa41A2NbcAhJSvmcZQ/vcTIbjDsd6hlslG82k5BEBqqS0ZJprxwIL3B45qpJ+bPHwJPlF7uQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^7.3.6", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.6.tgz", + "integrity": "sha512-R4DaYF3dgCQCUAkr4wW1w26GHXcf5rCmBRHVBuuvJvaGLmZdD8EjatP80Nz5JCw0KxORAzwftnHzXVnjR8HnFw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/core-downloads-tracker": "^7.3.6", + "@mui/system": "^7.3.6", + "@mui/types": "^7.4.9", + "@mui/utils": "^7.3.6", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^19.2.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^7.3.6", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.6.tgz", + "integrity": "sha512-Ws9wZpqM+FlnbZXaY/7yvyvWQo1+02Tbx50mVdNmzWEi51C51y56KAbaDCYyulOOBL6BJxuaqG8rNNuj7ivVyw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/utils": "^7.3.6", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.6.tgz", + "integrity": "sha512-+wiYbtvj+zyUkmDB+ysH6zRjuQIJ+CM56w0fEXV+VDNdvOuSywG+/8kpjddvvlfMLsaWdQe5oTuYGBcodmqGzQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.6.tgz", + "integrity": "sha512-8fehAazkHNP1imMrdD2m2hbA9sl7Ur6jfuNweh5o4l9YPty4iaZzRXqYvBCWQNwFaSHmMEj2KPbyXGp7Bt73Rg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/private-theming": "^7.3.6", + "@mui/styled-engine": "^7.3.6", + "@mui/types": "^7.4.9", + "@mui/utils": "^7.3.6", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.4.9", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.9.tgz", + "integrity": "sha512-dNO8Z9T2cujkSIaCnWwprfeKmTWh97cnjkgmpFJ2sbfXLx8SMZijCYHOtP/y5nnUb/Rm2omxbDMmtUoSaUtKaw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.6.tgz", + "integrity": "sha512-jn+Ba02O6PiFs7nKva8R2aJJ9kJC+3kQ2R0BbKNY3KQQ36Qng98GnPRFTlbwYTdMD6hLEBKaMLUktyg/rTfd2w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/types": "^7.4.9", + "@types/prop-types": "^15.7.15", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.2.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz", + "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sentence-structure-diagram-app/backend": { + "resolved": "packages/backend", + "link": true + }, + "node_modules/@sentence-structure-diagram-app/evaluation": { + "resolved": "packages/evaluation", + "link": true + }, + "node_modules/@sentence-structure-diagram-app/frontend": { + "resolved": "packages/frontend", + "link": true + }, + "node_modules/@sentence-structure-diagram-app/sentence-structure-data": { + "resolved": "packages/sentence-structure-data", + "link": true + }, + "node_modules/@sentence-structure-diagram-app/sentence-structure-diagram-configurations": { + "resolved": "packages/sentence-structure-diagram-configurations", + "link": true + }, + "node_modules/@sentence-structure-diagram-app/sentence-structure-diagram-data": { + "resolved": "packages/sentence-structure-diagram-data", + "link": true + }, + "node_modules/@sentence-structure-diagram-app/sentence-structure-diagram-svg": { + "resolved": "packages/sentence-structure-diagram-svg", + "link": true + }, + "node_modules/@sentence-structure-diagram-app/sentence-structure-diagram-tree": { + "resolved": "packages/sentence-structure-diagram-tree", + "link": true + }, + "node_modules/@sentence-structure-diagram-app/sentence-structure-tree": { + "resolved": "packages/sentence-structure-tree", + "link": true + }, + "node_modules/@tanstack/query-core": { + "version": "5.90.12", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.12.tgz", + "integrity": "sha512-T1/8t5DhV/SisWjDnaiU2drl6ySvsHj1bHBCWNXd+/T+Hh1cf6JodyEYMd5sgwm+b/mETT4EV3H+zCVczCU5hg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.90.12", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.12.tgz", + "integrity": "sha512-graRZspg7EoEaw0a8faiUASCyJrqjKPdqJ9EwuDRUF9mEYJ1YPczI9H+/agJ0mOJkPCJDk0lsz5QTrLZ/jQ2rg==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.90.12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@trpc/client": { + "version": "11.7.2", + "resolved": "https://registry.npmjs.org/@trpc/client/-/client-11.7.2.tgz", + "integrity": "sha512-OQxqUMfpDvjcszo9dbnqWQXnW2L5IbrKSz2H7l8s+mVM3EvYw7ztQ/gjFIN3iy0NcamiQfd4eE6qjcb9Lm+63A==", + "funding": [ + "https://trpc.io/sponsor" + ], + "license": "MIT", + "peerDependencies": { + "@trpc/server": "11.7.2", + "typescript": ">=5.7.2" + } + }, + "node_modules/@trpc/server": { + "version": "11.7.2", + "resolved": "https://registry.npmjs.org/@trpc/server/-/server-11.7.2.tgz", + "integrity": "sha512-AgB26PXY69sckherIhCacKLY49rxE2XP5h38vr/KMZTbLCL1p8IuIoKPjALTcugC2kbyQ7Lbqo2JDVfRSmPmfQ==", + "funding": [ + "https://trpc.io/sponsor" + ], + "license": "MIT", + "peerDependencies": { + "typescript": ">=5.7.2" + } + }, + "node_modules/@trpc/tanstack-react-query": { + "version": "11.7.2", + "resolved": "https://registry.npmjs.org/@trpc/tanstack-react-query/-/tanstack-react-query-11.7.2.tgz", + "integrity": "sha512-3XrY0b8lV0Fhj4Z2hVn1d1ZJzq2/stbc2F1e9Y6RrUWOfLmOKHlEVHYO1QfDGM6rqj66DkUj7eA593hAI0VTkQ==", + "funding": [ + "https://trpc.io/sponsor" + ], + "license": "MIT", + "peerDependencies": { + "@tanstack/react-query": "^5.80.3", + "@trpc/client": "11.7.2", + "@trpc/server": "11.7.2", + "react": ">=18.2.0", + "react-dom": ">=18.2.0", + "typescript": ">=5.7.2" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz", + "integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.10.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.4.tgz", + "integrity": "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", + "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.49.0.tgz", + "integrity": "sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/type-utils": "8.49.0", + "@typescript-eslint/utils": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.49.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.49.0.tgz", + "integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.49.0.tgz", + "integrity": "sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.49.0", + "@typescript-eslint/types": "^8.49.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.49.0.tgz", + "integrity": "sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.49.0.tgz", + "integrity": "sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.49.0.tgz", + "integrity": "sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0", + "@typescript-eslint/utils": "8.49.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.49.0.tgz", + "integrity": "sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.49.0.tgz", + "integrity": "sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.49.0", + "@typescript-eslint/tsconfig-utils": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", + "debug": "^4.3.4", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.49.0.tgz", + "integrity": "sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.49.0.tgz", + "integrity": "sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.49.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz", + "integrity": "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.5", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.53", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.6", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.6.tgz", + "integrity": "sha512-v9BVVpOTLB59C9E7aSnmIF8h7qRsFpx+A2nugVMTszEOMcfjlZMsXRm4LF23I3Z9AJxc8ANpIvzbzONoX9VJlg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/body-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", + "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001760", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz", + "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/compromise": { + "version": "14.14.4", + "resolved": "https://registry.npmjs.org/compromise/-/compromise-14.14.4.tgz", + "integrity": "sha512-QdbJwronwxeqb7a5KFK/+Y5YieZ4PE1f7ai0vU58Pp4jih+soDCBMuKVbhDEPQ+6+vI3vSiG4UAAjTAXLJw1Qw==", + "license": "MIT", + "dependencies": { + "efrt": "2.7.0", + "grad-school": "0.0.5", + "suffix-thumb": "5.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "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/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/efrt": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/efrt/-/efrt-2.7.0.tgz", + "integrity": "sha512-/RInbCy1d4P6Zdfa+TMVsf/ufZVotat5hCw3QXmWtjU+3pFEOvOQ7ibo3aIxyCJw2leIeAMjmPj+1SLJiCpdrQ==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.267", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz", + "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.1", + "@esbuild/android-arm": "0.27.1", + "@esbuild/android-arm64": "0.27.1", + "@esbuild/android-x64": "0.27.1", + "@esbuild/darwin-arm64": "0.27.1", + "@esbuild/darwin-x64": "0.27.1", + "@esbuild/freebsd-arm64": "0.27.1", + "@esbuild/freebsd-x64": "0.27.1", + "@esbuild/linux-arm": "0.27.1", + "@esbuild/linux-arm64": "0.27.1", + "@esbuild/linux-ia32": "0.27.1", + "@esbuild/linux-loong64": "0.27.1", + "@esbuild/linux-mips64el": "0.27.1", + "@esbuild/linux-ppc64": "0.27.1", + "@esbuild/linux-riscv64": "0.27.1", + "@esbuild/linux-s390x": "0.27.1", + "@esbuild/linux-x64": "0.27.1", + "@esbuild/netbsd-arm64": "0.27.1", + "@esbuild/netbsd-x64": "0.27.1", + "@esbuild/openbsd-arm64": "0.27.1", + "@esbuild/openbsd-x64": "0.27.1", + "@esbuild/openharmony-arm64": "0.27.1", + "@esbuild/sunos-x64": "0.27.1", + "@esbuild/win32-arm64": "0.27.1", + "@esbuild/win32-ia32": "0.27.1", + "@esbuild/win32-x64": "0.27.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", + "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.1", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", + "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.24.tgz", + "integrity": "sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-xml-parser": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.3.tgz", + "integrity": "sha512-2O3dkPAAC6JavuMm8+4+pgTk+5hoAs+CjZ+sWcQLkX9+/tHRuTkQh/Oaifr8qDmZ8iEHb771Ea6G8CdwkrgvYA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gaxios": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz", + "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2", + "rimraf": "^5.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", + "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/google-auth-library": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.5.0.tgz", + "integrity": "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.0.0", + "gcp-metadata": "^8.0.0", + "google-logging-utils": "^1.0.0", + "gtoken": "^8.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-logging-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/grad-school": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/grad-school/-/grad-school-0.0.5.tgz", + "integrity": "sha512-rXunEHF9M9EkMydTBux7+IryYXEZinRk6g8OBOGDBzo/qWJjhTxy86i5q7lQYpCLHN8Sqv1XX3OIOc7ka2gtvQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/gtoken": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz", + "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==", + "license": "MIT", + "dependencies": { + "gaxios": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/openai": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.15.0.tgz", + "integrity": "sha512-F1Lvs5BoVvmZtzkUEVyh8mDQPPFolq4F+xdsx/DO8Hee8YF3IGAlZqUIsF+DVGhqf4aU0a3bTghsxB6OIsRy1g==", + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/react": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", + "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.3" + } + }, + "node_modules/react-is": { + "version": "19.2.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.1.tgz", + "integrity": "sha512-L7BnWgRbMwzMAubQcS7sXdPdNLmKlucPlopgAzx7FtYbksWZgEWiuYM5x9T6UqS2Ne0rsgQTq5kY2SGqpzUkYA==", + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "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/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "node_modules/suffix-thumb": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/suffix-thumb/-/suffix-thumb-5.0.2.tgz", + "integrity": "sha512-I5PWXAFKx3FYnI9a+dQMWNqTxoRt6vdBdb0O+BJ1sxXCWtSoQCusc13E58f+9p4MYx/qCnEMkD5jac6K2j3dgA==", + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.49.0.tgz", + "integrity": "sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.49.0", + "@typescript-eslint/parser": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0", + "@typescript-eslint/utils": "8.49.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz", + "integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "7.2.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.7.tgz", + "integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "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", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.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 + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz", + "integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, + "packages/backend": { + "name": "@sentence-structure-diagram-app/backend", + "version": "0.1.0", + "dependencies": { + "@google/genai": "^1.33.0", + "@sentence-structure-diagram-app/evaluation": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", + "@trpc/server": "^11.7.2", + "cors": "^2.8.5", + "express": "^5.2.1", + "zod": "^4.1.13" + }, + "devDependencies": { + "@types/cors": "^2.8.19", + "@types/express": "^5.0.6", + "@types/node": "^24.10.3", + "tsx": "^4.21.0", + "typescript": "^5.9.3" + } + }, + "packages/evaluation": { + "name": "@sentence-structure-diagram-app/evaluation", + "version": "0.1.0", + "dependencies": { + "@google/genai": "^1.34.0", + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-configurations": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-svg": "^0.1.0", + "openai": "^6.15.0", + "zod": "^4.2.1" + }, + "devDependencies": { + "@types/node": "^24.10.4", + "tsx": "^4.21.0", + "typescript": "^5.9.3" + } + }, + "packages/frontend": { + "name": "@sentence-structure-diagram-app/frontend", + "version": "0.1.0", + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/icons-material": "^7.3.6", + "@mui/material": "^7.3.6", + "@sentence-structure-diagram-app/backend": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-data": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-svg": "^0.1.0", + "@tanstack/react-query": "^5.90.12", + "@trpc/client": "^11.7.2", + "@trpc/tanstack-react-query": "^11.7.2", + "react": "^19.2.0", + "react-dom": "^19.2.0" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@types/node": "^24.10.1", + "@types/react": "^19.2.5", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.46.4", + "vite": "^7.2.4" + } + }, + "packages/sentence-structure-data": { + "name": "@sentence-structure-diagram-app/sentence-structure-data", + "version": "0.1.0", + "dependencies": { + "compromise": "^14.14.4", + "fast-xml-parser": "^5.3.2", + "zod": "^4.1.13" + }, + "devDependencies": { + "typescript": "^5.9.3" + } + }, + "packages/sentence-structure-diagram-configurations": { + "name": "@sentence-structure-diagram-app/sentence-structure-diagram-configurations", + "version": "0.1.0", + "dependencies": { + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", + "fast-xml-parser": "^5.3.3", + "zod": "^4.2.1" + }, + "devDependencies": { + "typescript": "^5.9.3" + } + }, + "packages/sentence-structure-diagram-data": { + "name": "@sentence-structure-diagram-app/sentence-structure-diagram-data", + "version": "0.1.0", + "dependencies": { + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-configurations": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-tree": "^0.1.0", + "fast-xml-parser": "^5.3.2", + "zod": "^4.1.13" + }, + "devDependencies": { + "typescript": "^5.9.3" + } + }, + "packages/sentence-structure-diagram-svg": { + "name": "@sentence-structure-diagram-app/sentence-structure-diagram-svg", + "version": "0.1.0", + "dependencies": { + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-configurations": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-data": "^0.1.0", + "prettier": "^3.7.4", + "react": "^19.2.3", + "react-dom": "^19.2.3" + }, + "devDependencies": { + "typescript": "^5.9.3" + } + }, + "packages/sentence-structure-diagram-tree": { + "name": "@sentence-structure-diagram-app/sentence-structure-diagram-tree", + "version": "0.1.0", + "dependencies": { + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-configurations": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-tree": "^0.1.0" + }, + "devDependencies": { + "typescript": "^5.9.3" + } + }, + "packages/sentence-structure-tree": { + "name": "@sentence-structure-diagram-app/sentence-structure-tree", + "version": "0.1.0", + "dependencies": { + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0" + }, + "devDependencies": { + "typescript": "^5.9.3" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..70c57cc --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "sentence-structure-diagram-app", + "version": "0.1.0", + "type": "module", + "scripts": { + "build:packages": "npm run build --workspace=packages/sentence-structure-data && npm run build --workspace=packages/sentence-structure-tree && npm run build --workspace=packages/sentence-structure-diagram-configurations && npm run build --workspace=packages/sentence-structure-diagram-tree && npm run build --workspace=packages/sentence-structure-diagram-data && npm run build --workspace=packages/sentence-structure-diagram-svg", + "build": "npm run build:packages && npm run build --workspace=packages/evaluation && npm run build --workspace=packages/backend && npm run build --workspace=packages/frontend", + "format": "prettier --write .", + "format:check": "prettier --check .", + "clean": "npm run clean --workspaces" + }, + "devDependencies": { + "prettier": "^3.7.4" + }, + "workspaces": [ + "packages/backend", + "packages/frontend", + "packages/sentence-structure-data", + "packages/sentence-structure-diagram-data", + "packages/sentence-structure-diagram-svg", + "packages/evaluation", + "packages/sentence-structure-diagram-configurations", + "packages/sentence-structure-tree", + "packages/sentence-structure-diagram-tree" + ] +} diff --git a/packages/backend/.env.sample b/packages/backend/.env.sample new file mode 100644 index 0000000..623d822 --- /dev/null +++ b/packages/backend/.env.sample @@ -0,0 +1,3 @@ +WEB_ORIGIN=http://localhost:5173 + +GEMINI_API_KEY= diff --git a/packages/backend/.gitignore b/packages/backend/.gitignore new file mode 100644 index 0000000..22a1e06 --- /dev/null +++ b/packages/backend/.gitignore @@ -0,0 +1,2 @@ +/dist +/.env diff --git a/packages/backend/_app.ts b/packages/backend/_app.ts new file mode 100644 index 0000000..593da96 --- /dev/null +++ b/packages/backend/_app.ts @@ -0,0 +1,25 @@ +import { publicProcedure, router } from "./trpc.js"; +import * as z from "zod"; +import { SimplifiedAnnotationDataSchema } from "@sentence-structure-diagram-app/sentence-structure-data"; +import { generateSimplifiedAnnotationDataByGemini } from "./generate-simplified-annotation-by-gemini.js"; + +export const appRouter = router({ + status: publicProcedure + .output(z.object({ status: z.literal("ok") })) + .query(() => ({ status: "ok" })), + generateSentenceStructure: publicProcedure + .input( + z.object({ + text: z.string(), + words: z.array(z.string()), + }), + ) + .output(SimplifiedAnnotationDataSchema) + .mutation(async (opts) => { + const text = opts.input.text; + const words = opts.input.words; + console.log(`Input text: ${text}`); + + return await generateSimplifiedAnnotationDataByGemini(text, words); + }), +}); diff --git a/packages/backend/generate-simplified-annotation-by-gemini.ts b/packages/backend/generate-simplified-annotation-by-gemini.ts new file mode 100644 index 0000000..5a506dc --- /dev/null +++ b/packages/backend/generate-simplified-annotation-by-gemini.ts @@ -0,0 +1,43 @@ +import { GoogleGenAI } from "@google/genai"; +import { + SimplifiedAnnotationDataSchema, + type SimplifiedAnnotationData, +} from "@sentence-structure-diagram-app/sentence-structure-data"; +import { generateGeminiPrompt } from "@sentence-structure-diagram-app/evaluation"; + +const ai = new GoogleGenAI({}); + +export async function generateSimplifiedAnnotationDataByGemini( + text: string, + words: string[], +): Promise { + const prompt = generateGeminiPrompt(text, words); + try { + const result = await ai.models.generateContent({ + model: "gemini-2.5-flash", + contents: prompt.userInput, + config: { + systemInstruction: prompt.systemInstruction, + responseMimeType: "application/json", + // responseJsonSchema: z.toJSONSchema(SimplifiedAnnotationDataSchema), + }, + }); + if (!result.text) throw new Error("AI returned no result"); + const json = (() => { + try { + return JSON.parse(result.text); + } catch { + throw new Error("AI returned invalid JSON"); + } + })(); + console.log("Generated JSON:", JSON.stringify(json, null, 2)); + try { + return SimplifiedAnnotationDataSchema.parse(json); + } catch { + throw new Error("AI returned JSON with invalid schema"); + } + } catch (error) { + console.error(error); + throw error; + } +} diff --git a/packages/backend/index.ts b/packages/backend/index.ts new file mode 100644 index 0000000..710c71f --- /dev/null +++ b/packages/backend/index.ts @@ -0,0 +1,22 @@ +import express from "express"; +import cors from "cors"; +import * as trpcExpress from "@trpc/server/adapters/express"; +import { createContext } from "./trpc.js"; +import { appRouter } from "./_app.js"; + +export type AppRouter = typeof appRouter; + +const app = express(); + +app.use( + "/trpc", + trpcExpress.createExpressMiddleware({ + middleware: cors({ origin: process.env.WEB_ORIGIN }), + router: appRouter, + createContext, + }), +); + +app.listen(3000, () => { + console.log("Server is running on port 3000"); +}); diff --git a/packages/backend/package.json b/packages/backend/package.json new file mode 100644 index 0000000..d3df95f --- /dev/null +++ b/packages/backend/package.json @@ -0,0 +1,28 @@ +{ + "name": "@sentence-structure-diagram-app/backend", + "version": "0.1.0", + "type": "module", + "main": "./dist/index.js", + "scripts": { + "dev": "tsx watch --env-file=.env index.ts", + "build": "tsc", + "start": "node dist/index.js", + "clean": "rm -r dist" + }, + "dependencies": { + "@google/genai": "^1.33.0", + "@sentence-structure-diagram-app/evaluation": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", + "@trpc/server": "^11.7.2", + "cors": "^2.8.5", + "express": "^5.2.1", + "zod": "^4.1.13" + }, + "devDependencies": { + "@types/cors": "^2.8.19", + "@types/express": "^5.0.6", + "@types/node": "^24.10.3", + "tsx": "^4.21.0", + "typescript": "^5.9.3" + } +} diff --git a/packages/backend/trpc.ts b/packages/backend/trpc.ts new file mode 100644 index 0000000..6ac8e24 --- /dev/null +++ b/packages/backend/trpc.ts @@ -0,0 +1,13 @@ +import { initTRPC } from "@trpc/server"; +import * as trpcExpress from "@trpc/server/adapters/express"; + +export const createContext = ({ + req, + res, +}: trpcExpress.CreateExpressContextOptions) => ({}); +type Context = Awaited>; + +const t = initTRPC.context().create(); + +export const router = t.router; +export const publicProcedure = t.procedure; diff --git a/packages/backend/tsconfig.json b/packages/backend/tsconfig.json new file mode 100644 index 0000000..d6a2b39 --- /dev/null +++ b/packages/backend/tsconfig.json @@ -0,0 +1,44 @@ +{ + // Visit https://aka.ms/tsconfig to read more about this file + "compilerOptions": { + // File Layout + // "rootDir": "./src", + "outDir": "./dist", + + // Environment Settings + // See also https://aka.ms/tsconfig/module + "module": "nodenext", + "target": "esnext", + "types": [], + // For nodejs: + // "lib": ["esnext"], + // "types": ["node"], + // and npm install -D @types/node + + // Other Outputs + "sourceMap": true, + "declaration": true, + "declarationMap": true, + + // Stricter Typechecking Options + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + + // Style Options + // "noImplicitReturns": true, + // "noImplicitOverride": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + // "noFallthroughCasesInSwitch": true, + // "noPropertyAccessFromIndexSignature": true, + + // Recommended Options + "strict": true, + "jsx": "react-jsx", + "verbatimModuleSyntax": true, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "skipLibCheck": true + } +} diff --git a/packages/evaluation/.env.sample b/packages/evaluation/.env.sample new file mode 100644 index 0000000..e70c68d --- /dev/null +++ b/packages/evaluation/.env.sample @@ -0,0 +1,9 @@ +GEMINI_API_KEY= + +AZURE_OPENAI_ENDPOINT= +AZURE_OPENAI_API_KEY= +AZURE_OPENAI_API_VERSION= +AZURE_OPENAI_GPT5_DEPLOYMENT_NAME= +AZURE_OPENAI_GPT5_1_DEPLOYMENT_NAME= +AZURE_OPENAI_GPT5_2_DEPLOYMENT_NAME= +AZURE_OPENAI_LLAMA4_DEPLOYMENT_NAME= diff --git a/packages/evaluation/.gitignore b/packages/evaluation/.gitignore new file mode 100644 index 0000000..86d3f12 --- /dev/null +++ b/packages/evaluation/.gitignore @@ -0,0 +1,4 @@ +/dist +/.env +/data +/output diff --git a/packages/evaluation/calculate-metrics.ts b/packages/evaluation/calculate-metrics.ts new file mode 100644 index 0000000..ff35a7a --- /dev/null +++ b/packages/evaluation/calculate-metrics.ts @@ -0,0 +1,286 @@ +import { + findRangeById, + type Range, + type SentenceStructureData, +} from "@sentence-structure-diagram-app/sentence-structure-data"; + +function isSameRange(range1: Range, range2: Range): boolean { + return ( + range1.startWordIndex === range2.startWordIndex && + range1.endWordIndex === range2.endWordIndex && + range1.type === range2.type && + (range1.type === "relation" || + (range2.type !== "relation" && + range1.sentenceElementName === range2.sentenceElementName)) + ); +} + +function isSameRangeStartAndEndWordIndex( + range1: Range, + range2: Range, +): boolean { + return ( + range1.startWordIndex === range2.startWordIndex && + range1.endWordIndex === range2.endWordIndex + ); +} + +type CompareResult = { + answerRangeCount: number; + answerRelationCount: number; + answerCoordinationCount: number; + llmAnswerRangeCount: number; + llmAnswerRelationCount: number; + llmAnswerCoordinationCount: number; + correctRangeCount: number; + correctRangeStartAndEndWordIndexCount: number; + correctRelationCount: number; + correctRelationStartAndEndWordIndexCount: number; + correctCoordinationCount: number; + correctCoordinationStartAndEndWordIndexCount: number; +}; + +function compareSentenceStructureData( + answer: SentenceStructureData, + llmAnswer: SentenceStructureData | null, +): CompareResult { + return { + answerRangeCount: answer.ranges.length, + answerRelationCount: answer.relations.length, + answerCoordinationCount: answer.coordinations.length, + llmAnswerRangeCount: llmAnswer?.ranges.length ?? 0, + llmAnswerRelationCount: llmAnswer?.relations.length ?? 0, + llmAnswerCoordinationCount: llmAnswer?.coordinations.length ?? 0, + correctRangeCount: + llmAnswer?.ranges.filter((llmRange) => + answer.ranges.some((answerRange) => isSameRange(llmRange, answerRange)), + ).length ?? 0, + correctRangeStartAndEndWordIndexCount: + llmAnswer?.ranges.filter((llmRange) => + answer.ranges.some((answerRange) => + isSameRangeStartAndEndWordIndex(llmRange, answerRange), + ), + ).length ?? 0, + correctRelationCount: + llmAnswer?.relations.filter((llmRelation) => + answer.relations.some( + (answerRelation) => + isSameRange( + findRangeById(llmAnswer, { rangeId: llmRelation.fromRangeId })!, + findRangeById(answer, { rangeId: answerRelation.fromRangeId })!, + ) && + isSameRange( + findRangeById(llmAnswer, { rangeId: llmRelation.toRangeId })!, + findRangeById(answer, { rangeId: answerRelation.toRangeId })!, + ), + ), + ).length ?? 0, + correctRelationStartAndEndWordIndexCount: + llmAnswer?.relations.filter((llmRelation) => + answer.relations.some( + (answerRelation) => + isSameRangeStartAndEndWordIndex( + findRangeById(llmAnswer, { rangeId: llmRelation.fromRangeId })!, + findRangeById(answer, { rangeId: answerRelation.fromRangeId })!, + ) && + isSameRangeStartAndEndWordIndex( + findRangeById(llmAnswer, { rangeId: llmRelation.toRangeId })!, + findRangeById(answer, { rangeId: answerRelation.toRangeId })!, + ), + ), + ).length ?? 0, + correctCoordinationCount: + llmAnswer?.coordinations.filter((llmCoordination) => + answer.coordinations.some( + (answerCoordination) => + llmCoordination.children.every( + (llmCoordinationChild) => + llmCoordinationChild.startWordIndex === + answerCoordination.children.at(llmCoordinationChild.index) + ?.startWordIndex && + llmCoordinationChild.endWordIndex === + answerCoordination.children.at(llmCoordinationChild.index) + ?.endWordIndex && + llmCoordinationChild.type === + answerCoordination.children.at(llmCoordinationChild.index) + ?.type, + ) && + llmCoordination.children.length === + answerCoordination.children.length, + ), + ).length ?? 0, + correctCoordinationStartAndEndWordIndexCount: + llmAnswer?.coordinations.filter((llmCoordination) => + answer.coordinations.some((answerCoordination) => + llmCoordination.children.every( + (llmCoordinationChild) => + llmCoordinationChild.startWordIndex === + answerCoordination.children.at(llmCoordinationChild.index) + ?.startWordIndex && + llmCoordinationChild.endWordIndex === + answerCoordination.children.at(llmCoordinationChild.index) + ?.endWordIndex, + ), + ), + ).length ?? 0, + }; +} + +function calculateMetrics( + truePositive: number, + falsePositive: number, + falseNegative: number, +) { + const recall = truePositive / (truePositive + falseNegative); + const precision = truePositive / (truePositive + falsePositive); + const f1Score = + recall === 0 && precision === 0 + ? 0 + : (2 * precision * recall) / (precision + recall); + return { + count: truePositive + falseNegative, + recall, + precision, + f1Score, + }; +} + +export function calculateOverallMetrics( + answerLlMAnswerPairs: { + answer: SentenceStructureData; + llmAnswer: SentenceStructureData | null; + }[], +) { + const compareResults = answerLlMAnswerPairs.map(({ answer, llmAnswer }) => + compareSentenceStructureData(answer, llmAnswer), + ); + + const totalResult: CompareResult = compareResults.reduce( + (accumulator, currentValue) => { + return { + answerRangeCount: + accumulator.answerRangeCount + currentValue.answerRangeCount, + answerRelationCount: + accumulator.answerRelationCount + currentValue.answerRelationCount, + answerCoordinationCount: + accumulator.answerCoordinationCount + + currentValue.answerCoordinationCount, + llmAnswerRangeCount: + accumulator.llmAnswerRangeCount + currentValue.llmAnswerRangeCount, + llmAnswerRelationCount: + accumulator.llmAnswerRelationCount + + currentValue.llmAnswerRelationCount, + llmAnswerCoordinationCount: + accumulator.llmAnswerCoordinationCount + + currentValue.llmAnswerCoordinationCount, + correctRangeCount: + accumulator.correctRangeCount + currentValue.correctRangeCount, + correctRangeStartAndEndWordIndexCount: + accumulator.correctRangeStartAndEndWordIndexCount + + currentValue.correctRangeStartAndEndWordIndexCount, + correctRelationCount: + accumulator.correctRelationCount + currentValue.correctRelationCount, + correctRelationStartAndEndWordIndexCount: + accumulator.correctRelationStartAndEndWordIndexCount + + currentValue.correctRelationStartAndEndWordIndexCount, + correctCoordinationCount: + accumulator.correctCoordinationCount + + currentValue.correctCoordinationCount, + correctCoordinationStartAndEndWordIndexCount: + accumulator.correctCoordinationStartAndEndWordIndexCount + + currentValue.correctCoordinationStartAndEndWordIndexCount, + }; + }, + { + answerRangeCount: 0, + answerRelationCount: 0, + answerCoordinationCount: 0, + llmAnswerRangeCount: 0, + llmAnswerRelationCount: 0, + llmAnswerCoordinationCount: 0, + correctRangeCount: 0, + correctRangeStartAndEndWordIndexCount: 0, + correctRelationCount: 0, + correctRelationStartAndEndWordIndexCount: 0, + correctCoordinationCount: 0, + correctCoordinationStartAndEndWordIndexCount: 0, + }, + ); + + const rangeMetrics = calculateMetrics( + totalResult.correctRangeCount, + totalResult.llmAnswerRangeCount - totalResult.correctRangeCount, + totalResult.answerRangeCount - totalResult.correctRangeCount, + ); + + const rangeStartAndEndWordIndexMetrics = calculateMetrics( + totalResult.correctRangeStartAndEndWordIndexCount, + totalResult.llmAnswerRangeCount - + totalResult.correctRangeStartAndEndWordIndexCount, + totalResult.answerRangeCount - + totalResult.correctRangeStartAndEndWordIndexCount, + ); + + const relationMetrics = calculateMetrics( + totalResult.correctRelationCount, + totalResult.llmAnswerRelationCount - totalResult.correctRelationCount, + totalResult.answerRelationCount - totalResult.correctRelationCount, + ); + + const relationStartAndEndWordIndexMetrics = calculateMetrics( + totalResult.correctRelationStartAndEndWordIndexCount, + totalResult.llmAnswerRelationCount - + totalResult.correctRelationStartAndEndWordIndexCount, + totalResult.answerRelationCount - + totalResult.correctRelationStartAndEndWordIndexCount, + ); + + const coordinationMetrics = calculateMetrics( + totalResult.correctCoordinationCount, + totalResult.llmAnswerCoordinationCount - + totalResult.correctCoordinationCount, + totalResult.answerCoordinationCount - totalResult.correctCoordinationCount, + ); + + const coordinationStartAndEndWordIndexMetrics = calculateMetrics( + totalResult.correctCoordinationStartAndEndWordIndexCount, + totalResult.llmAnswerCoordinationCount - + totalResult.correctCoordinationStartAndEndWordIndexCount, + totalResult.answerCoordinationCount - + totalResult.correctCoordinationStartAndEndWordIndexCount, + ); + + return { + generationRate: + answerLlMAnswerPairs.filter(({ llmAnswer }) => llmAnswer !== null) + .length / answerLlMAnswerPairs.length, + rangeCount: rangeMetrics.count, + rangeRecall: rangeMetrics.recall, + rangePrecision: rangeMetrics.precision, + rangeF1Score: rangeMetrics.f1Score, + rangeStartAndEndWordIndexRecall: rangeStartAndEndWordIndexMetrics.recall, + rangeStartAndEndWordIndexPrecision: + rangeStartAndEndWordIndexMetrics.precision, + rangeStartAndEndWordIndexF1Score: rangeStartAndEndWordIndexMetrics.f1Score, + relationCount: relationMetrics.count, + relationRecall: relationMetrics.recall, + relationPrecision: relationMetrics.precision, + relationF1Score: relationMetrics.f1Score, + relationStartAndEndWordIndexRecall: + relationStartAndEndWordIndexMetrics.recall, + relationStartAndEndWordIndexPrecision: + relationStartAndEndWordIndexMetrics.precision, + relationStartAndEndWordIndexF1Score: + relationStartAndEndWordIndexMetrics.f1Score, + coordinationCount: coordinationMetrics.count, + coordinationRecall: coordinationMetrics.recall, + coordinationPrecision: coordinationMetrics.precision, + coordinationF1Score: coordinationMetrics.f1Score, + coordinationStartAndEndWordIndexRecall: + coordinationStartAndEndWordIndexMetrics.recall, + coordinationStartAndEndWordIndexPrecision: + coordinationStartAndEndWordIndexMetrics.precision, + coordinationStartAndEndWordIndexF1Score: + coordinationStartAndEndWordIndexMetrics.f1Score, + }; +} diff --git a/packages/evaluation/examples.ts b/packages/evaluation/examples.ts new file mode 100644 index 0000000..62286bc --- /dev/null +++ b/packages/evaluation/examples.ts @@ -0,0 +1,718 @@ +import type { SimplifiedSentenceStructureData } from "@sentence-structure-diagram-app/sentence-structure-data"; + +export const examples: SimplifiedSentenceStructureData[] = [ + { + text: "The girl who likes to draw pictures after school has been quietly improving her skills for the past two years.", + words: [ + { + index: 0, + text: "The", + }, + { + index: 1, + text: "girl", + }, + { + index: 2, + text: "who", + }, + { + index: 3, + text: "likes", + }, + { + index: 4, + text: "to", + }, + { + index: 5, + text: "draw", + }, + { + index: 6, + text: "pictures", + }, + { + index: 7, + text: "after", + }, + { + index: 8, + text: "school", + }, + { + index: 9, + text: "has", + }, + { + index: 10, + text: "been", + }, + { + index: 11, + text: "quietly", + }, + { + index: 12, + text: "improving", + }, + { + index: 13, + text: "her", + }, + { + index: 14, + text: "skills", + }, + { + index: 15, + text: "for", + }, + { + index: 16, + text: "the", + }, + { + index: 17, + text: "past", + }, + { + index: 18, + text: "two", + }, + { + index: 19, + text: "years", + }, + { + index: 20, + text: ".", + }, + ], + ranges: [ + { + type: "文の主要素", + index: 0, + startWordIndex: 0, + endWordIndex: 1, + sentenceElementName: "S", + }, + { + type: "節", + index: 1, + startWordIndex: 2, + endWordIndex: 8, + sentenceElementName: "M", + }, + { + type: "文の主要素", + index: 2, + startWordIndex: 3, + endWordIndex: 3, + sentenceElementName: "V", + }, + { + type: "句", + index: 3, + startWordIndex: 4, + endWordIndex: 8, + sentenceElementName: "O", + }, + { + type: "文の主要素", + index: 4, + startWordIndex: 5, + endWordIndex: 5, + sentenceElementName: "V", + }, + { + type: "文の主要素", + index: 5, + startWordIndex: 6, + endWordIndex: 6, + sentenceElementName: "O", + }, + { + type: "修飾語", + index: 6, + startWordIndex: 7, + endWordIndex: 8, + sentenceElementName: "M", + }, + { + type: "文の主要素", + index: 7, + startWordIndex: 9, + endWordIndex: 12, + sentenceElementName: "V", + }, + { + type: "修飾語", + index: 8, + startWordIndex: 11, + endWordIndex: 11, + sentenceElementName: "M", + }, + { + type: "文の主要素", + index: 9, + startWordIndex: 13, + endWordIndex: 14, + sentenceElementName: "O", + }, + { + type: "修飾語", + index: 10, + startWordIndex: 15, + endWordIndex: 19, + sentenceElementName: "M", + }, + ], + relations: [ + { + fromRangeIndex: 1, + toRangeIndex: 0, + }, + ], + coordinations: [], + }, + { + text: "The detailed handwritten notes from the lesson the teacher explained on the first day were helpful for everyone.", + words: [ + { + index: 0, + text: "The", + }, + { + index: 1, + text: "detailed", + }, + { + index: 2, + text: "handwritten", + }, + { + index: 3, + text: "notes", + }, + { + index: 4, + text: "from", + }, + { + index: 5, + text: "the", + }, + { + index: 6, + text: "lesson", + }, + { + index: 7, + text: "the", + }, + { + index: 8, + text: "teacher", + }, + { + index: 9, + text: "explained", + }, + { + index: 10, + text: "on", + }, + { + index: 11, + text: "the", + }, + { + index: 12, + text: "first", + }, + { + index: 13, + text: "day", + }, + { + index: 14, + text: "were", + }, + { + index: 15, + text: "helpful", + }, + { + index: 16, + text: "for", + }, + { + index: 17, + text: "everyone", + }, + { + index: 18, + text: ".", + }, + ], + ranges: [ + { + type: "文の主要素", + index: 0, + startWordIndex: 0, + endWordIndex: 3, + sentenceElementName: "S", + }, + { + type: "修飾語", + index: 1, + startWordIndex: 4, + endWordIndex: 13, + sentenceElementName: "M", + }, + { + type: "節", + index: 2, + startWordIndex: 7, + endWordIndex: 13, + sentenceElementName: "M", + }, + { + type: "文の主要素", + index: 3, + startWordIndex: 7, + endWordIndex: 8, + sentenceElementName: "S", + }, + { + type: "文の主要素", + index: 4, + startWordIndex: 9, + endWordIndex: 9, + sentenceElementName: "V", + }, + { + type: "修飾語", + index: 5, + startWordIndex: 10, + endWordIndex: 13, + sentenceElementName: "M", + }, + { + type: "文の主要素", + index: 6, + startWordIndex: 14, + endWordIndex: 14, + sentenceElementName: "V", + }, + { + type: "文の主要素", + index: 7, + startWordIndex: 15, + endWordIndex: 15, + sentenceElementName: "C", + }, + { + type: "修飾語", + index: 8, + startWordIndex: 16, + endWordIndex: 17, + sentenceElementName: "M", + }, + { + type: "関係", + index: 9, + startWordIndex: 5, + endWordIndex: 6, + }, + ], + relations: [ + { + fromRangeIndex: 2, + toRangeIndex: 9, + }, + ], + coordinations: [], + }, + { + text: "When the weather is nice, we play soccer and visit our friends and neighbors.", + words: [ + { + index: 0, + text: "When", + }, + { + index: 1, + text: "the", + }, + { + index: 2, + text: "weather", + }, + { + index: 3, + text: "is", + }, + { + index: 4, + text: "nice", + }, + { + index: 5, + text: ",", + }, + { + index: 6, + text: "we", + }, + { + index: 7, + text: "play", + }, + { + index: 8, + text: "soccer", + }, + { + index: 9, + text: "and", + }, + { + index: 10, + text: "visit", + }, + { + index: 11, + text: "our", + }, + { + index: 12, + text: "friends", + }, + { + index: 13, + text: "and", + }, + { + index: 14, + text: "neighbors", + }, + { + index: 15, + text: ".", + }, + ], + ranges: [ + { + type: "節", + index: 0, + startWordIndex: 0, + endWordIndex: 4, + sentenceElementName: "M", + }, + { + type: "文の主要素", + index: 1, + startWordIndex: 1, + endWordIndex: 2, + sentenceElementName: "S", + }, + { + type: "文の主要素", + index: 2, + startWordIndex: 3, + endWordIndex: 3, + sentenceElementName: "V", + }, + { + type: "文の主要素", + index: 3, + startWordIndex: 4, + endWordIndex: 4, + sentenceElementName: "C", + }, + { + type: "文の主要素", + index: 4, + startWordIndex: 6, + endWordIndex: 6, + sentenceElementName: "S", + }, + { + type: "文の主要素", + index: 5, + startWordIndex: 7, + endWordIndex: 7, + sentenceElementName: "V", + }, + { + type: "文の主要素", + index: 6, + startWordIndex: 8, + endWordIndex: 8, + sentenceElementName: "O", + }, + { + type: "文の主要素", + index: 7, + startWordIndex: 10, + endWordIndex: 10, + sentenceElementName: "V", + }, + { + type: "文の主要素", + index: 8, + startWordIndex: 12, + endWordIndex: 12, + sentenceElementName: "O", + }, + { + type: "文の主要素", + index: 9, + startWordIndex: 14, + endWordIndex: 14, + sentenceElementName: "O", + }, + ], + relations: [], + coordinations: [ + { + children: [ + { + type: "並列要素", + startWordIndex: 7, + endWordIndex: 8, + }, + { + type: "等位接続詞", + startWordIndex: 9, + endWordIndex: 9, + }, + { + type: "並列要素", + startWordIndex: 10, + endWordIndex: 14, + }, + ], + }, + { + children: [ + { + type: "並列要素", + startWordIndex: 12, + endWordIndex: 12, + }, + { + type: "等位接続詞", + startWordIndex: 13, + endWordIndex: 13, + }, + { + type: "並列要素", + startWordIndex: 14, + endWordIndex: 14, + }, + ], + }, + ], + }, + { + text: "She not only bought apples, oranges, and bananas, but also got some bread on her way home.", + words: [ + { + index: 0, + text: "She", + }, + { + index: 1, + text: "not", + }, + { + index: 2, + text: "only", + }, + { + index: 3, + text: "bought", + }, + { + index: 4, + text: "apples", + }, + { + index: 5, + text: ",", + }, + { + index: 6, + text: "oranges", + }, + { + index: 7, + text: ",", + }, + { + index: 8, + text: "and", + }, + { + index: 9, + text: "bananas", + }, + { + index: 10, + text: ",", + }, + { + index: 11, + text: "but", + }, + { + index: 12, + text: "also", + }, + { + index: 13, + text: "got", + }, + { + index: 14, + text: "some", + }, + { + index: 15, + text: "bread", + }, + { + index: 16, + text: "on", + }, + { + index: 17, + text: "her", + }, + { + index: 18, + text: "way", + }, + { + index: 19, + text: "home", + }, + { + index: 20, + text: ".", + }, + ], + ranges: [ + { + type: "文の主要素", + index: 0, + startWordIndex: 0, + endWordIndex: 0, + sentenceElementName: "S", + }, + { + type: "文の主要素", + index: 1, + startWordIndex: 3, + endWordIndex: 3, + sentenceElementName: "V", + }, + { + type: "文の主要素", + index: 2, + startWordIndex: 4, + endWordIndex: 4, + sentenceElementName: "O", + }, + { + type: "文の主要素", + index: 3, + startWordIndex: 6, + endWordIndex: 6, + sentenceElementName: "O", + }, + { + type: "文の主要素", + index: 4, + startWordIndex: 9, + endWordIndex: 9, + sentenceElementName: "O", + }, + { + type: "文の主要素", + index: 5, + startWordIndex: 13, + endWordIndex: 13, + sentenceElementName: "V", + }, + { + type: "文の主要素", + index: 6, + startWordIndex: 14, + endWordIndex: 15, + sentenceElementName: "O", + }, + { + type: "修飾語", + index: 7, + startWordIndex: 16, + endWordIndex: 19, + sentenceElementName: "M", + }, + ], + relations: [], + coordinations: [ + { + children: [ + { + type: "相関接続詞", + startWordIndex: 1, + endWordIndex: 2, + }, + { + type: "並列要素", + startWordIndex: 3, + endWordIndex: 10, + }, + { + type: "相関接続詞", + startWordIndex: 11, + endWordIndex: 12, + }, + { + type: "並列要素", + startWordIndex: 13, + endWordIndex: 19, + }, + ], + }, + { + children: [ + { + type: "並列要素", + startWordIndex: 4, + endWordIndex: 5, + }, + { + type: "並列要素", + startWordIndex: 6, + endWordIndex: 7, + }, + { + type: "等位接続詞", + startWordIndex: 8, + endWordIndex: 8, + }, + { + type: "並列要素", + startWordIndex: 9, + endWordIndex: 9, + }, + ], + }, + ], + }, +] satisfies SimplifiedSentenceStructureData[]; diff --git a/packages/evaluation/generate-by-gemini.ts b/packages/evaluation/generate-by-gemini.ts new file mode 100644 index 0000000..272b91b --- /dev/null +++ b/packages/evaluation/generate-by-gemini.ts @@ -0,0 +1,159 @@ +import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { sentenceStructureDataToString } from "@sentence-structure-diagram-app/sentence-structure-data"; +import { defaultConfigurations } from "@sentence-structure-diagram-app/sentence-structure-diagram-configurations"; +import { generateSvgString } from "@sentence-structure-diagram-app/sentence-structure-diagram-svg"; +import { + geminiModelNames, + geminiTemperatureValues, + geminiThinkingLevelValues, + isValidGeminiParameters, + retryCount, + runCount, + type Dataset, + type GeminiLog, + type GeminiModelName, + type GeminiTemperature, + type GeminiThinkingLevel, +} from "./llmConfigurations.js"; +import { generateSentenceStructureDataByGemini } from "./generate-sentence-structure-data-by-gemini.js"; + +const args = process.argv.slice(2); +const modelName = args + .find((arg) => arg.startsWith("--model=")) + ?.split("=")[1] as GeminiModelName | undefined; +if (!modelName || !geminiModelNames.includes(modelName)) + throw new Error("Please specify a valid model name with --model="); + +type RunConfiguration = { + modelName: GeminiModelName; + temperature: GeminiTemperature; + thinkingLevel: GeminiThinkingLevel; + runIndex: number; +}; + +const runConfigurations: RunConfiguration[] = geminiTemperatureValues.flatMap( + (temperature) => + geminiThinkingLevelValues.flatMap((thinkingLevel) => { + if (!isValidGeminiParameters(modelName, temperature, thinkingLevel)) + return []; + return Array.from({ length: runCount }, (_, runIndex) => ({ + modelName, + temperature, + thinkingLevel, + runIndex, + })); + }), +); + +const datasets: Dataset[] = JSON.parse( + readFileSync(`${import.meta.dirname}/data/datasets.json`, "utf-8"), +); + +await Promise.all( + runConfigurations.map( + async ({ modelName, temperature, thinkingLevel, runIndex }) => { + const directoryPath = `${import.meta.dirname}/output/${modelName}-${temperature}-${thinkingLevel.toLowerCase()}-${runIndex}`; + if (!existsSync(directoryPath)) { + mkdirSync(directoryPath, { recursive: true }); + } + for (const dataset of datasets) { + while (true) { + const logs: GeminiLog[] = existsSync(`${directoryPath}/logs.json`) + ? JSON.parse(readFileSync(`${directoryPath}/logs.json`, "utf-8")) + : []; + const log = logs.find((log) => log.id === dataset.id) ?? null; + const retryIndex = log?.retries.length ?? 0; + if (log?.success || retryCount <= retryIndex) break; + + const startTime = Date.now(); + const result = await generateSentenceStructureDataByGemini( + dataset.englishText, + { + modelName, + temperature, + seed: runIndex * 100 + retryIndex, + thinkingLevel, + }, + ); + const endTime = Date.now(); + + if (result.success) { + writeFileSync( + `${directoryPath}/${dataset.id}.json`, + sentenceStructureDataToString(result.sentenceStructureData), + ); + writeFileSync( + `${directoryPath}/${dataset.id}.svg`, + generateSvgString( + result.sentenceStructureData, + 1500, + (text) => text.length * 8, + defaultConfigurations, + ), + ); + + const existingLog = logs.find((log) => log.id === dataset.id); + if (existingLog) { + existingLog.success = true; + existingLog.retries.push({ + success: true, + processingTime: endTime - startTime, + rawResponse: result.rawResponse, + }); + } else { + logs.push({ + id: dataset.id, + success: true, + englishText: dataset.englishText, + prompt: result.prompt, + retries: [ + { + success: true, + processingTime: endTime - startTime, + rawResponse: result.rawResponse, + }, + ], + }); + } + console.log( + `[model: ${modelName}, temperature: ${temperature}, thinkingLevel: ${thinkingLevel}, run index: ${runIndex}] Processed dataset ID ${dataset.id} successfully.`, + ); + } else { + const existingLog = logs.find((log) => log.id === dataset.id); + if (existingLog) { + existingLog.retries.push({ + success: false, + processingTime: endTime - startTime, + rawResponse: result.rawResponse, + errorMessage: result.errorMessage, + }); + } else { + logs.push({ + id: dataset.id, + success: false, + englishText: dataset.englishText, + prompt: result.prompt, + retries: [ + { + success: false, + processingTime: endTime - startTime, + rawResponse: result.rawResponse, + errorMessage: result.errorMessage, + }, + ], + }); + } + console.log( + `[model: ${modelName}, temperature: ${temperature}, thinkingLevel: ${thinkingLevel}, run index: ${runIndex}] Failed to process dataset ID ${dataset.id}. Retry index: ${retryIndex}.`, + ); + } + + writeFileSync( + `${directoryPath}/logs.json`, + JSON.stringify(logs, null, 2), + ); + } + } + }, + ), +); diff --git a/packages/evaluation/generate-by-openai.ts b/packages/evaluation/generate-by-openai.ts new file mode 100644 index 0000000..f4359b9 --- /dev/null +++ b/packages/evaluation/generate-by-openai.ts @@ -0,0 +1,158 @@ +import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { sentenceStructureDataToString } from "@sentence-structure-diagram-app/sentence-structure-data"; +import { defaultConfigurations } from "@sentence-structure-diagram-app/sentence-structure-diagram-configurations"; +import { generateSvgString } from "@sentence-structure-diagram-app/sentence-structure-diagram-svg"; +import { + isValidOpenAIParameters, + openAIModelNames, + openAIReasoningEffortValues, + openAIVerbosityValues, + retryCount, + runCount, + type Dataset, + type OpenAILog, + type OpenAIModelName, + type OpenAIReasoningEffort, + type OpenAIVerbosity, +} from "./llmConfigurations.js"; +import { generateSentenceStructureDataByOpenAI } from "./generate-sentence-structure-data-by-openai.js"; + +const args = process.argv.slice(2); +const modelName = args + .find((arg) => arg.startsWith("--model=")) + ?.split("=")[1] as OpenAIModelName | undefined; +if (!modelName || !openAIModelNames.includes(modelName)) + throw new Error("Please specify a valid model name with --model="); + +type RunConfiguration = { + modelName: OpenAIModelName; + reasoningEffort: OpenAIReasoningEffort; + verbosity: OpenAIVerbosity; + runIndex: number; +}; + +const runConfigurations: RunConfiguration[] = + openAIReasoningEffortValues.flatMap((reasoningEffort) => + openAIVerbosityValues.flatMap((verbosity) => { + if (!isValidOpenAIParameters(modelName, reasoningEffort, verbosity)) + return []; + return Array.from({ length: runCount }, (_, runIndex) => ({ + modelName, + reasoningEffort, + verbosity, + runIndex, + })); + }), + ); + +const datasets: Dataset[] = JSON.parse( + readFileSync(`${import.meta.dirname}/data/datasets.json`, "utf-8"), +); + +await Promise.all( + runConfigurations.map( + async ({ modelName, reasoningEffort, verbosity, runIndex }) => { + const directoryPath = `${import.meta.dirname}/output/${modelName}-${reasoningEffort}-${verbosity}-${runIndex}`; + if (!existsSync(directoryPath)) { + mkdirSync(directoryPath, { recursive: true }); + } + for (const dataset of datasets) { + while (true) { + const logs: OpenAILog[] = existsSync(`${directoryPath}/logs.json`) + ? JSON.parse(readFileSync(`${directoryPath}/logs.json`, "utf-8")) + : []; + const log = logs.find((log) => log.id === dataset.id) ?? null; + const retryIndex = log?.retries.length ?? 0; + if (log?.success || retryCount <= retryIndex) break; + + const startTime = Date.now(); + const result = await generateSentenceStructureDataByOpenAI( + dataset.englishText, + { + modelName, + reasoningEffort, + verbosity, + }, + ); + const endTime = Date.now(); + + if (result.success) { + writeFileSync( + `${directoryPath}/${dataset.id}.json`, + sentenceStructureDataToString(result.sentenceStructureData), + ); + writeFileSync( + `${directoryPath}/${dataset.id}.svg`, + generateSvgString( + result.sentenceStructureData, + 1500, + (text) => text.length * 8, + defaultConfigurations, + ), + ); + + const existingLog = logs.find((log) => log.id === dataset.id); + if (existingLog) { + existingLog.success = true; + existingLog.retries.push({ + success: true, + processingTime: endTime - startTime, + rawResponse: result.rawResponse, + }); + } else { + logs.push({ + id: dataset.id, + success: true, + englishText: dataset.englishText, + prompt: result.prompt, + retries: [ + { + success: true, + processingTime: endTime - startTime, + rawResponse: result.rawResponse, + }, + ], + }); + } + console.log( + `[model: ${modelName}, reasoning.effort: ${reasoningEffort}, verbosity: ${verbosity}, run index: ${runIndex}] Processed dataset ID ${dataset.id} successfully.`, + ); + } else { + const existingLog = logs.find((log) => log.id === dataset.id); + if (existingLog) { + existingLog.retries.push({ + success: false, + processingTime: endTime - startTime, + rawResponse: result.rawResponse, + errorMessage: result.errorMessage, + }); + } else { + logs.push({ + id: dataset.id, + success: false, + englishText: dataset.englishText, + prompt: result.prompt, + retries: [ + { + success: false, + processingTime: endTime - startTime, + rawResponse: result.rawResponse, + errorMessage: result.errorMessage, + }, + ], + }); + } + console.log( + `[model: ${modelName}, reasoning.effort: ${reasoningEffort}, verbosity: ${verbosity}, run index: ${runIndex}] Failed to process dataset ID ${dataset.id}. Retry index: ${retryIndex}.`, + ); + } + + writeFileSync( + `${directoryPath}/logs.json`, + JSON.stringify(logs, null, 2), + ); + } + } + }, + ), +); diff --git a/packages/evaluation/generate-metrics.ts b/packages/evaluation/generate-metrics.ts new file mode 100644 index 0000000..ba837dd --- /dev/null +++ b/packages/evaluation/generate-metrics.ts @@ -0,0 +1,652 @@ +import { existsSync, readFileSync, writeFileSync } from "node:fs"; +import { + createSentenceStructureDataFromStringData, + type SentenceStructureData, +} from "@sentence-structure-diagram-app/sentence-structure-data"; +import { + runCount, + geminiModelNames, + geminiTemperatureValues, + geminiThinkingLevelValues, + isValidGeminiParameters, + isValidOpenAIParameters, + openAIModelNames, + openAIReasoningEffortValues, + openAIVerbosityValues, + type Dataset, + type GeminiLog, + type OpenAILog, +} from "./llmConfigurations.js"; +import { calculateOverallMetrics } from "./calculate-metrics.js"; + +const datasets: Dataset[] = JSON.parse( + readFileSync(`${import.meta.dirname}/data/datasets.json`, "utf-8"), +); +const answerSentenceStructureDataList: SentenceStructureData[] = datasets.map( + (dataset) => { + const result = createSentenceStructureDataFromStringData( + readFileSync( + `${import.meta.dirname}/data/answer-${dataset.id}.json`, + "utf-8", + ), + ); + if (!result.success) throw new Error("Failed to parse answer data."); + return result.data.newSentenceStructureData; + }, +); + +const headerRow = [ + "モデル名とパラメーター", + "生成成功割合(再試行可)", + "生成成功割合(再試行不可)", + "範囲の総数", + "完全に一致した範囲のRecall(再試行可)", + "完全に一致した範囲のRecall(再試行不可)", + "完全に一致した範囲のPrecision(再試行可)", + "完全に一致した範囲のPrecision(再試行不可)", + "完全に一致した範囲のF1(再試行可)", + "完全に一致した範囲のF1(再試行不可)", + "範囲が一致した範囲のRecall(再試行可)", + "範囲が一致した範囲のRecall(再試行不可)", + "範囲が一致した範囲のPrecision(再試行可)", + "範囲が一致した範囲のPrecision(再試行不可)", + "範囲が一致した範囲のF1(再試行可)", + "範囲が一致した範囲のF1(再試行不可)", + "関係の総数", + "完全に一致した関係のRecall(再試行可)", + "完全に一致した関係のRecall(再試行不可)", + "完全に一致した関係のPrecision(再試行可)", + "完全に一致した関係のPrecision(再試行不可)", + "完全に一致した関係のF1(再試行可)", + "完全に一致した関係のF1(再試行不可)", + "範囲が一致した関係のRecall(再試行可)", + "範囲が一致した関係のRecall(再試行不可)", + "範囲が一致した関係のPrecision(再試行可)", + "範囲が一致した関係のPrecision(再試行不可)", + "範囲が一致した関係のF1(再試行可)", + "範囲が一致した関係のF1(再試行不可)", + "並列構造の総数", + "完全に一致した並列構造のRecall(再試行可)", + "完全に一致した並列構造のRecall(再試行不可)", + "完全に一致した並列構造のPrecision(再試行可)", + "完全に一致した並列構造のPrecision(再試行不可)", + "完全に一致した並列構造のF1(再試行可)", + "完全に一致した並列構造のF1(再試行不可)", + "範囲が一致した並列構造のRecall(再試行可)", + "範囲が一致した並列構造のRecall(再試行不可)", + "範囲が一致した並列構造のPrecision(再試行可)", + "範囲が一致した並列構造のPrecision(再試行不可)", + "範囲が一致した並列構造のF1(再試行可)", + "範囲が一致した並列構造のF1(再試行不可)", +]; + +const metricsCsv: string[][] = [headerRow]; + +for (const modelName of geminiModelNames) { + for (const temperature of geminiTemperatureValues) { + for (const thinkingLevel of geminiThinkingLevelValues) { + if (!isValidGeminiParameters(modelName, temperature, thinkingLevel)) + continue; + + const allTriesMetrics = Array.from( + { length: runCount }, + (_, runIndex) => { + const directoryPath = `${import.meta.dirname}/output/${modelName}-${temperature}-${thinkingLevel.toLowerCase()}-${runIndex}`; + const llmSentenceStructureDataList: (SentenceStructureData | null)[] = + datasets.map((dataset) => { + if (!existsSync(`${directoryPath}/${dataset.id}.json`)) + return null; + const result = createSentenceStructureDataFromStringData( + readFileSync(`${directoryPath}/${dataset.id}.json`, "utf-8"), + ); + if (!result.success) throw new Error("Failed to parse LLM data."); + return result.data.newSentenceStructureData; + }); + return calculateOverallMetrics( + datasets.map((_, index) => ({ + answer: answerSentenceStructureDataList.at(index)!, + llmAnswer: llmSentenceStructureDataList.at(index) ?? null, + })), + ); + }, + ).reduce( + (accumulator, currentValue) => ({ + generationRate: + accumulator.generationRate + currentValue.generationRate / runCount, + rangeCount: + accumulator.rangeCount + currentValue.rangeCount / runCount, + rangeRecall: + accumulator.rangeRecall + currentValue.rangeRecall / runCount, + rangePrecision: + accumulator.rangePrecision + currentValue.rangePrecision / runCount, + rangeF1Score: + accumulator.rangeF1Score + currentValue.rangeF1Score / runCount, + rangeStartAndEndWordIndexRecall: + accumulator.rangeStartAndEndWordIndexRecall + + currentValue.rangeStartAndEndWordIndexRecall / runCount, + rangeStartAndEndWordIndexPrecision: + accumulator.rangeStartAndEndWordIndexPrecision + + currentValue.rangeStartAndEndWordIndexPrecision / runCount, + rangeStartAndEndWordIndexF1Score: + accumulator.rangeStartAndEndWordIndexF1Score + + currentValue.rangeStartAndEndWordIndexF1Score / runCount, + relationCount: + accumulator.relationCount + currentValue.relationCount / runCount, + relationRecall: + accumulator.relationRecall + currentValue.relationRecall / runCount, + relationPrecision: + accumulator.relationPrecision + + currentValue.relationPrecision / runCount, + relationF1Score: + accumulator.relationF1Score + + currentValue.relationF1Score / runCount, + relationStartAndEndWordIndexRecall: + accumulator.relationStartAndEndWordIndexRecall + + currentValue.relationStartAndEndWordIndexRecall / runCount, + relationStartAndEndWordIndexPrecision: + accumulator.relationStartAndEndWordIndexPrecision + + currentValue.relationStartAndEndWordIndexPrecision / runCount, + relationStartAndEndWordIndexF1Score: + accumulator.relationStartAndEndWordIndexF1Score + + currentValue.relationStartAndEndWordIndexF1Score / runCount, + coordinationCount: + accumulator.coordinationCount + + currentValue.coordinationCount / runCount, + coordinationRecall: + accumulator.coordinationRecall + + currentValue.coordinationRecall / runCount, + coordinationPrecision: + accumulator.coordinationPrecision + + currentValue.coordinationPrecision / runCount, + coordinationF1Score: + accumulator.coordinationF1Score + + currentValue.coordinationF1Score / runCount, + coordinationStartAndEndWordIndexRecall: + accumulator.coordinationStartAndEndWordIndexRecall + + currentValue.coordinationStartAndEndWordIndexRecall / runCount, + coordinationStartAndEndWordIndexPrecision: + accumulator.coordinationStartAndEndWordIndexPrecision + + currentValue.coordinationStartAndEndWordIndexPrecision / runCount, + coordinationStartAndEndWordIndexF1Score: + accumulator.coordinationStartAndEndWordIndexF1Score + + currentValue.coordinationStartAndEndWordIndexF1Score / runCount, + }), + { + generationRate: 0, + rangeCount: 0, + rangeRecall: 0, + rangePrecision: 0, + rangeF1Score: 0, + rangeStartAndEndWordIndexRecall: 0, + rangeStartAndEndWordIndexPrecision: 0, + rangeStartAndEndWordIndexF1Score: 0, + relationCount: 0, + relationRecall: 0, + relationPrecision: 0, + relationF1Score: 0, + relationStartAndEndWordIndexRecall: 0, + relationStartAndEndWordIndexPrecision: 0, + relationStartAndEndWordIndexF1Score: 0, + coordinationCount: 0, + coordinationRecall: 0, + coordinationPrecision: 0, + coordinationF1Score: 0, + coordinationStartAndEndWordIndexRecall: 0, + coordinationStartAndEndWordIndexPrecision: 0, + coordinationStartAndEndWordIndexF1Score: 0, + }, + ); + + const firstTryMetrics = Array.from( + { length: runCount }, + (_, runIndex) => { + const directoryPath = `${import.meta.dirname}/output/${modelName}-${temperature}-${thinkingLevel.toLowerCase()}-${runIndex}`; + const logs: GeminiLog[] | null = existsSync( + `${directoryPath}/logs.json`, + ) + ? JSON.parse(readFileSync(`${directoryPath}/logs.json`, "utf-8")) + : null; + const llmSentenceStructureDataList: (SentenceStructureData | null)[] = + datasets.map((dataset) => { + const log = logs?.find((log) => log.id === dataset.id); + if ( + !existsSync(`${directoryPath}/${dataset.id}.json`) || + !log || + 1 < log.retries.length + ) + return null; + const result = createSentenceStructureDataFromStringData( + readFileSync(`${directoryPath}/${dataset.id}.json`, "utf-8"), + ); + if (!result.success) throw new Error("Failed to parse LLM data."); + return result.data.newSentenceStructureData; + }); + return calculateOverallMetrics( + datasets.map((_, index) => ({ + answer: answerSentenceStructureDataList.at(index)!, + llmAnswer: llmSentenceStructureDataList.at(index) ?? null, + })), + ); + }, + ).reduce( + (accumulator, currentValue) => ({ + generationRate: + accumulator.generationRate + currentValue.generationRate / runCount, + rangeCount: + accumulator.rangeCount + currentValue.rangeCount / runCount, + rangeRecall: + accumulator.rangeRecall + currentValue.rangeRecall / runCount, + rangePrecision: + accumulator.rangePrecision + currentValue.rangePrecision / runCount, + rangeF1Score: + accumulator.rangeF1Score + currentValue.rangeF1Score / runCount, + rangeStartAndEndWordIndexRecall: + accumulator.rangeStartAndEndWordIndexRecall + + currentValue.rangeStartAndEndWordIndexRecall / runCount, + rangeStartAndEndWordIndexPrecision: + accumulator.rangeStartAndEndWordIndexPrecision + + currentValue.rangeStartAndEndWordIndexPrecision / runCount, + rangeStartAndEndWordIndexF1Score: + accumulator.rangeStartAndEndWordIndexF1Score + + currentValue.rangeStartAndEndWordIndexF1Score / runCount, + relationCount: + accumulator.relationCount + currentValue.relationCount / runCount, + relationRecall: + accumulator.relationRecall + currentValue.relationRecall / runCount, + relationPrecision: + accumulator.relationPrecision + + currentValue.relationPrecision / runCount, + relationF1Score: + accumulator.relationF1Score + + currentValue.relationF1Score / runCount, + relationStartAndEndWordIndexRecall: + accumulator.relationStartAndEndWordIndexRecall + + currentValue.relationStartAndEndWordIndexRecall / runCount, + relationStartAndEndWordIndexPrecision: + accumulator.relationStartAndEndWordIndexPrecision + + currentValue.relationStartAndEndWordIndexPrecision / runCount, + relationStartAndEndWordIndexF1Score: + accumulator.relationStartAndEndWordIndexF1Score + + currentValue.relationStartAndEndWordIndexF1Score / runCount, + coordinationCount: + accumulator.coordinationCount + + currentValue.coordinationCount / runCount, + coordinationRecall: + accumulator.coordinationRecall + + currentValue.coordinationRecall / runCount, + coordinationPrecision: + accumulator.coordinationPrecision + + currentValue.coordinationPrecision / runCount, + coordinationF1Score: + accumulator.coordinationF1Score + + currentValue.coordinationF1Score / runCount, + coordinationStartAndEndWordIndexRecall: + accumulator.coordinationStartAndEndWordIndexRecall + + currentValue.coordinationStartAndEndWordIndexRecall / runCount, + coordinationStartAndEndWordIndexPrecision: + accumulator.coordinationStartAndEndWordIndexPrecision + + currentValue.coordinationStartAndEndWordIndexPrecision / runCount, + coordinationStartAndEndWordIndexF1Score: + accumulator.coordinationStartAndEndWordIndexF1Score + + currentValue.coordinationStartAndEndWordIndexF1Score / runCount, + }), + { + generationRate: 0, + rangeCount: 0, + rangeRecall: 0, + rangePrecision: 0, + rangeF1Score: 0, + rangeStartAndEndWordIndexRecall: 0, + rangeStartAndEndWordIndexPrecision: 0, + rangeStartAndEndWordIndexF1Score: 0, + relationCount: 0, + relationRecall: 0, + relationPrecision: 0, + relationF1Score: 0, + relationStartAndEndWordIndexRecall: 0, + relationStartAndEndWordIndexPrecision: 0, + relationStartAndEndWordIndexF1Score: 0, + coordinationCount: 0, + coordinationRecall: 0, + coordinationPrecision: 0, + coordinationF1Score: 0, + coordinationStartAndEndWordIndexRecall: 0, + coordinationStartAndEndWordIndexPrecision: 0, + coordinationStartAndEndWordIndexF1Score: 0, + }, + ); + + metricsCsv.push([ + `${modelName} temperature=${temperature} thinkingLevel=${thinkingLevel}`, + allTriesMetrics.generationRate.toFixed(3), + firstTryMetrics.generationRate.toFixed(3), + allTriesMetrics.rangeCount.toString(), + allTriesMetrics.rangeRecall.toFixed(3), + firstTryMetrics.rangeRecall.toFixed(3), + allTriesMetrics.rangePrecision.toFixed(3), + firstTryMetrics.rangePrecision.toFixed(3), + allTriesMetrics.rangeF1Score.toFixed(3), + firstTryMetrics.rangeF1Score.toFixed(3), + allTriesMetrics.rangeStartAndEndWordIndexRecall.toFixed(3), + firstTryMetrics.rangeStartAndEndWordIndexRecall.toFixed(3), + allTriesMetrics.rangeStartAndEndWordIndexPrecision.toFixed(3), + firstTryMetrics.rangeStartAndEndWordIndexPrecision.toFixed(3), + allTriesMetrics.rangeStartAndEndWordIndexF1Score.toFixed(3), + firstTryMetrics.rangeStartAndEndWordIndexF1Score.toFixed(3), + allTriesMetrics.relationCount.toString(), + allTriesMetrics.relationRecall.toFixed(3), + firstTryMetrics.relationRecall.toFixed(3), + allTriesMetrics.relationPrecision.toFixed(3), + firstTryMetrics.relationPrecision.toFixed(3), + allTriesMetrics.relationF1Score.toFixed(3), + firstTryMetrics.relationF1Score.toFixed(3), + allTriesMetrics.relationStartAndEndWordIndexRecall.toFixed(3), + firstTryMetrics.relationStartAndEndWordIndexRecall.toFixed(3), + allTriesMetrics.relationStartAndEndWordIndexPrecision.toFixed(3), + firstTryMetrics.relationStartAndEndWordIndexPrecision.toFixed(3), + allTriesMetrics.relationStartAndEndWordIndexF1Score.toFixed(3), + firstTryMetrics.relationStartAndEndWordIndexF1Score.toFixed(3), + allTriesMetrics.coordinationCount.toString(), + allTriesMetrics.coordinationRecall.toFixed(3), + firstTryMetrics.coordinationRecall.toFixed(3), + allTriesMetrics.coordinationPrecision.toFixed(3), + firstTryMetrics.coordinationPrecision.toFixed(3), + allTriesMetrics.coordinationF1Score.toFixed(3), + firstTryMetrics.coordinationF1Score.toFixed(3), + allTriesMetrics.coordinationStartAndEndWordIndexRecall.toFixed(3), + firstTryMetrics.coordinationStartAndEndWordIndexRecall.toFixed(3), + allTriesMetrics.coordinationStartAndEndWordIndexPrecision.toFixed(3), + firstTryMetrics.coordinationStartAndEndWordIndexPrecision.toFixed(3), + allTriesMetrics.coordinationStartAndEndWordIndexF1Score.toFixed(3), + firstTryMetrics.coordinationStartAndEndWordIndexF1Score.toFixed(3), + ]); + } + } +} + +for (const modelName of openAIModelNames) { + for (const reasoningEffort of openAIReasoningEffortValues) { + for (const verbosity of openAIVerbosityValues) { + if (!isValidOpenAIParameters(modelName, reasoningEffort, verbosity)) + continue; + + const allTriesMetrics = Array.from( + { length: runCount }, + (_, runIndex) => { + const directoryPath = `${import.meta.dirname}/output/${modelName}-${reasoningEffort}-${verbosity}-${runIndex}`; + const llmSentenceStructureDataList: (SentenceStructureData | null)[] = + datasets.map((dataset) => { + if (!existsSync(`${directoryPath}/${dataset.id}.json`)) + return null; + const result = createSentenceStructureDataFromStringData( + readFileSync(`${directoryPath}/${dataset.id}.json`, "utf-8"), + ); + if (!result.success) throw new Error("Failed to parse LLM data."); + return result.data.newSentenceStructureData; + }); + return calculateOverallMetrics( + datasets.map((_, index) => ({ + answer: answerSentenceStructureDataList.at(index)!, + llmAnswer: llmSentenceStructureDataList.at(index) ?? null, + })), + ); + }, + ).reduce( + (accumulator, currentValue) => ({ + generationRate: + accumulator.generationRate + currentValue.generationRate / runCount, + rangeCount: + accumulator.rangeCount + currentValue.rangeCount / runCount, + rangeRecall: + accumulator.rangeRecall + currentValue.rangeRecall / runCount, + rangePrecision: + accumulator.rangePrecision + currentValue.rangePrecision / runCount, + rangeF1Score: + accumulator.rangeF1Score + currentValue.rangeF1Score / runCount, + rangeStartAndEndWordIndexRecall: + accumulator.rangeStartAndEndWordIndexRecall + + currentValue.rangeStartAndEndWordIndexRecall / runCount, + rangeStartAndEndWordIndexPrecision: + accumulator.rangeStartAndEndWordIndexPrecision + + currentValue.rangeStartAndEndWordIndexPrecision / runCount, + rangeStartAndEndWordIndexF1Score: + accumulator.rangeStartAndEndWordIndexF1Score + + currentValue.rangeStartAndEndWordIndexF1Score / runCount, + relationCount: + accumulator.relationCount + currentValue.relationCount / runCount, + relationRecall: + accumulator.relationRecall + currentValue.relationRecall / runCount, + relationPrecision: + accumulator.relationPrecision + + currentValue.relationPrecision / runCount, + relationF1Score: + accumulator.relationF1Score + + currentValue.relationF1Score / runCount, + relationStartAndEndWordIndexRecall: + accumulator.relationStartAndEndWordIndexRecall + + currentValue.relationStartAndEndWordIndexRecall / runCount, + relationStartAndEndWordIndexPrecision: + accumulator.relationStartAndEndWordIndexPrecision + + currentValue.relationStartAndEndWordIndexPrecision / runCount, + relationStartAndEndWordIndexF1Score: + accumulator.relationStartAndEndWordIndexF1Score + + currentValue.relationStartAndEndWordIndexF1Score / runCount, + coordinationCount: + accumulator.coordinationCount + + currentValue.coordinationCount / runCount, + coordinationRecall: + accumulator.coordinationRecall + + currentValue.coordinationRecall / runCount, + coordinationPrecision: + accumulator.coordinationPrecision + + currentValue.coordinationPrecision / runCount, + coordinationF1Score: + accumulator.coordinationF1Score + + currentValue.coordinationF1Score / runCount, + coordinationStartAndEndWordIndexRecall: + accumulator.coordinationStartAndEndWordIndexRecall + + currentValue.coordinationStartAndEndWordIndexRecall / runCount, + coordinationStartAndEndWordIndexPrecision: + accumulator.coordinationStartAndEndWordIndexPrecision + + currentValue.coordinationStartAndEndWordIndexPrecision / runCount, + coordinationStartAndEndWordIndexF1Score: + accumulator.coordinationStartAndEndWordIndexF1Score + + currentValue.coordinationStartAndEndWordIndexF1Score / runCount, + }), + { + generationRate: 0, + rangeCount: 0, + rangeRecall: 0, + rangePrecision: 0, + rangeF1Score: 0, + rangeStartAndEndWordIndexRecall: 0, + rangeStartAndEndWordIndexPrecision: 0, + rangeStartAndEndWordIndexF1Score: 0, + relationCount: 0, + relationRecall: 0, + relationPrecision: 0, + relationF1Score: 0, + relationStartAndEndWordIndexRecall: 0, + relationStartAndEndWordIndexPrecision: 0, + relationStartAndEndWordIndexF1Score: 0, + coordinationCount: 0, + coordinationRecall: 0, + coordinationPrecision: 0, + coordinationF1Score: 0, + coordinationStartAndEndWordIndexRecall: 0, + coordinationStartAndEndWordIndexPrecision: 0, + coordinationStartAndEndWordIndexF1Score: 0, + }, + ); + + const firstTryMetrics = Array.from( + { length: runCount }, + (_, runIndex) => { + const directoryPath = `${import.meta.dirname}/output/${modelName}-${reasoningEffort}-${verbosity}-${runIndex}`; + const logs: OpenAILog[] | null = existsSync( + `${directoryPath}/logs.json`, + ) + ? JSON.parse(readFileSync(`${directoryPath}/logs.json`, "utf-8")) + : null; + const llmSentenceStructureDataList: (SentenceStructureData | null)[] = + datasets.map((dataset) => { + const log = logs?.find((log) => log.id === dataset.id); + if ( + !existsSync(`${directoryPath}/${dataset.id}.json`) || + !log || + 1 < log.retries.length + ) + return null; + const result = createSentenceStructureDataFromStringData( + readFileSync(`${directoryPath}/${dataset.id}.json`, "utf-8"), + ); + if (!result.success) throw new Error("Failed to parse LLM data."); + return result.data.newSentenceStructureData; + }); + return calculateOverallMetrics( + datasets.map((_, index) => ({ + answer: answerSentenceStructureDataList.at(index)!, + llmAnswer: llmSentenceStructureDataList.at(index) ?? null, + })), + ); + }, + ).reduce( + (accumulator, currentValue) => ({ + generationRate: + accumulator.generationRate + currentValue.generationRate / runCount, + rangeCount: + accumulator.rangeCount + currentValue.rangeCount / runCount, + rangeRecall: + accumulator.rangeRecall + currentValue.rangeRecall / runCount, + rangePrecision: + accumulator.rangePrecision + currentValue.rangePrecision / runCount, + rangeF1Score: + accumulator.rangeF1Score + currentValue.rangeF1Score / runCount, + rangeStartAndEndWordIndexRecall: + accumulator.rangeStartAndEndWordIndexRecall + + currentValue.rangeStartAndEndWordIndexRecall / runCount, + rangeStartAndEndWordIndexPrecision: + accumulator.rangeStartAndEndWordIndexPrecision + + currentValue.rangeStartAndEndWordIndexPrecision / runCount, + rangeStartAndEndWordIndexF1Score: + accumulator.rangeStartAndEndWordIndexF1Score + + currentValue.rangeStartAndEndWordIndexF1Score / runCount, + relationCount: + accumulator.relationCount + currentValue.relationCount / runCount, + relationRecall: + accumulator.relationRecall + currentValue.relationRecall / runCount, + relationPrecision: + accumulator.relationPrecision + + currentValue.relationPrecision / runCount, + relationF1Score: + accumulator.relationF1Score + + currentValue.relationF1Score / runCount, + relationStartAndEndWordIndexRecall: + accumulator.relationStartAndEndWordIndexRecall + + currentValue.relationStartAndEndWordIndexRecall / runCount, + relationStartAndEndWordIndexPrecision: + accumulator.relationStartAndEndWordIndexPrecision + + currentValue.relationStartAndEndWordIndexPrecision / runCount, + relationStartAndEndWordIndexF1Score: + accumulator.relationStartAndEndWordIndexF1Score + + currentValue.relationStartAndEndWordIndexF1Score / runCount, + coordinationCount: + accumulator.coordinationCount + + currentValue.coordinationCount / runCount, + coordinationRecall: + accumulator.coordinationRecall + + currentValue.coordinationRecall / runCount, + coordinationPrecision: + accumulator.coordinationPrecision + + currentValue.coordinationPrecision / runCount, + coordinationF1Score: + accumulator.coordinationF1Score + + currentValue.coordinationF1Score / runCount, + coordinationStartAndEndWordIndexRecall: + accumulator.coordinationStartAndEndWordIndexRecall + + currentValue.coordinationStartAndEndWordIndexRecall / runCount, + coordinationStartAndEndWordIndexPrecision: + accumulator.coordinationStartAndEndWordIndexPrecision + + currentValue.coordinationStartAndEndWordIndexPrecision / runCount, + coordinationStartAndEndWordIndexF1Score: + accumulator.coordinationStartAndEndWordIndexF1Score + + currentValue.coordinationStartAndEndWordIndexF1Score / runCount, + }), + { + generationRate: 0, + rangeCount: 0, + rangeRecall: 0, + rangePrecision: 0, + rangeF1Score: 0, + rangeStartAndEndWordIndexRecall: 0, + rangeStartAndEndWordIndexPrecision: 0, + rangeStartAndEndWordIndexF1Score: 0, + relationCount: 0, + relationRecall: 0, + relationPrecision: 0, + relationF1Score: 0, + relationStartAndEndWordIndexRecall: 0, + relationStartAndEndWordIndexPrecision: 0, + relationStartAndEndWordIndexF1Score: 0, + coordinationCount: 0, + coordinationRecall: 0, + coordinationPrecision: 0, + coordinationF1Score: 0, + coordinationStartAndEndWordIndexRecall: 0, + coordinationStartAndEndWordIndexPrecision: 0, + coordinationStartAndEndWordIndexF1Score: 0, + }, + ); + + metricsCsv.push([ + `${modelName} reasoning.effort=${reasoningEffort} verbosity=${verbosity}`, + allTriesMetrics.generationRate.toFixed(3), + firstTryMetrics.generationRate.toFixed(3), + allTriesMetrics.rangeCount.toString(), + allTriesMetrics.rangeRecall.toFixed(3), + firstTryMetrics.rangeRecall.toFixed(3), + allTriesMetrics.rangePrecision.toFixed(3), + firstTryMetrics.rangePrecision.toFixed(3), + allTriesMetrics.rangeF1Score.toFixed(3), + firstTryMetrics.rangeF1Score.toFixed(3), + allTriesMetrics.rangeStartAndEndWordIndexRecall.toFixed(3), + firstTryMetrics.rangeStartAndEndWordIndexRecall.toFixed(3), + allTriesMetrics.rangeStartAndEndWordIndexPrecision.toFixed(3), + firstTryMetrics.rangeStartAndEndWordIndexPrecision.toFixed(3), + allTriesMetrics.rangeStartAndEndWordIndexF1Score.toFixed(3), + firstTryMetrics.rangeStartAndEndWordIndexF1Score.toFixed(3), + allTriesMetrics.relationCount.toString(), + allTriesMetrics.relationRecall.toFixed(3), + firstTryMetrics.relationRecall.toFixed(3), + allTriesMetrics.relationPrecision.toFixed(3), + firstTryMetrics.relationPrecision.toFixed(3), + allTriesMetrics.relationF1Score.toFixed(3), + firstTryMetrics.relationF1Score.toFixed(3), + allTriesMetrics.relationStartAndEndWordIndexRecall.toFixed(3), + firstTryMetrics.relationStartAndEndWordIndexRecall.toFixed(3), + allTriesMetrics.relationStartAndEndWordIndexPrecision.toFixed(3), + firstTryMetrics.relationStartAndEndWordIndexPrecision.toFixed(3), + allTriesMetrics.relationStartAndEndWordIndexF1Score.toFixed(3), + firstTryMetrics.relationStartAndEndWordIndexF1Score.toFixed(3), + allTriesMetrics.coordinationCount.toString(), + allTriesMetrics.coordinationRecall.toFixed(3), + firstTryMetrics.coordinationRecall.toFixed(3), + allTriesMetrics.coordinationPrecision.toFixed(3), + firstTryMetrics.coordinationPrecision.toFixed(3), + allTriesMetrics.coordinationF1Score.toFixed(3), + firstTryMetrics.coordinationF1Score.toFixed(3), + allTriesMetrics.coordinationStartAndEndWordIndexRecall.toFixed(3), + firstTryMetrics.coordinationStartAndEndWordIndexRecall.toFixed(3), + allTriesMetrics.coordinationStartAndEndWordIndexPrecision.toFixed(3), + firstTryMetrics.coordinationStartAndEndWordIndexPrecision.toFixed(3), + allTriesMetrics.coordinationStartAndEndWordIndexF1Score.toFixed(3), + firstTryMetrics.coordinationStartAndEndWordIndexF1Score.toFixed(3), + ]); + } + } +} + +writeFileSync( + `${import.meta.dirname}/output/metrics.csv`, + metricsCsv.map((row) => row.join(",")).join("\n"), +); diff --git a/packages/evaluation/generate-sentence-structure-data-by-gemini.ts b/packages/evaluation/generate-sentence-structure-data-by-gemini.ts new file mode 100644 index 0000000..882dddd --- /dev/null +++ b/packages/evaluation/generate-sentence-structure-data-by-gemini.ts @@ -0,0 +1,94 @@ +import { + GenerateContentResponse, + GoogleGenAI, + ThinkingLevel, +} from "@google/genai"; +import { + createSentenceStructureDataFromSimplifiedAnnotationData, + createSentenceStructureDataFromText, + type SentenceStructureData, +} from "@sentence-structure-diagram-app/sentence-structure-data"; +import type { + GeminiModelName, + GeminiTemperature, + GeminiThinkingLevel, +} from "./llmConfigurations.js"; +import { generateGeminiPrompt, type Prompt } from "./prompt.js"; + +const ai = new GoogleGenAI({}); + +export async function generateSentenceStructureDataByGemini( + text: string, + parameters: { + modelName: GeminiModelName; + temperature: GeminiTemperature; + seed: number; + thinkingLevel: GeminiThinkingLevel; + }, +): Promise< + | { + success: true; + prompt: Prompt; + rawResponse: GenerateContentResponse; + sentenceStructureData: SentenceStructureData; + } + | { + success: false; + prompt: Prompt; + rawResponse: GenerateContentResponse; + errorMessage: string; + } +> { + const prompt = generateGeminiPrompt( + text, + createSentenceStructureDataFromText({ + text, + }).words.map((word) => word.text), + ); + const response = await ai.models.generateContent({ + model: parameters.modelName, + contents: prompt.userInput, + config: { + systemInstruction: prompt.systemInstruction, + responseMimeType: "application/json", + temperature: parameters.temperature, + seed: parameters.seed, + ...((parameters.modelName === "gemini-3-pro-preview" || + parameters.modelName === "gemini-3-flash-preview") && { + thinkingConfig: { + thinkingLevel: ThinkingLevel[parameters.thinkingLevel], + }, + }), + }, + }); + try { + if (!response.text) throw new Error("AI returned no result"); + const json = (() => { + try { + return JSON.parse(response.text); + } catch { + throw new Error("AI returned invalid JSON"); + } + })(); + const sentenceStructureData = + createSentenceStructureDataFromSimplifiedAnnotationData(text, json); + if (sentenceStructureData.success) { + return { + success: true, + prompt, + rawResponse: response, + sentenceStructureData: + sentenceStructureData.data.newSentenceStructureData, + }; + } else { + throw new Error("AI returned JSON with invalid schema"); + } + } catch (error) { + return { + success: false, + prompt, + rawResponse: response, + errorMessage: error instanceof Error ? error.message : "", + }; + } +} diff --git a/packages/evaluation/generate-sentence-structure-data-by-llama.ts b/packages/evaluation/generate-sentence-structure-data-by-llama.ts new file mode 100644 index 0000000..8c16b05 --- /dev/null +++ b/packages/evaluation/generate-sentence-structure-data-by-llama.ts @@ -0,0 +1,51 @@ +// import { AzureOpenAI } from "openai"; +// import { +// SimplifiedAnnotationDataSchema, +// type SimplifiedAnnotationData, +// } from "@sentence-structure-diagram-app/sentence-structure-data"; +// import { InvalidAIOutputError } from "./invalid-ai-output-error.js"; + +// export async function generateSimplifiedAnnotationDataByLlama( +// prompt: [ +// { +// role: "system"; +// content: string; +// }, +// { +// role: "user"; +// content: string; +// }, +// ], +// ): Promise { +// const client = new AzureOpenAI({ +// endpoint: process.env.AZURE_OPENAI_ENDPOINT, +// apiKey: process.env.AZURE_OPENAI_API_KEY, +// apiVersion: process.env.AZURE_OPENAI_API_VERSION, +// }); + +// try { +// const completion = await client.chat.completions.parse({ +// model: process.env.AZURE_OPENAI_LLAMA4_DEPLOYMENT_NAME!, +// messages: prompt, +// response_format: { type: "json_object" }, +// }); +// const result = completion.choices[0]?.message.content; +// if (!result) throw new InvalidAIOutputError("AI returned no result"); +// const json = (() => { +// try { +// return JSON.parse(result); +// } catch { +// throw new InvalidAIOutputError("AI returned invalid JSON"); +// } +// })(); +// console.log("Generated JSON:", JSON.stringify(json, null, 2)); +// try { +// return SimplifiedAnnotationDataSchema.parse(json); +// } catch { +// throw new InvalidAIOutputError("AI returned JSON with invalid schema"); +// } +// } catch (error) { +// console.error(error); +// throw error; +// } +// } diff --git a/packages/evaluation/generate-sentence-structure-data-by-openai.ts b/packages/evaluation/generate-sentence-structure-data-by-openai.ts new file mode 100644 index 0000000..35669f2 --- /dev/null +++ b/packages/evaluation/generate-sentence-structure-data-by-openai.ts @@ -0,0 +1,95 @@ +import { AzureOpenAI } from "openai"; +import { zodTextFormat } from "openai/helpers/zod.js"; +import type { ParsedResponse } from "openai/resources/responses/responses.js"; +import { + createSentenceStructureDataFromSimplifiedAnnotationData, + createSentenceStructureDataFromText, + SimplifiedAnnotationDataSchema, + type SentenceStructureData, + type SimplifiedAnnotationData, +} from "@sentence-structure-diagram-app/sentence-structure-data"; +import type { + OpenAIModelName, + OpenAIReasoningEffort, + OpenAIVerbosity, +} from "./llmConfigurations.js"; +import { generateGPTPrompt, type Prompt } from "./prompt.js"; + +const client = new AzureOpenAI({ + endpoint: process.env.AZURE_OPENAI_ENDPOINT, + apiKey: process.env.AZURE_OPENAI_API_KEY, + apiVersion: process.env.AZURE_OPENAI_API_VERSION, +}); + +export async function generateSentenceStructureDataByOpenAI( + text: string, + parameters: { + modelName: OpenAIModelName; + reasoningEffort: OpenAIReasoningEffort; + verbosity: OpenAIVerbosity; + }, +): Promise< + | { + success: true; + prompt: Prompt; + rawResponse: ParsedResponse; + sentenceStructureData: SentenceStructureData; + } + | { + success: false; + prompt: Prompt; + rawResponse: ParsedResponse; + errorMessage: string; + } +> { + const prompt = generateGPTPrompt( + text, + createSentenceStructureDataFromText({ + text, + }).words.map((word) => word.text), + ); + const response = await client.responses.parse({ + model: + parameters.modelName === "gpt-5" + ? process.env.AZURE_OPENAI_GPT5_DEPLOYMENT_NAME! + : parameters.modelName === "gpt-5.1" + ? process.env.AZURE_OPENAI_GPT5_1_DEPLOYMENT_NAME! + : process.env.AZURE_OPENAI_GPT5_2_DEPLOYMENT_NAME!, + instructions: prompt.systemInstruction, + reasoning: { effort: parameters.reasoningEffort }, + input: prompt.userInput, + text: { + verbosity: parameters.verbosity, + format: zodTextFormat( + SimplifiedAnnotationDataSchema, + "simplified-annotation-data", + ), + }, + }); + try { + if (!response.output_parsed) throw new Error("AI returned no result"); + const sentenceStructureData = + createSentenceStructureDataFromSimplifiedAnnotationData( + text, + response.output_parsed, + ); + if (sentenceStructureData.success) { + return { + success: true, + prompt, + rawResponse: response, + sentenceStructureData: + sentenceStructureData.data.newSentenceStructureData, + }; + } else { + throw new Error("AI returned JSON with invalid schema"); + } + } catch (error) { + return { + success: false, + prompt, + rawResponse: response, + errorMessage: error instanceof Error ? error.message : "", + }; + } +} diff --git a/packages/evaluation/index.ts b/packages/evaluation/index.ts new file mode 100644 index 0000000..958b5e1 --- /dev/null +++ b/packages/evaluation/index.ts @@ -0,0 +1,5 @@ +export { + generateGeminiPrompt, + generateGPTPrompt, + type Prompt, +} from "./prompt.js"; diff --git a/packages/evaluation/llmConfigurations.ts b/packages/evaluation/llmConfigurations.ts new file mode 100644 index 0000000..82e4c2e --- /dev/null +++ b/packages/evaluation/llmConfigurations.ts @@ -0,0 +1,110 @@ +import type { GenerateContentResponse } from "@google/genai"; +import type { ParsedResponse } from "openai/resources/responses/responses.js"; +import type { SimplifiedAnnotationData } from "@sentence-structure-diagram-app/sentence-structure-data"; +import type { Prompt } from "./prompt.js"; + +export type Dataset = { + id: number; + englishText: string; +}; + +export const runCount = 5; +export const retryCount = 10; + +export const geminiModelNames = [ + "gemini-2.5-flash-lite", + "gemini-2.5-flash", + "gemini-2.5-pro", + "gemini-3-flash-preview", + "gemini-3-pro-preview", +] as const; +export type GeminiModelName = (typeof geminiModelNames)[number]; +export const geminiTemperatureValues = [0, 0.5, 1, 1.5, 2] as const; +export type GeminiTemperature = (typeof geminiTemperatureValues)[number]; +export const geminiThinkingLevelValues = ["LOW", "HIGH"] as const; +export type GeminiThinkingLevel = (typeof geminiThinkingLevelValues)[number]; + +export function isValidGeminiParameters( + modelName: GeminiModelName, + temperature: GeminiTemperature, + thinkingLevel: GeminiThinkingLevel, +): boolean { + // Gemini 3以前ではthinkingLevelにLOWは指定できない cf. https://ai.google.dev/api/generate-content#ThinkingConfig + if ( + (modelName === "gemini-2.5-pro" || + modelName === "gemini-2.5-flash" || + modelName === "gemini-2.5-flash-lite") && + thinkingLevel === "LOW" + ) { + return false; + } + // レート制限回避のため、最小限の組み合わせのみ許可 + if (modelName === "gemini-3-pro-preview" && temperature !== 1) { + return false; + } + return true; +} + +export type GeminiLog = { + id: number; + success: boolean; + englishText: string; + prompt: Prompt; + retries: ( + | { + success: true; + processingTime: number; + rawResponse: GenerateContentResponse; + } + | { + success: false; + processingTime: number; + rawResponse: GenerateContentResponse; + errorMessage: string; + } + )[]; +}; + +export const openAIModelNames = ["gpt-5", "gpt-5.1", "gpt-5.2"] as const; +export const openAIReasoningEffortValues = [ + "none", + "low", + "medium", + "high", +] as const; +export const openAIVerbosityValues = ["low", "medium", "high"] as const; + +export type OpenAIModelName = (typeof openAIModelNames)[number]; +export type OpenAIReasoningEffort = + (typeof openAIReasoningEffortValues)[number]; +export type OpenAIVerbosity = (typeof openAIVerbosityValues)[number]; + +export function isValidOpenAIParameters( + modelName: OpenAIModelName, + reasoningEffort: OpenAIReasoningEffort, + verbosity: OpenAIVerbosity, +): boolean { + // gpt-5ではreasoning.effortにnoneは指定できない cf. https://platform.openai.com/docs/api-reference/responses/create#responses_create-reasoning-effort + if (modelName === "gpt-5" && reasoningEffort === "none") return false; + return true; +} + +export type OpenAILog = { + id: number; + success: boolean; + englishText: string; + prompt: Prompt; + retries: ( + | { + success: true; + processingTime: number; + rawResponse: ParsedResponse; + } + | { + success: false; + processingTime: number; + rawResponse: ParsedResponse; + errorMessage: string; + } + )[]; +}; diff --git a/packages/evaluation/package.json b/packages/evaluation/package.json new file mode 100644 index 0000000..542bfa7 --- /dev/null +++ b/packages/evaluation/package.json @@ -0,0 +1,24 @@ +{ + "name": "@sentence-structure-diagram-app/evaluation", + "version": "0.1.0", + "type": "module", + "main": "./dist/index.js", + "scripts": { + "build": "tsc", + "build:watch": "tsc --watch", + "clean": "rm -r dist" + }, + "dependencies": { + "@google/genai": "^1.34.0", + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-configurations": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-svg": "^0.1.0", + "openai": "^6.15.0", + "zod": "^4.2.1" + }, + "devDependencies": { + "@types/node": "^24.10.4", + "tsx": "^4.21.0", + "typescript": "^5.9.3" + } +} diff --git a/packages/evaluation/prompt.ts b/packages/evaluation/prompt.ts new file mode 100644 index 0000000..2546869 --- /dev/null +++ b/packages/evaluation/prompt.ts @@ -0,0 +1,121 @@ +import * as z from "zod"; +import { + SimplifiedAnnotationDataSchema, + type SimplifiedAnnotationData, +} from "@sentence-structure-diagram-app/sentence-structure-data"; +import { examples } from "./examples.js"; + +export type Prompt = { + systemInstruction: string; + userInput: string; +}; + +const instructionText = `\ +あなたは学習者向けに英文の構造を解析する専門家です。 +これから英文を与えます。また、その英文の単語とそれに対応するインデックスの関係を与えます。 +与えられた英文の構造を解析し、与えられたJSON Schemaに従ったJSONデータを生成してください。 + +## 要件 + +- 単語とそれに対応するインデックスの関係は、必ずこちらが与えるものをそのまま使用してください。 +- 英文中のすべての文の要素や構成単位に対して、範囲(文の主要素、修飾語、句(ここでは準動詞句のみを指す)、節)を必ず作成してください。 +- 並列構造の場合には、並列構造全体ではなく必ずそれぞれの子要素に対して範囲を作成してください。 +- 句や節の入れ子の中の文の要素や構成単位に対しては、必ず範囲を作成してください。 +- すべての範囲に対して、文の主要素にはS、V、O、Cのいずれかを、修飾語にはMを、句と節にはS、O、C、Mのいずれかを必ず割り当ててください。 +- 句(ここでは準動詞句のみを指す)や節が他の名詞や名詞句を修飾している場合には、句や節から被修飾語句に向かう関係をすべて必ず作成してください。 +- これらの関係以外には、関係を作成しないでください。 +- 関係を作成する際に始点あるいは終点となる範囲が文の要素や構成単位でない場合に限り、範囲(関係)を作成することができます。それ以外の場合には、範囲(関係)を作成しないでください。 +- 等位接続詞や相関接続詞による並列構造がある場合には、すべての並列構造を必ず作成してください。 +- 並列構造を作成する際には、内部のすべての子要素に対して子要素(等位接続詞、相関接続詞、並列要素)を必ず作成してください。 +- 子要素の範囲の開始と終了を表す単語のインデックスは、隣の子要素のインデックスと完全に連続している必要があります。`; + +const examplePrompt = examples + .map((example, index) => { + const { + text: _exampleText, + words: _exampleWords, + ..._exampleAnnotationData + } = example; + const exampleText = _exampleText; + const exampleWords = _exampleWords + .map((word) => `${word.text}:${word.index}`) + .join("\n "); + const exampleAnnotationData = JSON.stringify( + SimplifiedAnnotationDataSchema.parse( + _exampleAnnotationData satisfies SimplifiedAnnotationData, + ), + null, + 2, + ); + return `\ +## 例${index + 1} +英文:${exampleText} +単語とそれに対応するインデックスの関係: + ${exampleWords} +JSON: +${exampleAnnotationData}`; + }) + .join("\n\n"); + +export function generateGeminiPrompt(text: string, words: string[]): Prompt { + const prompt = { + systemInstruction: `\ +${instructionText} + +## JSON Schema +${JSON.stringify(z.toJSONSchema(SimplifiedAnnotationDataSchema), null, 2)} + +${examplePrompt}`, + userInput: `\ +英文:${text} +単語とそれに対応するインデックスの関係: + ${words.map((word, index) => `${word}:${index}`).join("\n ")}`, + }; + return prompt; +} + +export function generateGPTPrompt(text: string, words: string[]): Prompt { + const prompt = { + systemInstruction: `\ +${instructionText} + +${examplePrompt}`, + userInput: `\ +英文:${text} +単語とそれに対応するインデックスの関係: + ${words.map((word, index) => `${word}:${index}`).join("\n ")}`, + }; + return prompt; +} + +// export function generateLlamaPrompt(text: string, words: string[]) { +// const prompt: [ +// { +// role: "system"; +// content: string; +// }, +// { +// role: "user"; +// content: string; +// }, +// ] = [ +// { +// role: "system" as const, +// content: `\ +// ${instructionText} + +// ## JSON Schema +// ${JSON.stringify(z.toJSONSchema(SimplifiedAnnotationDataSchema), null, 2)} + +// ${examplePrompt}`, +// }, +// { +// role: "user" as const, +// content: `\ +// 英文:${text} +// 単語とそれに対応するインデックスの関係: +// ${words.map((word, index) => `${word}:${index}`).join("\n ")}`, +// }, +// ]; +// return prompt; +// } diff --git a/packages/evaluation/show-differences-by-json-patch.ts b/packages/evaluation/show-differences-by-json-patch.ts new file mode 100644 index 0000000..6068df7 --- /dev/null +++ b/packages/evaluation/show-differences-by-json-patch.ts @@ -0,0 +1,40 @@ +// import { readFileSync } from "node:fs"; +// import jsonpatch from "fast-json-patch"; +// import { type SimplifiedSentenceStructureData } from "@sentence-structure-diagram-app/sentence-structure-data"; +// import type { Dataset, ModelName } from "./types.js"; + +// const modelName: ModelName = "gpt-5.1"; + +// const datasets: Dataset[] = JSON.parse( +// readFileSync(`${import.meta.dirname}/data/datasets.json`, "utf-8"), +// ); + +// let differenceCount = 0; +// for (const dataset of datasets) { +// const answerSimplifiedSentenceStructureData: SimplifiedSentenceStructureData = +// JSON.parse( +// readFileSync( +// `${import.meta.dirname}/output/answer-${dataset.id}.json`, +// "utf-8", +// ), +// ); +// const llmSimplifiedSentenceStructureData: SimplifiedSentenceStructureData = +// JSON.parse( +// readFileSync( +// `${import.meta.dirname}/output/${modelName}-${dataset.id}.json`, +// "utf-8", +// ), +// ); + +// const differences = jsonpatch.compare( +// answerSimplifiedSentenceStructureData, +// llmSimplifiedSentenceStructureData, +// ); +// differenceCount += differences.length; +// console.log( +// `Number of differences between answer and ${modelName} for dataset ID ${dataset.id}: ${differences.length}`, +// ); +// } +// console.log( +// `Total number of differences between answer and ${modelName}: ${differenceCount}`, +// ); diff --git a/packages/evaluation/tsconfig.json b/packages/evaluation/tsconfig.json new file mode 100644 index 0000000..d6a2b39 --- /dev/null +++ b/packages/evaluation/tsconfig.json @@ -0,0 +1,44 @@ +{ + // Visit https://aka.ms/tsconfig to read more about this file + "compilerOptions": { + // File Layout + // "rootDir": "./src", + "outDir": "./dist", + + // Environment Settings + // See also https://aka.ms/tsconfig/module + "module": "nodenext", + "target": "esnext", + "types": [], + // For nodejs: + // "lib": ["esnext"], + // "types": ["node"], + // and npm install -D @types/node + + // Other Outputs + "sourceMap": true, + "declaration": true, + "declarationMap": true, + + // Stricter Typechecking Options + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + + // Style Options + // "noImplicitReturns": true, + // "noImplicitOverride": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + // "noFallthroughCasesInSwitch": true, + // "noPropertyAccessFromIndexSignature": true, + + // Recommended Options + "strict": true, + "jsx": "react-jsx", + "verbatimModuleSyntax": true, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "skipLibCheck": true + } +} diff --git a/packages/frontend/.env b/packages/frontend/.env new file mode 100644 index 0000000..b3d1a5c --- /dev/null +++ b/packages/frontend/.env @@ -0,0 +1 @@ +VITE_API_ENDPOINT=http://localhost:3000 diff --git a/packages/frontend/.gitignore b/packages/frontend/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/packages/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/packages/frontend/eslint.config.js b/packages/frontend/eslint.config.js new file mode 100644 index 0000000..75d3c46 --- /dev/null +++ b/packages/frontend/eslint.config.js @@ -0,0 +1,23 @@ +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; +import { defineConfig, globalIgnores } from "eslint/config"; + +export default defineConfig([ + globalIgnores(["dist"]), + { + files: ["**/*.{ts,tsx}"], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]); diff --git a/packages/frontend/index.html b/packages/frontend/index.html new file mode 100644 index 0000000..cf3c3e1 --- /dev/null +++ b/packages/frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + 英文構造図作図支援アプリ(ベータ版) + + +
+ + + diff --git a/packages/frontend/package.json b/packages/frontend/package.json new file mode 100644 index 0000000..d8f1750 --- /dev/null +++ b/packages/frontend/package.json @@ -0,0 +1,42 @@ +{ + "name": "@sentence-structure-diagram-app/frontend", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview", + "clean": "rm -r dist" + }, + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/icons-material": "^7.3.6", + "@mui/material": "^7.3.6", + "@sentence-structure-diagram-app/backend": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-data": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-svg": "^0.1.0", + "@tanstack/react-query": "^5.90.12", + "@trpc/client": "^11.7.2", + "@trpc/tanstack-react-query": "^11.7.2", + "react": "^19.2.0", + "react-dom": "^19.2.0" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@types/node": "^24.10.1", + "@types/react": "^19.2.5", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.46.4", + "vite": "^7.2.4" + } +} diff --git a/packages/frontend/public/vite.svg b/packages/frontend/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/packages/frontend/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/frontend/src/App.tsx b/packages/frontend/src/App.tsx new file mode 100644 index 0000000..23f2800 --- /dev/null +++ b/packages/frontend/src/App.tsx @@ -0,0 +1,11 @@ +import { QueryClientProvider } from "@tanstack/react-query"; +import { queryClient } from "./utils/trpc"; +import Home from "./pages"; + +export default function App() { + return ( + + + + ); +} diff --git a/packages/frontend/src/main.tsx b/packages/frontend/src/main.tsx new file mode 100644 index 0000000..5d4a2be --- /dev/null +++ b/packages/frontend/src/main.tsx @@ -0,0 +1,9 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import App from "./App.tsx"; + +createRoot(document.getElementById("root")!).render( + + + , +); diff --git a/packages/frontend/src/pages/components/AppBar.tsx b/packages/frontend/src/pages/components/AppBar.tsx new file mode 100644 index 0000000..9b84587 --- /dev/null +++ b/packages/frontend/src/pages/components/AppBar.tsx @@ -0,0 +1,148 @@ +import { useRef, useState } from "react"; +import { + AppBar as MUIAppBar, + IconButton, + ListItemIcon, + Menu, + MenuItem, + Toolbar, + Typography, +} from "@mui/material"; +import { + FileDownload as FileDownloadIcon, + FileUpload as FileUploadIcon, + Menu as MenuIcon, +} from "@mui/icons-material"; +import { + createSentenceStructureDataFromStringData, + createSentenceStructureDataFromXMLData, +} from "@sentence-structure-diagram-app/sentence-structure-data"; +import { xmlStringToConfigurations } from "@sentence-structure-diagram-app/sentence-structure-diagram-configurations"; +import { useSentenceStructureData } from "../contexts/SentenceStructureDataProvider"; +import { useConfigurations } from "../contexts/ConfigurationsProvider"; +import ExportDialog from "./ExportDialog"; + +export default function AppBar() { + const [anchorElement, setAnchorElement] = useState(null); + + const [isExportDialogOpen, setIsExportDialogOpen] = useState(false); + + const fileInputRef = useRef(null); + + const { setSentenceStructureData } = useSentenceStructureData(); + + const { setConfigurations } = useConfigurations(); + + return ( + + + + 英文構造図作図支援アプリ(ベータ版) + + setAnchorElement(e.currentTarget)}> + + + setAnchorElement(null)} + > + { + setAnchorElement(null); + setIsExportDialogOpen(true); + }} + > + + + + エクスポート + + { + setAnchorElement(null); + fileInputRef.current?.click(); + }} + > + + + + インポート + + + setIsExportDialogOpen(false)} + /> + { + const file = e.target.files?.[0]; + if (!file) return; + const reader = new FileReader(); + reader.onload = () => { + try { + if (typeof reader.result !== "string") + throw new Error("Invalid file"); + if (file.name.endsWith(".json")) { + const result = createSentenceStructureDataFromStringData( + reader.result, + ); + if (result.success) { + setSentenceStructureData( + result.data.newSentenceStructureData, + ); + } else { + alert(result.message); + } + } else { + const svg = new DOMParser().parseFromString( + reader.result, + "image/svg+xml", + ); + const sentenceStructureDataResult = + createSentenceStructureDataFromXMLData( + svg.querySelector("sentence-structure-data")?.innerHTML ?? + "", + ); + if (sentenceStructureDataResult.success) { + setSentenceStructureData( + sentenceStructureDataResult.data.newSentenceStructureData, + ); + } else { + alert(sentenceStructureDataResult.message); + return; + } + // const newSentenceStructure = + // simplifiedSentenceStructureDataToSentenceStructureData.decode( + // stringToSimplifiedSentenceStructureData.decode( + // svg.getElementById("sentence-structure-data") + // ?.textContent ?? "", + // ), + // ); + + setConfigurations( + xmlStringToConfigurations.decode( + svg.querySelector("configurations")?.innerHTML ?? "", + ), + ); + // const newConfigurations = stringToConfigurations.decode( + // svg.getElementById("configurations")?.textContent ?? "", + // ); + } + } catch { + alert( + "ファイルの読み込みに失敗しました。正しい形式のファイルを選択してください。", + ); + } + }; + reader.readAsText(file); + }} + /> + + + ); +} diff --git a/packages/frontend/src/pages/components/ConfirmClearAllDialog.tsx b/packages/frontend/src/pages/components/ConfirmClearAllDialog.tsx new file mode 100644 index 0000000..ff1291d --- /dev/null +++ b/packages/frontend/src/pages/components/ConfirmClearAllDialog.tsx @@ -0,0 +1,44 @@ +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, +} from "@mui/material"; +import { useSentenceStructureData } from "../contexts/SentenceStructureDataProvider"; + +type ConfirmClearAllDialogProps = { + isOpen: boolean; + onClose: () => void; +}; + +export default function ConfirmClearAllDialog({ + isOpen, + onClose, +}: ConfirmClearAllDialogProps) { + const { initialSentenceStructureData, setSentenceStructureData } = + useSentenceStructureData(); + + return ( + + 本当に全てクリアしますか? + + + 全てクリアすると、入力した英文と注釈データがすべて失われます。この操作は元に戻せません。 + + + + + + + + ); +} diff --git a/packages/frontend/src/pages/components/ConfirmClearAnnotationsDialog.tsx b/packages/frontend/src/pages/components/ConfirmClearAnnotationsDialog.tsx new file mode 100644 index 0000000..2dc269a --- /dev/null +++ b/packages/frontend/src/pages/components/ConfirmClearAnnotationsDialog.tsx @@ -0,0 +1,35 @@ +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, +} from "@mui/material"; + +type ConfirmClearAnnotationsDialogProps = { + isOpen: boolean; + onClose: () => void; + onConfirm: () => void; +}; + +export default function ConfirmClearAnnotationsDialog({ + isOpen, + onClose, + onConfirm, +}: ConfirmClearAnnotationsDialogProps) { + return ( + + 本当にテキスト編集モードに切り替えますか? + + + テキスト編集モードに切り替えると、現在の注釈データがすべて失われます。この操作は元に戻せません。 + + + + + + + + ); +} diff --git a/packages/frontend/src/pages/components/ExportDialog.tsx b/packages/frontend/src/pages/components/ExportDialog.tsx new file mode 100644 index 0000000..713c452 --- /dev/null +++ b/packages/frontend/src/pages/components/ExportDialog.tsx @@ -0,0 +1,494 @@ +import { useState } from "react"; +import { + AppBar, + Box, + Button, + Dialog, + Divider, + FormControl, + IconButton, + Input, + InputLabel, + MenuItem, + Paper, + Select, + Slider, + Stack, + TextField, + Toolbar, + Typography, +} from "@mui/material"; +import { Close as CloseIcon } from "@mui/icons-material"; +import { sentenceStructureDataToString } from "@sentence-structure-diagram-app/sentence-structure-data"; +import { generateSvgString } from "@sentence-structure-diagram-app/sentence-structure-diagram-svg"; +import { useSentenceStructureData } from "../contexts/SentenceStructureDataProvider"; +import { useConfigurations } from "../contexts/ConfigurationsProvider"; +import { measureTextWidth } from "../utils/measure-text-width"; + +type ExportDialogProps = { + isOpen: boolean; + onClose: () => void; +}; + +export default function ExportDialog({ isOpen, onClose }: ExportDialogProps) { + const [imageWidth, setImageWidth] = useState(1000); + + const { sentenceStructureData } = useSentenceStructureData(); + const { configurations, setConfigurations } = useConfigurations(); + + return ( + + + + + + + + + エクスポート + + + + + + + + + + + + + + + 画像の幅 + + + setImageWidth(newValue)} + min={500} + max={1500} + valueLabelDisplay="auto" + /> + setImageWidth(Number(e.target.value))} + inputProps={{ + step: 100, + min: 500, + max: 1500, + type: "number", + }} + /> + + + + + + 色 + + + + setConfigurations({ + ...configurations, + color: { + ...configurations.color, + primaryColor: e.target.value, + }, + }) + } + sx={{ minWidth: 140 }} + /> + + setConfigurations({ + ...configurations, + color: { + ...configurations.color, + textColor: e.target.value, + }, + }) + } + sx={{ minWidth: 140 }} + /> + + + + + + 文の構成単位の範囲 + + + + 修飾語 + + + + + + + + + + + + + + + + 文の要素 + + + + setConfigurations({ + ...configurations, + sentenceElementNameToSentenceElementSymbolMap: { + ...configurations.sentenceElementNameToSentenceElementSymbolMap, + S: e.target.value, + }, + }) + } + /> + + setConfigurations({ + ...configurations, + sentenceElementNameToSentenceElementSymbolMap: { + ...configurations.sentenceElementNameToSentenceElementSymbolMap, + V: e.target.value, + }, + }) + } + /> + + setConfigurations({ + ...configurations, + sentenceElementNameToSentenceElementSymbolMap: { + ...configurations.sentenceElementNameToSentenceElementSymbolMap, + C: e.target.value, + }, + }) + } + /> + + setConfigurations({ + ...configurations, + sentenceElementNameToSentenceElementSymbolMap: { + ...configurations.sentenceElementNameToSentenceElementSymbolMap, + O: e.target.value, + }, + }) + } + /> + + setConfigurations({ + ...configurations, + sentenceElementNameToSentenceElementSymbolMap: { + ...configurations.sentenceElementNameToSentenceElementSymbolMap, + M: e.target.value, + }, + }) + } + /> + + + + + + 矢印の形状 + + + + 矢印の形状 + + + + + + + + レイアウト方式 + + + + レイアウト方式 + + + + + + + + ); +} diff --git a/packages/frontend/src/pages/components/SentenceStructureDiagramAnnotator.tsx b/packages/frontend/src/pages/components/SentenceStructureDiagramAnnotator.tsx new file mode 100644 index 0000000..4deef84 --- /dev/null +++ b/packages/frontend/src/pages/components/SentenceStructureDiagramAnnotator.tsx @@ -0,0 +1,296 @@ +import { Fragment } from "react/jsx-runtime"; +import { alpha, useTheme } from "@mui/material"; +import { + findCoordinationById, + findRangeById, + findRelationById, +} from "@sentence-structure-diagram-app/sentence-structure-data"; +import type { SentenceStructureDiagramData } from "@sentence-structure-diagram-app/sentence-structure-diagram-data"; +import { useSentenceStructureData } from "../contexts/SentenceStructureDataProvider"; +import { useInteractionState } from "../contexts/InteractionStateProvider"; + +type SentenceStructureDiagramAnnotatorProps = { + sentenceStructureDiagramData: SentenceStructureDiagramData; +}; + +export default function SentenceStructureDiagramAnnotator( + props: SentenceStructureDiagramAnnotatorProps, +) { + const theme = useTheme(); + + const { sentenceStructureData } = useSentenceStructureData(); + + const { + interactionState, + handleMouseUpOnWord, + handleMouseEnterOnWord, + handleMouseDownOnWord, + handleClickOnRange, + handleClickOnRelation, + handleClickOnCoordination, + } = useInteractionState(); + + return ( + + {/* 単語と括弧類 */} + {props.sentenceStructureDiagramData.words.map((word) => { + const isWithinSelectingRange = + (interactionState.type === "range-selecting" || + interactionState.type === "relation-selecting" || + interactionState.type === "coordination-selecting") && + Math.min( + interactionState.anchorWordIndex, + interactionState.focusWordIndex, + ) <= word.index && + word.index <= + Math.max( + interactionState.anchorWordIndex, + interactionState.focusWordIndex, + ); + const isWithinConfirmingRange = + (interactionState.type === "range-confirming" || + interactionState.type === "coordination-confirming") && + interactionState.startWordIndex <= word.index && + word.index <= interactionState.endWordIndex; + const isWithinSelectedCoordinationChild = (() => { + if ( + !( + interactionState.type === "coordination-idle" || + interactionState.type === "coordination-selecting" || + interactionState.type === "coordination-confirming" + ) + ) + return false; + return interactionState.children.some( + (child) => + child.startWordIndex <= word.index && + word.index <= child.endWordIndex, + ); + })(); + const isWithinSelectedRange = (() => { + if ( + !( + interactionState.type === "range-selected" || + interactionState.type === "relation-idle" || + interactionState.type === "relation-selecting" + ) + ) + return false; + const range = + interactionState.type === "range-selected" + ? findRangeById(sentenceStructureData, { + rangeId: interactionState.rangeId, + }) + : interactionState.fromRange; + return ( + range !== null && + range.startWordIndex <= word.index && + word.index <= range.endWordIndex + ); + })(); + const isWithinSelectedRelationRange = (() => { + if (interactionState.type !== "relation-selected") return false; + const relation = findRelationById(sentenceStructureData, { + relationId: interactionState.relationId, + }); + if (relation === null) return false; + const fromRange = findRangeById(sentenceStructureData, { + rangeId: relation.fromRangeId, + }); + const toRange = findRangeById(sentenceStructureData, { + rangeId: relation.toRangeId, + }); + if (fromRange === null || toRange === null) return false; + return ( + (fromRange.startWordIndex <= word.index && + word.index <= fromRange.endWordIndex) || + (toRange.startWordIndex <= word.index && + word.index <= toRange.endWordIndex) + ); + })(); + const isWithinSelectedCoordination = (() => { + if (interactionState.type !== "coordination-selected") return false; + const coordination = findCoordinationById(sentenceStructureData, { + coordinationId: interactionState.coordinationId, + }); + if (coordination === null) return false; + return coordination.children.some( + (child) => + child.startWordIndex <= word.index && + word.index <= child.endWordIndex, + ); + })(); + const isSelecting = isWithinSelectingRange || isWithinConfirmingRange; + const isSelected = + isWithinSelectedCoordinationChild || + isWithinSelectedRange || + isWithinSelectedRelationRange || + isWithinSelectedCoordination; + + return ( + + {word.openingBrackets.length > 0 && ( + + {word.openingBrackets.join("")} + + )} + { + const result = handleMouseDownOnWord(word.index); + if (!result.success) { + alert(result.message); + } + }} + onMouseEnter={(e) => { + if (e.buttons !== 1) return; + const result = handleMouseEnterOnWord(word.index); + if (!result.success) { + alert(result.message); + } + }} + onMouseUp={(e) => { + e.stopPropagation(); + const result = handleMouseUpOnWord(word.index); + if (!result.success) { + alert(result.message); + } + }} + > + + + {word.text} + + + {word.closingBrackets.length > 0 && ( + + {word.closingBrackets.join("")} + + )} + + ); + })} + {/* 下線 */} + {props.sentenceStructureDiagramData.underlines.map((underline) => + underline.position.map((position, index) => ( + + )), + )} + {/* 文の要素の記号 */} + {props.sentenceStructureDiagramData.sentenceElements.map( + (sentenceElement) => + sentenceElement.symbol && ( + { + const result = handleClickOnRange(sentenceElement.rangeId); + if (!result.success) { + alert(result.message); + } + }} + > + {sentenceElement.symbol} + + ), + )} + {/* 矢印 */} + {props.sentenceStructureDiagramData.relations.map((relation) => ( + { + const result = handleClickOnRelation(relation.relationId); + if (!result.success) { + alert(result.message); + } + }} + /> + ))} + {/* 並列 */} + {props.sentenceStructureDiagramData.coordinations.map((coordination) => ( + { + const result = handleClickOnCoordination( + coordination.coordinationId, + ); + if (!result.success) { + alert(result.message); + } + }} + /> + ))} + + ); +} diff --git a/packages/frontend/src/pages/components/SentenceStructureEditor.tsx b/packages/frontend/src/pages/components/SentenceStructureEditor.tsx new file mode 100644 index 0000000..56ebb02 --- /dev/null +++ b/packages/frontend/src/pages/components/SentenceStructureEditor.tsx @@ -0,0 +1,642 @@ +import { useEffect, useRef, useState } from "react"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { trpc } from "../../utils/trpc"; +import { + Box, + Button, + Divider, + Paper, + TextField, + ToggleButton, + ToggleButtonGroup, + Tooltip, +} from "@mui/material"; +import { + AutoAwesome as AutoAwesomeIcon, + ClearAll as ClearAllIcon, + EditNote as EditNoteIcon, + Schema as SchemaIcon, +} from "@mui/icons-material"; +import { + coordinationChildTypeOptions, + createSentenceStructureDataFromSimplifiedAnnotationData, + createSentenceStructureDataFromText, + findCoordinationById, + findRangeById, + findRelationById, + sentenceElementRangeTypeToAllowedSentenceElementNameOptionsMap, + sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap, + type CoordinationChildType, +} from "@sentence-structure-diagram-app/sentence-structure-data"; +import { convertSentenceStructureDataToSentenceStructureDiagramData } from "@sentence-structure-diagram-app/sentence-structure-diagram-data"; +import { useSentenceStructureData } from "../contexts/SentenceStructureDataProvider"; +import { useInteractionState } from "../contexts/InteractionStateProvider"; +import { measureTextWidth } from "../utils/measure-text-width"; +import AppBar from "./AppBar"; +import ConfirmClearAnnotationsDialog from "./ConfirmClearAnnotationsDialog"; +import ConfirmClearAllDialog from "./ConfirmClearAllDialog"; +import SentenceStructureDiagramAnnotator from "./SentenceStructureDiagramAnnotator"; + +type ViewMode = "edit" | "annotate"; + +export default function SentenceStructureEditor() { + const [viewMode, _setViewMode] = useState(() => { + const savedViewMode = localStorage.getItem("viewMode"); + if (savedViewMode === "annotate") return "annotate"; + return "edit"; + }); + function setViewMode(newViewMode: ViewMode) { + localStorage.setItem("viewMode", newViewMode); + _setViewMode(newViewMode); + } + const [ + isConfirmClearAnnotationsDialogOpen, + setIsConfirmClearAnnotationsDialogOpen, + ] = useState(false); + const [isConfirmClearAllDialogOpen, setIsConfirmClearAllDialogOpen] = + useState(false); + + const { data: statusData } = useQuery(trpc.status.queryOptions()); + const generateSentenceStructureMutation = useMutation( + trpc.generateSentenceStructure.mutationOptions(), + ); + + const { sentenceStructureData, setSentenceStructureData } = + useSentenceStructureData(); + + const { + interactionState, + handleMouseUpOutsideWord, + handleCreateSentenceElementRange, + handleCreateSentenceStructureRange, + handleUpdateSentenceElementName, + handleDeleteRange, + handleStartCreatingRelation, + handleCancelCreatingRelation, + handleDeleteRelation, + handleStartCreatingCoordination, + handleCreateCoordinationChild, + handleConfirmCreatingCoordination, + handleCancelCreatingCoordination, + handleDeleteCoordination, + } = useInteractionState(); + useEffect(() => { + function onMouseUp() { + const result = handleMouseUpOutsideWord(); + if (!result.success) { + alert(result.message); + } + } + document.addEventListener("mouseup", onMouseUp); + return () => { + document.removeEventListener("mouseup", onMouseUp); + }; + }, [handleMouseUpOutsideWord]); + + const canvasRef = useRef(null); + const [maxWidth, setMaxWidth] = useState(1000); + useEffect(() => { + if (!canvasRef.current) return; + const resizeObserver = new ResizeObserver(() => { + setMaxWidth(canvasRef.current?.getBoundingClientRect().width ?? 0); + }); + resizeObserver.observe(canvasRef.current); + + return () => resizeObserver.disconnect(); + }, [canvasRef, setMaxWidth, viewMode]); + + const sentenceStructureDiagramData = + convertSentenceStructureDataToSentenceStructureDiagramData( + sentenceStructureData, + maxWidth, + measureTextWidth, + { + layoutMode: "linear", + }, + ); + + return ( + <> + + + + + { + if ( + newValue === "edit" && + (sentenceStructureData.ranges.length !== 0 || + sentenceStructureData.relations.length !== 0 || + sentenceStructureData.coordinations.length !== 0) + ) { + setIsConfirmClearAnnotationsDialogOpen(true); + return; + } + setViewMode(newValue); + }} + sx={{ flex: 1 }} + > + + + + + + + + + + + + setIsConfirmClearAnnotationsDialogOpen(false)} + onConfirm={() => { + setIsConfirmClearAnnotationsDialogOpen(false); + setViewMode("edit"); + }} + /> + + + + setIsConfirmClearAllDialogOpen(false)} + /> + + + {viewMode === "edit" ? ( + { + setSentenceStructureData( + createSentenceStructureDataFromText({ text: e.target.value }), + ); + }} + placeholder="ここに英文を入力してください。" + /> + ) : ( + + + + + + {/* 範囲の作成 */} + {interactionState.type === "range-confirming" && + (() => { + const endWord = + sentenceStructureDiagramData.words[ + interactionState.endWordIndex + ]; + return ( + e.stopPropagation()} + sx={{ + position: "absolute", + left: + (endWord.position.left + endWord.position.right) / 2, + top: endWord.position.top - 8, + translate: "-50% -100%", + display: "flex", + whiteSpace: "nowrap", + }} + > + + + + + + + + + + + ); + })()} + + {/* 文の要素の付与 */} + {interactionState.type === "range-selected" && + (() => { + const activeRange = findRangeById(sentenceStructureData, { + rangeId: interactionState.rangeId, + }); + if (activeRange === null) + throw new Error("Active range not found"); + const endWord = + sentenceStructureDiagramData.words[ + activeRange.endWordIndex + ]; + return ( + e.stopPropagation()} + > + {(activeRange.kind === "sentence-element" + ? sentenceElementRangeTypeToAllowedSentenceElementNameOptionsMap[ + activeRange.type + ] + : activeRange.kind === "sentence-structure" + ? sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap[ + activeRange.type + ] + : null + )?.map((allowedSentenceElement) => ( + + ))} + + + + + + + + ); + })()} + + {/* 矢印の作成のキャンセル */} + {(interactionState.type === "relation-idle" || + interactionState.type === "relation-selecting") && + (() => { + const endWord = + sentenceStructureDiagramData.words[ + interactionState.fromRange.endWordIndex + ]; + return ( + e.stopPropagation()} + sx={{ + position: "absolute", + left: + (endWord.position.left + endWord.position.right) / 2, + top: endWord.position.top - 8, + translate: "-50% -100%", + display: "flex", + whiteSpace: "nowrap", + }} + > + + + ); + })()} + + {/* 矢印の削除 */} + {interactionState.type === "relation-selected" && + (() => { + const activeRelation = findRelationById( + sentenceStructureData, + { + relationId: interactionState.relationId, + }, + ); + if (activeRelation === null) + throw new Error("Active relation not found"); + const fromRange = findRangeById(sentenceStructureData, { + rangeId: activeRelation.fromRangeId, + }); + if (fromRange === null) + throw new Error("From range not found"); + const endWord = + sentenceStructureDiagramData.words[fromRange.endWordIndex]; + return ( + e.stopPropagation()} + sx={{ + position: "absolute", + left: + (endWord.position.left + endWord.position.right) / 2, + top: endWord.position.top - 8, + translate: "-50% -100%", + display: "flex", + whiteSpace: "nowrap", + }} + > + + + ); + })()} + + {/* 並列の要素の作成 */} + {interactionState.type === "coordination-confirming" && + (() => { + const endWord = + sentenceStructureDiagramData.words[ + interactionState.endWordIndex + ]; + return ( + e.stopPropagation()} + sx={{ + position: "absolute", + left: + (endWord.position.left + endWord.position.right) / 2, + top: endWord.position.top - 8, + translate: "-50% -100%", + display: "flex", + whiteSpace: "nowrap", + }} + > + {coordinationChildTypeOptions.map( + (coordinationChildType) => ( + + ), + )} + + ); + })()} + + {/* 並列の作成のキャンセル */} + {(interactionState.type === "coordination-idle" || + interactionState.type === "coordination-selecting") && + (() => { + const endWord = + sentenceStructureDiagramData.words[ + interactionState.children[0].endWordIndex + ]; + return ( + e.stopPropagation()} + sx={{ + position: "absolute", + left: + (endWord.position.left + endWord.position.right) / 2, + top: endWord.position.top - 8, + translate: "-50% -100%", + display: "flex", + whiteSpace: "nowrap", + }} + > + + + + + ); + })()} + + {/* 並列の削除 */} + {interactionState.type === "coordination-selected" && + (() => { + const activeCoordination = findCoordinationById( + sentenceStructureData, + { + coordinationId: interactionState.coordinationId, + }, + ); + if (activeCoordination === null) + throw new Error("Active coordination not found"); + const endChild = activeCoordination.children.at(-1); + if (endChild == null) throw new Error("End child not found"); + const endWord = + sentenceStructureDiagramData.words[endChild.endWordIndex]; + return ( + e.stopPropagation()} + sx={{ + position: "absolute", + left: + (endWord.position.left + endWord.position.right) / 2, + top: endWord.position.top - 8, + translate: "-50% -100%", + display: "flex", + whiteSpace: "nowrap", + }} + > + + + ); + })()} + + )} + + + + ); +} diff --git a/packages/frontend/src/pages/contexts/ConfigurationsProvider.tsx b/packages/frontend/src/pages/contexts/ConfigurationsProvider.tsx new file mode 100644 index 0000000..07c7bd6 --- /dev/null +++ b/packages/frontend/src/pages/contexts/ConfigurationsProvider.tsx @@ -0,0 +1,72 @@ +import { + createContext, + useContext, + useState, + type PropsWithChildren, +} from "react"; +import { + defaultConfigurations, + stringToConfigurations, + xmlStringToConfigurations, + type Configurations, +} from "@sentence-structure-diagram-app/sentence-structure-diagram-configurations"; + +type ConfigurationsContextValue = { + configurations: Configurations; + setConfigurations: (newConfigurations: Configurations) => void; + setConfigurationsFromXMLData: (xmlString: string) => void; +}; + +const ConfigurationsContext = createContext( + null, +); + +export function ConfigurationsProvider(props: PropsWithChildren) { + const [configurations, _setConfigurations] = useState(() => { + const savedConfigurations = localStorage.getItem("configurations"); + if (!savedConfigurations) { + return defaultConfigurations; + } + const result = stringToConfigurations.safeDecode(savedConfigurations); + if (result.success) { + return result.data; + } else { + return defaultConfigurations; + } + }); + + function setConfigurations(newConfigurations: Configurations) { + const result = stringToConfigurations.safeEncode(newConfigurations); + if (result.success) { + localStorage.setItem("configurations", result.data); + } + _setConfigurations(newConfigurations); + } + + function setConfigurationsFromXMLData(xmlString: string) { + setConfigurations(xmlStringToConfigurations.decode(xmlString)); + } + + return ( + + {props.children} + + ); +} + +// eslint-disable-next-line react-refresh/only-export-components +export function useConfigurations() { + const context = useContext(ConfigurationsContext); + if (!context) { + throw new Error( + "useConfigurations must be used within a ConfigurationsProvider", + ); + } + return context; +} diff --git a/packages/frontend/src/pages/contexts/InteractionStateProvider.tsx b/packages/frontend/src/pages/contexts/InteractionStateProvider.tsx new file mode 100644 index 0000000..e4b8051 --- /dev/null +++ b/packages/frontend/src/pages/contexts/InteractionStateProvider.tsx @@ -0,0 +1,1229 @@ +import { + createContext, + useContext, + useState, + type PropsWithChildren, +} from "react"; +import { + createCoordination, + createRelation, + createSentenceElementRange, + createSentenceStructureRange, + deleteCoordination, + deleteRange, + deleteRelation, + findRangeById, + findRangeByStartAndEndWordIndex, + updateSentenceElementName, + type CoordinationChildType, + type RangeType, + type SentenceElementName, + type SentenceElementRangeType, + type SentenceStructureData, + type SentenceStructureRangeType, +} from "@sentence-structure-diagram-app/sentence-structure-data"; +import { useSentenceStructureData } from "./SentenceStructureDataProvider"; + +type InteractionState = + | { + type: "idle"; + } + | { + type: "range-selecting"; + anchorWordIndex: number; + focusWordIndex: number; + } + | { + type: "range-confirming"; + startWordIndex: number; + endWordIndex: number; + } + | { + type: "range-selected"; + rangeType: RangeType; + rangeId: string; + } + | { + type: "relation-idle"; + fromRange: { + startWordIndex: number; + endWordIndex: number; + }; + } + | { + type: "relation-selecting"; + fromRange: { + startWordIndex: number; + endWordIndex: number; + }; + anchorWordIndex: number; + focusWordIndex: number; + } + | { + type: "relation-selected"; + relationId: string; + } + | { + type: "coordination-idle"; + children: { + type: CoordinationChildType; + startWordIndex: number; + endWordIndex: number; + }[]; + } + | { + type: "coordination-selecting"; + children: { + type: CoordinationChildType; + startWordIndex: number; + endWordIndex: number; + }[]; + anchorWordIndex: number; + focusWordIndex: number; + } + | { + type: "coordination-confirming"; + children: { + type: CoordinationChildType; + startWordIndex: number; + endWordIndex: number; + }[]; + startWordIndex: number; + endWordIndex: number; + } + | { + type: "coordination-selected"; + coordinationId: string; + }; + +type InteractionStateAction = + | { + type: "MOUSE_UP_OUTSIDE_WORD"; + } + | { + type: "MOUSE_DOWN_ON_WORD"; + payload: { + wordIndex: number; + }; + } + | { + type: "MOUSE_ENTER_ON_WORD"; + payload: { + wordIndex: number; + }; + } + | { + type: "MOUSE_UP_ON_WORD"; + payload: { + wordIndex: number; + }; + } + | { + type: "CREATE_SENTENCE_ELEMENT_RANGE"; + payload: { + rangeType: SentenceElementRangeType; + }; + } + | { + type: "CREATE_SENTENCE_STRUCTURE_RANGE"; + payload: { + rangeType: SentenceStructureRangeType; + }; + } + | { + type: "CLICK_ON_RANGE"; + payload: { + rangeId: string; + }; + } + | { + type: "UPDATE_SENTENCE_ELEMENT_NAME"; + payload: { + sentenceElementName: SentenceElementName; + }; + } + | { + type: "DELETE_RANGE"; + } + | { + type: "START_CREATING_RELATION"; + } + | { + type: "CANCEL_CREATING_RELATION"; + } + | { + type: "CLICK_ON_RELATION"; + payload: { + relationId: string; + }; + } + | { + type: "DELETE_RELATION"; + } + | { + type: "START_CREATING_COORDINATION"; + } + | { + type: "CREATE_COORDINATION_CHILD"; + payload: { + coordinationChildType: CoordinationChildType; + }; + } + | { + type: "CONFIRM_CREATING_COORDINATION"; + } + | { + type: "CANCEL_CREATING_COORDINATION"; + } + | { + type: "CLICK_ON_COORDINATION"; + payload: { + coordinationId: string; + }; + } + | { + type: "DELETE_COORDINATION"; + }; + +function interactionStateReducer( + interactionState: InteractionState, + sentenceStructureData: SentenceStructureData, + action: InteractionStateAction, +): + | { + success: true; + interactionState: InteractionState; + sentenceStructureData: SentenceStructureData; + } + | { + success: false; + message: string; + } { + switch (action.type) { + case "MOUSE_UP_OUTSIDE_WORD": + switch (interactionState.type) { + case "idle": + return { + success: true, + interactionState: { type: "idle" }, + sentenceStructureData, + }; + case "range-selecting": { + const matchedRange = findRangeByStartAndEndWordIndex( + sentenceStructureData, + { + startWordIndex: Math.min( + interactionState.anchorWordIndex, + interactionState.focusWordIndex, + ), + endWordIndex: Math.max( + interactionState.anchorWordIndex, + interactionState.focusWordIndex, + ), + }, + ); + if (matchedRange) { + return { + success: true, + interactionState: { + type: "range-selected", + rangeType: matchedRange.type, + rangeId: matchedRange.id, + }, + sentenceStructureData, + }; + } else { + return { + success: true, + interactionState: { + type: "range-confirming", + startWordIndex: Math.min( + interactionState.anchorWordIndex, + interactionState.focusWordIndex, + ), + endWordIndex: Math.max( + interactionState.anchorWordIndex, + interactionState.focusWordIndex, + ), + }, + sentenceStructureData, + }; + } + } + case "range-confirming": + case "range-selected": + case "relation-idle": + return { + success: true, + interactionState: { type: "idle" }, + sentenceStructureData, + }; + case "relation-selecting": { + const newSentenceStructureData = createRelation( + sentenceStructureData, + { + fromRange: interactionState.fromRange, + toRange: { + startWordIndex: Math.min( + interactionState.anchorWordIndex, + interactionState.focusWordIndex, + ), + endWordIndex: Math.max( + interactionState.anchorWordIndex, + interactionState.focusWordIndex, + ), + }, + }, + ); + if (newSentenceStructureData.success) { + return { + success: true, + interactionState: { type: "idle" }, + sentenceStructureData: + newSentenceStructureData.data.newSentenceStructureData, + }; + } else { + return { + success: false, + message: newSentenceStructureData.message, + }; + } + } + case "relation-selected": + case "coordination-idle": + return { + success: true, + interactionState: { type: "idle" }, + sentenceStructureData, + }; + case "coordination-selecting": { + return { + success: true, + interactionState: { + type: "coordination-confirming", + children: interactionState.children, + startWordIndex: Math.min( + interactionState.anchorWordIndex, + interactionState.focusWordIndex, + ), + endWordIndex: Math.max( + interactionState.anchorWordIndex, + interactionState.focusWordIndex, + ), + }, + sentenceStructureData, + }; + } + case "coordination-confirming": + case "coordination-selected": + return { + success: true, + interactionState: { type: "idle" }, + sentenceStructureData, + }; + default: { + const _exhaustiveCheck: never = interactionState; + return _exhaustiveCheck; + } + } + case "MOUSE_DOWN_ON_WORD": + switch (interactionState.type) { + case "idle": + case "range-selecting": + case "range-confirming": + case "range-selected": + case "relation-selected": + case "coordination-selected": + return { + success: true, + interactionState: { + type: "range-selecting", + anchorWordIndex: action.payload.wordIndex, + focusWordIndex: action.payload.wordIndex, + }, + sentenceStructureData, + }; + case "relation-idle": + case "relation-selecting": + return { + success: true, + interactionState: { + type: "relation-selecting", + fromRange: interactionState.fromRange, + anchorWordIndex: action.payload.wordIndex, + focusWordIndex: action.payload.wordIndex, + }, + sentenceStructureData, + }; + case "coordination-idle": + case "coordination-selecting": + case "coordination-confirming": + if (interactionState.children.length === 0) { + return { + success: true, + interactionState: { + type: "range-selecting", + anchorWordIndex: action.payload.wordIndex, + focusWordIndex: action.payload.wordIndex, + }, + sentenceStructureData, + }; + } + return { + success: true, + interactionState: { + type: "coordination-selecting", + children: interactionState.children, + anchorWordIndex: action.payload.wordIndex, + focusWordIndex: action.payload.wordIndex, + }, + sentenceStructureData, + }; + default: { + const _exhaustiveCheck: never = interactionState; + return _exhaustiveCheck; + } + } + case "MOUSE_ENTER_ON_WORD": + switch (interactionState.type) { + case "idle": + return { + success: true, + interactionState, + sentenceStructureData, + }; + case "range-selecting": + return { + success: true, + interactionState: { + type: "range-selecting", + anchorWordIndex: interactionState.anchorWordIndex, + focusWordIndex: action.payload.wordIndex, + }, + sentenceStructureData, + }; + case "range-confirming": + case "range-selected": + case "relation-idle": + return { + success: true, + interactionState, + sentenceStructureData, + }; + case "relation-selecting": + return { + success: true, + interactionState: { + type: "relation-selecting", + fromRange: interactionState.fromRange, + anchorWordIndex: interactionState.anchorWordIndex, + focusWordIndex: action.payload.wordIndex, + }, + sentenceStructureData, + }; + case "relation-selected": + case "coordination-idle": + return { + success: true, + interactionState, + sentenceStructureData, + }; + case "coordination-selecting": + return { + success: true, + interactionState: { + type: "coordination-selecting", + children: interactionState.children, + anchorWordIndex: interactionState.anchorWordIndex, + focusWordIndex: action.payload.wordIndex, + }, + sentenceStructureData, + }; + case "coordination-confirming": + case "coordination-selected": + return { + success: true, + interactionState, + sentenceStructureData, + }; + default: { + const _exhaustiveCheck: never = interactionState; + return _exhaustiveCheck; + } + } + case "MOUSE_UP_ON_WORD": + switch (interactionState.type) { + case "idle": + return { + success: true, + interactionState: { type: "idle" }, + sentenceStructureData, + }; + case "range-selecting": { + const matchedRange = findRangeByStartAndEndWordIndex( + sentenceStructureData, + { + startWordIndex: Math.min( + interactionState.anchorWordIndex, + action.payload.wordIndex, + ), + endWordIndex: Math.max( + interactionState.anchorWordIndex, + action.payload.wordIndex, + ), + }, + ); + if (matchedRange) { + return { + success: true, + interactionState: { + type: "range-selected", + rangeType: matchedRange.type, + rangeId: matchedRange.id, + }, + sentenceStructureData, + }; + } else { + return { + success: true, + interactionState: { + type: "range-confirming", + startWordIndex: Math.min( + interactionState.anchorWordIndex, + action.payload.wordIndex, + ), + endWordIndex: Math.max( + interactionState.anchorWordIndex, + action.payload.wordIndex, + ), + }, + sentenceStructureData, + }; + } + } + case "range-confirming": + case "range-selected": + case "relation-idle": + return { + success: true, + interactionState: { type: "idle" }, + sentenceStructureData, + }; + case "relation-selecting": { + const newSentenceStructureData = createRelation( + sentenceStructureData, + { + fromRange: interactionState.fromRange, + toRange: { + startWordIndex: Math.min( + interactionState.anchorWordIndex, + action.payload.wordIndex, + ), + endWordIndex: Math.max( + interactionState.anchorWordIndex, + action.payload.wordIndex, + ), + }, + }, + ); + if (newSentenceStructureData.success) { + return { + success: true, + interactionState: { type: "idle" }, + sentenceStructureData: + newSentenceStructureData.data.newSentenceStructureData, + }; + } else { + return { + success: false, + message: newSentenceStructureData.message, + }; + } + } + case "relation-selected": + case "coordination-idle": + return { + success: true, + interactionState: { type: "idle" }, + sentenceStructureData, + }; + case "coordination-selecting": { + return { + success: true, + interactionState: { + type: "coordination-confirming", + children: interactionState.children, + startWordIndex: Math.min( + interactionState.anchorWordIndex, + action.payload.wordIndex, + ), + endWordIndex: Math.max( + interactionState.anchorWordIndex, + action.payload.wordIndex, + ), + }, + sentenceStructureData, + }; + } + case "coordination-confirming": + case "coordination-selected": + return { + success: true, + interactionState: { type: "idle" }, + sentenceStructureData, + }; + default: { + const _exhaustiveCheck: never = interactionState; + return _exhaustiveCheck; + } + } + case "CREATE_SENTENCE_ELEMENT_RANGE": { + if (interactionState.type !== "range-confirming") + throw new Error("Invalid interaction state"); + const result = createSentenceElementRange(sentenceStructureData, { + type: action.payload.rangeType, + startWordIndex: interactionState.startWordIndex, + endWordIndex: interactionState.endWordIndex, + }); + if (result.success) { + return { + success: true, + interactionState: { + type: "range-selected", + rangeType: action.payload.rangeType, + rangeId: result.data.rangeId, + }, + sentenceStructureData: result.data.newSentenceStructureData, + }; + } else { + return { success: false, message: result.message }; + } + } + case "CREATE_SENTENCE_STRUCTURE_RANGE": { + if (interactionState.type !== "range-confirming") + throw new Error("Invalid interaction state"); + const result = createSentenceStructureRange(sentenceStructureData, { + type: action.payload.rangeType, + startWordIndex: interactionState.startWordIndex, + endWordIndex: interactionState.endWordIndex, + }); + if (result.success) { + return { + success: true, + interactionState: { + type: "range-selected", + rangeType: action.payload.rangeType, + rangeId: result.data.rangeId, + }, + sentenceStructureData: result.data.newSentenceStructureData, + }; + } else { + return { success: false, message: result.message }; + } + } + case "CLICK_ON_RANGE": { + const matchedRange = findRangeById(sentenceStructureData, { + rangeId: action.payload.rangeId, + }); + if (!matchedRange) throw new Error("Invalid range ID"); + + return { + success: true, + interactionState: { + type: "range-selected", + rangeType: matchedRange.type, + rangeId: action.payload.rangeId, + }, + sentenceStructureData, + }; + } + case "UPDATE_SENTENCE_ELEMENT_NAME": + if (interactionState.type !== "range-selected") + throw new Error("Invalid interaction state"); + return { + success: true, + interactionState, + sentenceStructureData: updateSentenceElementName< + typeof interactionState.rangeType + >(sentenceStructureData, { + rangeId: interactionState.rangeId, + sentenceElementName: action.payload.sentenceElementName, + }), + }; + case "DELETE_RANGE": + if (interactionState.type !== "range-selected") + throw new Error("Invalid interaction state"); + return { + success: true, + interactionState: { + type: "idle", + }, + sentenceStructureData: deleteRange(sentenceStructureData, { + rangeId: interactionState.rangeId, + }), + }; + case "START_CREATING_RELATION": { + if ( + interactionState.type !== "range-confirming" && + interactionState.type !== "range-selected" + ) + throw new Error("Invalid interaction state"); + const fromRange = + interactionState.type === "range-confirming" + ? { + startWordIndex: interactionState.startWordIndex, + endWordIndex: interactionState.endWordIndex, + } + : findRangeById(sentenceStructureData, { + rangeId: interactionState.rangeId, + }); + if (!fromRange) throw new Error("Invalid range ID"); + return { + success: true, + interactionState: { + type: "relation-idle", + fromRange, + }, + sentenceStructureData, + }; + } + case "CANCEL_CREATING_RELATION": { + if ( + interactionState.type !== "relation-idle" && + interactionState.type !== "relation-selecting" + ) + throw new Error("Invalid interaction state"); + const fromRange = findRangeByStartAndEndWordIndex(sentenceStructureData, { + startWordIndex: interactionState.fromRange.startWordIndex, + endWordIndex: interactionState.fromRange.endWordIndex, + }); + if (fromRange) { + return { + success: true, + interactionState: { + type: "range-selected", + rangeType: fromRange.type, + rangeId: fromRange.id, + }, + sentenceStructureData, + }; + } else { + return { + success: true, + interactionState: { + type: "idle", + }, + sentenceStructureData, + }; + } + } + case "CLICK_ON_RELATION": + return { + success: true, + interactionState: { + type: "relation-selected", + relationId: action.payload.relationId, + }, + sentenceStructureData, + }; + case "DELETE_RELATION": + if (interactionState.type !== "relation-selected") + throw new Error("Invalid interaction state"); + return { + success: true, + interactionState: { + type: "idle", + }, + sentenceStructureData: deleteRelation(sentenceStructureData, { + relationId: interactionState.relationId, + }), + }; + case "START_CREATING_COORDINATION": + if ( + interactionState.type !== "range-confirming" && + interactionState.type !== "range-selected" + ) + throw new Error("Invalid interaction state"); + switch (interactionState.type) { + case "range-confirming": + return { + success: true, + interactionState: { + type: "coordination-confirming", + children: [], + startWordIndex: interactionState.startWordIndex, + endWordIndex: interactionState.endWordIndex, + }, + sentenceStructureData, + }; + case "range-selected": { + const selectingRange = findRangeById(sentenceStructureData, { + rangeId: interactionState.rangeId, + }); + if (!selectingRange) throw new Error("Invalid range ID"); + return { + success: true, + interactionState: { + type: "coordination-confirming", + children: [], + startWordIndex: selectingRange.startWordIndex, + endWordIndex: selectingRange.endWordIndex, + }, + sentenceStructureData, + }; + } + default: { + const _exhaustiveCheck: never = interactionState; + return _exhaustiveCheck; + } + } + case "CREATE_COORDINATION_CHILD": + if (interactionState.type !== "coordination-confirming") + throw new Error("Invalid interaction state"); + return { + success: true, + interactionState: { + type: "coordination-idle", + children: [ + ...interactionState.children, + { + type: action.payload.coordinationChildType, + startWordIndex: interactionState.startWordIndex, + endWordIndex: interactionState.endWordIndex, + }, + ], + }, + sentenceStructureData, + }; + case "CONFIRM_CREATING_COORDINATION": { + if ( + interactionState.type !== "coordination-idle" && + interactionState.type !== "coordination-selecting" + ) + throw new Error("Invalid interaction state"); + const result = createCoordination(sentenceStructureData, { + children: interactionState.children, + }); + if (result.success) { + return { + success: true, + interactionState: { + type: "idle", + }, + sentenceStructureData: result.data.newSentenceStructureData, + }; + } else { + return { success: false, message: result.message }; + } + } + case "CANCEL_CREATING_COORDINATION": + if ( + interactionState.type !== "coordination-idle" && + interactionState.type !== "coordination-selecting" + ) + throw new Error("Invalid interaction state"); + return { + success: true, + interactionState: { + type: "idle", + }, + sentenceStructureData, + }; + case "CLICK_ON_COORDINATION": + return { + success: true, + interactionState: { + type: "coordination-selected", + coordinationId: action.payload.coordinationId, + }, + sentenceStructureData, + }; + case "DELETE_COORDINATION": + if (interactionState.type !== "coordination-selected") + throw new Error("Invalid interaction state"); + return { + success: true, + interactionState: { + type: "idle", + }, + sentenceStructureData: deleteCoordination(sentenceStructureData, { + coordinationId: interactionState.coordinationId, + }), + }; + default: { + const _exhaustiveCheck: never = action; + return _exhaustiveCheck; + } + } +} + +type InteractionStateContextValue = { + interactionState: InteractionState; + handleMouseUpOutsideWord: () => + | { success: true } + | { success: false; message: string }; + handleMouseDownOnWord: ( + wordIndex: number, + ) => { success: true } | { success: false; message: string }; + handleMouseEnterOnWord: ( + wordIndex: number, + ) => { success: true } | { success: false; message: string }; + handleMouseUpOnWord: ( + wordIndex: number, + ) => { success: true } | { success: false; message: string }; + handleCreateSentenceElementRange: ( + rangeType: SentenceElementRangeType, + ) => { success: true } | { success: false; message: string }; + handleCreateSentenceStructureRange: ( + rangeType: SentenceStructureRangeType, + ) => { success: true } | { success: false; message: string }; + handleClickOnRange: ( + rangeId: string, + ) => { success: true } | { success: false; message: string }; + handleUpdateSentenceElementName: ( + sentenceElementName: SentenceElementName, + ) => { success: true } | { success: false; message: string }; + handleDeleteRange: () => + | { success: true } + | { success: false; message: string }; + handleStartCreatingRelation: () => + | { success: true } + | { success: false; message: string }; + handleCancelCreatingRelation: () => + | { success: true } + | { success: false; message: string }; + handleClickOnRelation: ( + relationId: string, + ) => { success: true } | { success: false; message: string }; + handleDeleteRelation: () => + | { success: true } + | { success: false; message: string }; + handleStartCreatingCoordination: () => + | { success: true } + | { success: false; message: string }; + handleCreateCoordinationChild: ( + coordinationChildType: CoordinationChildType, + ) => { success: true } | { success: false; message: string }; + handleConfirmCreatingCoordination: () => + | { success: true } + | { success: false; message: string }; + handleCancelCreatingCoordination: () => + | { success: true } + | { success: false; message: string }; + handleClickOnCoordination: ( + coordinationId: string, + ) => { success: true } | { success: false; message: string }; + handleDeleteCoordination: () => + | { success: true } + | { success: false; message: string }; +}; + +const InteractionStateContext = + createContext(null); + +export function InteractionStateProvider(props: PropsWithChildren) { + const [interactionState, setInteractionState] = useState({ + type: "idle", + }); + const { sentenceStructureData, setSentenceStructureData } = + useSentenceStructureData(); + + return ( + { + const result = interactionStateReducer( + interactionState, + sentenceStructureData, + { type: "MOUSE_UP_OUTSIDE_WORD" }, + ); + if (result.success) { + setInteractionState(result.interactionState); + setSentenceStructureData(result.sentenceStructureData); + return { success: true }; + } else { + return { success: false, message: result.message }; + } + }, + handleMouseDownOnWord: (wordIndex: number) => { + const result = interactionStateReducer( + interactionState, + sentenceStructureData, + { type: "MOUSE_DOWN_ON_WORD", payload: { wordIndex } }, + ); + if (result.success) { + setInteractionState(result.interactionState); + setSentenceStructureData(result.sentenceStructureData); + return { success: true }; + } else { + return { success: false, message: result.message }; + } + }, + handleMouseEnterOnWord: (wordIndex: number) => { + const result = interactionStateReducer( + interactionState, + sentenceStructureData, + { type: "MOUSE_ENTER_ON_WORD", payload: { wordIndex } }, + ); + if (result.success) { + setInteractionState(result.interactionState); + setSentenceStructureData(result.sentenceStructureData); + return { success: true }; + } else { + return { success: false, message: result.message }; + } + }, + handleMouseUpOnWord: (wordIndex: number) => { + const result = interactionStateReducer( + interactionState, + sentenceStructureData, + { type: "MOUSE_UP_ON_WORD", payload: { wordIndex } }, + ); + if (result.success) { + setInteractionState(result.interactionState); + setSentenceStructureData(result.sentenceStructureData); + return { success: true }; + } else { + return { success: false, message: result.message }; + } + }, + handleCreateSentenceElementRange: (rangeType) => { + const result = interactionStateReducer( + interactionState, + sentenceStructureData, + { + type: "CREATE_SENTENCE_ELEMENT_RANGE", + payload: { rangeType }, + }, + ); + if (result.success) { + setInteractionState(result.interactionState); + setSentenceStructureData(result.sentenceStructureData); + return { success: true }; + } else { + return { success: false, message: result.message }; + } + }, + handleCreateSentenceStructureRange: (rangeType) => { + const result = interactionStateReducer( + interactionState, + sentenceStructureData, + { + type: "CREATE_SENTENCE_STRUCTURE_RANGE", + payload: { rangeType }, + }, + ); + if (result.success) { + setInteractionState(result.interactionState); + setSentenceStructureData(result.sentenceStructureData); + return { success: true }; + } else { + return { success: false, message: result.message }; + } + }, + handleClickOnRange: (rangeId: string) => { + const result = interactionStateReducer( + interactionState, + sentenceStructureData, + { type: "CLICK_ON_RANGE", payload: { rangeId } }, + ); + if (result.success) { + setInteractionState(result.interactionState); + setSentenceStructureData(result.sentenceStructureData); + return { success: true }; + } else { + return { success: false, message: result.message }; + } + }, + handleUpdateSentenceElementName: (sentenceElementName) => { + const result = interactionStateReducer( + interactionState, + sentenceStructureData, + { + type: "UPDATE_SENTENCE_ELEMENT_NAME", + payload: { sentenceElementName }, + }, + ); + if (result.success) { + setInteractionState(result.interactionState); + setSentenceStructureData(result.sentenceStructureData); + return { success: true }; + } else { + return { success: false, message: result.message }; + } + }, + handleDeleteRange: () => { + const result = interactionStateReducer( + interactionState, + sentenceStructureData, + { type: "DELETE_RANGE" }, + ); + if (result.success) { + setInteractionState(result.interactionState); + setSentenceStructureData(result.sentenceStructureData); + return { success: true }; + } else { + return { success: false, message: result.message }; + } + }, + handleStartCreatingRelation: () => { + const result = interactionStateReducer( + interactionState, + sentenceStructureData, + { type: "START_CREATING_RELATION" }, + ); + if (result.success) { + setInteractionState(result.interactionState); + setSentenceStructureData(result.sentenceStructureData); + return { success: true }; + } else { + return { success: false, message: result.message }; + } + }, + handleCancelCreatingRelation: () => { + const result = interactionStateReducer( + interactionState, + sentenceStructureData, + { type: "CANCEL_CREATING_RELATION" }, + ); + if (result.success) { + setInteractionState(result.interactionState); + setSentenceStructureData(result.sentenceStructureData); + return { success: true }; + } else { + return { success: false, message: result.message }; + } + }, + handleClickOnRelation: (relationId: string) => { + const result = interactionStateReducer( + interactionState, + sentenceStructureData, + { type: "CLICK_ON_RELATION", payload: { relationId } }, + ); + if (result.success) { + setInteractionState(result.interactionState); + setSentenceStructureData(result.sentenceStructureData); + return { success: true }; + } else { + return { success: false, message: result.message }; + } + }, + handleDeleteRelation: () => { + const result = interactionStateReducer( + interactionState, + sentenceStructureData, + { type: "DELETE_RELATION" }, + ); + if (result.success) { + setInteractionState(result.interactionState); + setSentenceStructureData(result.sentenceStructureData); + return { success: true }; + } else { + return { success: false, message: result.message }; + } + }, + handleStartCreatingCoordination: () => { + const result = interactionStateReducer( + interactionState, + sentenceStructureData, + { type: "START_CREATING_COORDINATION" }, + ); + if (result.success) { + setInteractionState(result.interactionState); + setSentenceStructureData(result.sentenceStructureData); + return { success: true }; + } else { + return { success: false, message: result.message }; + } + }, + handleCreateCoordinationChild: (coordinationChildType) => { + const result = interactionStateReducer( + interactionState, + sentenceStructureData, + { + type: "CREATE_COORDINATION_CHILD", + payload: { coordinationChildType }, + }, + ); + if (result.success) { + setInteractionState(result.interactionState); + setSentenceStructureData(result.sentenceStructureData); + return { success: true }; + } else { + return { success: false, message: result.message }; + } + }, + handleConfirmCreatingCoordination: () => { + const result = interactionStateReducer( + interactionState, + sentenceStructureData, + { type: "CONFIRM_CREATING_COORDINATION" }, + ); + if (result.success) { + setInteractionState(result.interactionState); + setSentenceStructureData(result.sentenceStructureData); + return { success: true }; + } else { + return { success: false, message: result.message }; + } + }, + handleCancelCreatingCoordination: () => { + const result = interactionStateReducer( + interactionState, + sentenceStructureData, + { type: "CANCEL_CREATING_COORDINATION" }, + ); + if (result.success) { + setInteractionState(result.interactionState); + setSentenceStructureData(result.sentenceStructureData); + return { success: true }; + } else { + return { success: false, message: result.message }; + } + }, + handleClickOnCoordination: (coordinationId: string) => { + const result = interactionStateReducer( + interactionState, + sentenceStructureData, + { type: "CLICK_ON_COORDINATION", payload: { coordinationId } }, + ); + if (result.success) { + setInteractionState(result.interactionState); + setSentenceStructureData(result.sentenceStructureData); + return { success: true }; + } else { + return { success: false, message: result.message }; + } + }, + handleDeleteCoordination: () => { + const result = interactionStateReducer( + interactionState, + sentenceStructureData, + { type: "DELETE_COORDINATION" }, + ); + if (result.success) { + setInteractionState(result.interactionState); + setSentenceStructureData(result.sentenceStructureData); + return { success: true }; + } else { + return { success: false, message: result.message }; + } + }, + }} + > + {props.children} + + ); +} + +// eslint-disable-next-line react-refresh/only-export-components +export function useInteractionState() { + const context = useContext(InteractionStateContext); + if (!context) { + throw new Error( + "useInteractionState must be used within a InteractionStateProvider", + ); + } + return context; +} diff --git a/packages/frontend/src/pages/contexts/SentenceStructureDataProvider.tsx b/packages/frontend/src/pages/contexts/SentenceStructureDataProvider.tsx new file mode 100644 index 0000000..c23e6b5 --- /dev/null +++ b/packages/frontend/src/pages/contexts/SentenceStructureDataProvider.tsx @@ -0,0 +1,82 @@ +import { + createContext, + useContext, + useState, + type PropsWithChildren, +} from "react"; +import { + createSentenceStructureDataFromStringData, + createSentenceStructureDataFromText, + sentenceStructureDataToString, + type SentenceStructureData, +} from "@sentence-structure-diagram-app/sentence-structure-data"; + +const initialText = ""; + +type SentenceStructureDataContextValue = { + initialSentenceStructureData: SentenceStructureData; + sentenceStructureData: SentenceStructureData; + setSentenceStructureData: ( + newSentenceStructureData: SentenceStructureData, + ) => void; +}; + +const SentenceStructureDataContext = + createContext(null); + +export function SentenceStructureDataProvider(props: PropsWithChildren) { + const [sentenceStructureData, _setSentenceStructureData] = + useState(() => { + const savedSentenceStructureData = localStorage.getItem( + "sentenceStructureData", + ); + if (!savedSentenceStructureData) { + return createSentenceStructureDataFromText({ text: initialText }); + } + const result = createSentenceStructureDataFromStringData( + savedSentenceStructureData, + ); + if (result.success) { + return result.data.newSentenceStructureData; + } else { + return createSentenceStructureDataFromText({ + text: initialText, + }); + } + }); + + function setSentenceStructureData( + newSentenceStructureData: SentenceStructureData, + ) { + localStorage.setItem( + "sentenceStructureData", + sentenceStructureDataToString(newSentenceStructureData), + ); + _setSentenceStructureData(newSentenceStructureData); + } + + return ( + + {props.children} + + ); +} + +// eslint-disable-next-line react-refresh/only-export-components +export function useSentenceStructureData() { + const context = useContext(SentenceStructureDataContext); + if (!context) { + throw new Error( + "useSentenceStructureData must be used within a SentenceStructureDataProvider", + ); + } + return context; +} diff --git a/packages/frontend/src/pages/index.tsx b/packages/frontend/src/pages/index.tsx new file mode 100644 index 0000000..484f748 --- /dev/null +++ b/packages/frontend/src/pages/index.tsx @@ -0,0 +1,16 @@ +import { SentenceStructureDataProvider } from "./contexts/SentenceStructureDataProvider"; +import { ConfigurationsProvider } from "./contexts/ConfigurationsProvider"; +import { InteractionStateProvider } from "./contexts/InteractionStateProvider"; +import SentenceStructureEditor from "./components/SentenceStructureEditor"; + +export default function Home() { + return ( + + + + + + + + ); +} diff --git a/packages/frontend/src/pages/utils/measure-text-width.ts b/packages/frontend/src/pages/utils/measure-text-width.ts new file mode 100644 index 0000000..18b4f8c --- /dev/null +++ b/packages/frontend/src/pages/utils/measure-text-width.ts @@ -0,0 +1,9 @@ +export function measureTextWidth(text: string): number { + const canvas = document.createElement("canvas"); + const context = canvas.getContext("2d"); + if (!context) return 0; + context.font = "16px system-ui"; + + const metrics = context.measureText(text); + return metrics.width; +} diff --git a/packages/frontend/src/utils/trpc.ts b/packages/frontend/src/utils/trpc.ts new file mode 100644 index 0000000..9537fa1 --- /dev/null +++ b/packages/frontend/src/utils/trpc.ts @@ -0,0 +1,15 @@ +import { createTRPCClient, httpBatchLink } from "@trpc/client"; +import { QueryClient } from "@tanstack/react-query"; +import { createTRPCOptionsProxy } from "@trpc/tanstack-react-query"; +import type { AppRouter } from "@sentence-structure-diagram-app/backend"; + +export const queryClient = new QueryClient(); + +const trpcClient = createTRPCClient({ + links: [httpBatchLink({ url: `${import.meta.env.VITE_API_ENDPOINT}/trpc` })], +}); + +export const trpc = createTRPCOptionsProxy({ + client: trpcClient, + queryClient, +}); diff --git a/packages/frontend/tsconfig.app.json b/packages/frontend/tsconfig.app.json new file mode 100644 index 0000000..a9b5a59 --- /dev/null +++ b/packages/frontend/tsconfig.app.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/packages/frontend/tsconfig.json b/packages/frontend/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/packages/frontend/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/packages/frontend/tsconfig.node.json b/packages/frontend/tsconfig.node.json new file mode 100644 index 0000000..8a67f62 --- /dev/null +++ b/packages/frontend/tsconfig.node.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts new file mode 100644 index 0000000..9313147 --- /dev/null +++ b/packages/frontend/vite.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], + base: process.env.BASE_URL || "/", +}); diff --git a/packages/sentence-structure-data/.gitignore b/packages/sentence-structure-data/.gitignore new file mode 100644 index 0000000..9b1c8b1 --- /dev/null +++ b/packages/sentence-structure-data/.gitignore @@ -0,0 +1 @@ +/dist diff --git a/packages/sentence-structure-data/format.ts b/packages/sentence-structure-data/format.ts new file mode 100644 index 0000000..9881db6 --- /dev/null +++ b/packages/sentence-structure-data/format.ts @@ -0,0 +1,281 @@ +import * as z from "zod"; +import { XMLBuilder, XMLParser } from "fast-xml-parser"; +import { + sentenceElementRangeTypeOptions, + SentenceStructureDataSchema, + type Coordination, + type CoordinationChildType, + type Range, + type Relation, + type SentenceElementRangeType, + type SentenceStructureRangeType, +} from "./schema.js"; +import { + SimplifiedSentenceStructureDataSchema, + type SimplifiedSentenceStructureData, +} from "./simplified-schema.js"; + +const sentenceElementRangeTypePairs = [ + ["core-sentence-element", "文の主要素"], +] as const satisfies [SentenceElementRangeType, string][]; +const sentenceElementRangeTypeToSimplifiedSentenceElementRangeTypeMap = new Map( + sentenceElementRangeTypePairs, +); +const simplifiedSentenceElementRangeTypeToSentenceElementRangeTypeMap = new Map( + sentenceElementRangeTypePairs.map(([a, b]) => [b, a]), +); + +const sentenceStructureRangeTypePairs = [ + ["modifier", "修飾語"], + ["phrase", "句"], + ["clause", "節"], +] as const satisfies [SentenceStructureRangeType, string][]; +const sentenceStructureRangeTypeToSimplifiedSentenceStructureRangeTypeMap = + new Map(sentenceStructureRangeTypePairs); +const simplifiedSentenceStructureRangeTypeToSentenceStructureRangeTypeMap = + new Map(sentenceStructureRangeTypePairs.map(([a, b]) => [b, a])); + +const coordinationChildTypePairs = [ + ["coordinating conjunction", "等位接続詞"], + ["correlative conjunction", "相関接続詞"], + ["conjunct", "並列要素"], +] as const satisfies [CoordinationChildType, string][]; +const coordinationChildTypeToSimplifiedCoordinationChildTypeMap = new Map( + coordinationChildTypePairs, +); +const simplifiedCoordinationChildTypeToCoordinationChildTypeMap = new Map( + coordinationChildTypePairs.map(([a, b]) => [b, a]), +); + +export const simplifiedSentenceStructureDataToSentenceStructureData = z.codec( + SimplifiedSentenceStructureDataSchema, + SentenceStructureDataSchema, + { + decode: (simplifiedSentenceStructureData) => { + const sortedSimplifiedSentenceStructureData = { + text: simplifiedSentenceStructureData.text, + words: simplifiedSentenceStructureData.words.sort( + (a, b) => a.index - b.index, + ), + ranges: simplifiedSentenceStructureData.ranges.sort( + (a, b) => a.index - b.index, + ), + relations: simplifiedSentenceStructureData.relations, + coordinations: simplifiedSentenceStructureData.coordinations.map( + (coordination) => ({ + children: coordination.children.sort( + (a, b) => a.startWordIndex - b.startWordIndex, + ), + }), + ), + }; + const rangeIds = sortedSimplifiedSentenceStructureData.ranges.map(() => + crypto.randomUUID(), + ); + + return { + text: sortedSimplifiedSentenceStructureData.text, + words: sortedSimplifiedSentenceStructureData.words, + ranges: sortedSimplifiedSentenceStructureData.ranges.map((range) => + range.type === "関係" + ? { + kind: "relation", + type: "relation", + id: rangeIds[range.index]!, + startWordIndex: range.startWordIndex, + endWordIndex: range.endWordIndex, + } + : sentenceElementRangeTypeOptions.includes( + simplifiedSentenceElementRangeTypeToSentenceElementRangeTypeMap.get( + range.type as any, + )!, + ) + ? { + kind: "sentence-element", + type: simplifiedSentenceElementRangeTypeToSentenceElementRangeTypeMap.get( + range.type as any, + )!, + id: rangeIds[range.index]!, + startWordIndex: range.startWordIndex, + endWordIndex: range.endWordIndex, + sentenceElementName: range.sentenceElementName, + } + : { + kind: "sentence-structure", + type: simplifiedSentenceStructureRangeTypeToSentenceStructureRangeTypeMap.get( + range.type as any, + )!, + id: rangeIds[range.index]!, + startWordIndex: range.startWordIndex, + endWordIndex: range.endWordIndex, + sentenceElementName: range.sentenceElementName, + }, + ) as Range[], + relations: sortedSimplifiedSentenceStructureData.relations.map( + (relation) => { + return { + id: crypto.randomUUID(), + fromRangeId: rangeIds[relation.fromRangeIndex]!, + toRangeId: rangeIds[relation.toRangeIndex]!, + }; + }, + ) satisfies Relation[], + coordinations: sortedSimplifiedSentenceStructureData.coordinations.map( + (coordination) => ({ + id: crypto.randomUUID(), + children: coordination.children.map((child, index) => ({ + type: simplifiedCoordinationChildTypeToCoordinationChildTypeMap.get( + child.type, + )!, + index: index, + startWordIndex: child.startWordIndex, + endWordIndex: child.endWordIndex, + })), + }), + ) satisfies Coordination[], + }; + }, + encode: (sentenceStructureData) => { + const sortedSentenceStructureData = { + ...sentenceStructureData, + words: sentenceStructureData.words.sort((a, b) => a.index - b.index), + ranges: sentenceStructureData.ranges.sort((a, b) => { + if (a.startWordIndex !== b.startWordIndex) { + return a.startWordIndex - b.startWordIndex; + } + return b.endWordIndex - a.endWordIndex; + }), + relations: sentenceStructureData.relations, + coordinations: sentenceStructureData.coordinations + .map((coordination) => ({ + children: coordination.children.sort((a, b) => a.index - b.index), + })) + .sort((a, b) => { + if ( + a.children.at(0)!.startWordIndex !== + b.children.at(0)!.startWordIndex + ) { + return ( + a.children.at(0)!.startWordIndex - + b.children.at(0)!.startWordIndex + ); + } + return ( + b.children.at(-1)!.endWordIndex - a.children.at(-1)!.endWordIndex + ); + }), + }; + return { + text: sortedSentenceStructureData.text, + words: sortedSentenceStructureData.words, + ranges: sortedSentenceStructureData.ranges.map((range, index) => + range.kind === "relation" + ? { + type: "関係", + index: index, + startWordIndex: range.startWordIndex, + endWordIndex: range.endWordIndex, + } + : { + type: + range.kind === "sentence-element" + ? sentenceElementRangeTypeToSimplifiedSentenceElementRangeTypeMap.get( + range.type, + )! + : sentenceStructureRangeTypeToSimplifiedSentenceStructureRangeTypeMap.get( + range.type, + )!, + index: index, + startWordIndex: range.startWordIndex, + endWordIndex: range.endWordIndex, + sentenceElementName: range.sentenceElementName, + }, + ) as SimplifiedSentenceStructureData["ranges"], + relations: ( + sortedSentenceStructureData.relations.map((relation) => ({ + fromRangeIndex: sortedSentenceStructureData.ranges.findIndex( + (range) => range.id === relation.fromRangeId, + ), + toRangeIndex: sortedSentenceStructureData.ranges.findIndex( + (range) => range.id === relation.toRangeId, + ), + })) satisfies SimplifiedSentenceStructureData["relations"] + ).sort((a, b) => { + if (a.fromRangeIndex !== b.fromRangeIndex) { + return a.fromRangeIndex - b.fromRangeIndex; + } + return b.toRangeIndex - a.toRangeIndex; + }), + coordinations: sortedSentenceStructureData.coordinations.map( + (coordination) => ({ + children: coordination.children.map((child) => ({ + type: coordinationChildTypeToSimplifiedCoordinationChildTypeMap.get( + child.type, + )!, + startWordIndex: child.startWordIndex, + endWordIndex: child.endWordIndex, + })), + }), + ) satisfies SimplifiedSentenceStructureData["coordinations"], + }; + }, + }, +); + +export const stringToSentenceStructureData = z.codec( + z.string(), + SentenceStructureDataSchema, + { + decode: (string) => + simplifiedSentenceStructureDataToSentenceStructureData.decode( + JSON.parse(string), + ), + encode: (sentenceStructureData) => + JSON.stringify( + simplifiedSentenceStructureDataToSentenceStructureData.encode( + sentenceStructureData, + ), + null, + 2, + ), + }, +); + +export const xmlStringToSentenceStructureData = z.codec( + z.string(), + SentenceStructureDataSchema, + { + decode: (xml) => { + const parsed = new XMLParser({ + isArray: (tagName) => + [ + "words", + "ranges", + "relations", + "coordinations", + "children", + ].includes(tagName), + }).parse(xml); + return simplifiedSentenceStructureDataToSentenceStructureData.decode({ + words: [], + relations: [], + coordinations: [], + ...parsed, + ranges: + parsed.ranges?.map((range: any) => ({ + ...range, + sentenceElementName: + range.sentenceElementName === "" + ? null + : range.sentenceElementName, + })) ?? [], + }); + }, + encode: (sentenceStructureData) => + new XMLBuilder({ format: true }).build( + simplifiedSentenceStructureDataToSentenceStructureData.encode( + sentenceStructureData, + ), + ), + }, +); diff --git a/packages/sentence-structure-data/index.ts b/packages/sentence-structure-data/index.ts new file mode 100644 index 0000000..db80ee6 --- /dev/null +++ b/packages/sentence-structure-data/index.ts @@ -0,0 +1,47 @@ +export { + sentenceElementNameOptions, + sentenceElementRangeTypeOptions, + sentenceElementRangeTypeToAllowedSentenceElementNameOptionsMap, + sentenceStructureRangeTypeOptions, + sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap, + coordinationChildTypeOptions, + SentenceStructureDataSchema, + type Word, + type SentenceElementName, + type SentenceElementRangeType, + type SentenceStructureRangeType, + type RangeType, + type Range, + type Relation, + type CoordinationChildType, + type CoordinationChild, + type Coordination, + type SentenceStructureData, +} from "./schema.js"; +export { + SimplifiedAnnotationDataSchema, + SimplifiedSentenceStructureDataSchema, + type SimplifiedAnnotationData, + type SimplifiedSentenceStructureData, +} from "./simplified-schema.js"; +export { + createSentenceStructureDataFromText, + createSentenceStructureDataFromStringData, + createSentenceStructureDataFromXMLData, + createSentenceStructureDataFromSimplifiedAnnotationData, + sentenceStructureDataToString, + sentenceStructureDataToXMLString, + createSentenceElementRange, + createSentenceStructureRange, + findRangeById, + findRangeByStartAndEndWordIndex, + updateSentenceElementName, + deleteRange, + createRelation, + findRelationById, + deleteRelation, + createCoordination, + findCoordinationById, + findCoordinationByStartAndEndWordIndex, + deleteCoordination, +} from "./operations.js"; diff --git a/packages/sentence-structure-data/operations.ts b/packages/sentence-structure-data/operations.ts new file mode 100644 index 0000000..58065bd --- /dev/null +++ b/packages/sentence-structure-data/operations.ts @@ -0,0 +1,559 @@ +import { + sentenceElementRangeTypeToAllowedSentenceElementNameOptionsMap, + SentenceStructureDataSchema, + sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap, + type Coordination, + type CoordinationChildType, + type Range, + type Relation, + type SentenceElementRangeType, + type SentenceStructureData, + type SentenceStructureRangeType, +} from "./schema.js"; +import { + simplifiedSentenceStructureDataToSentenceStructureData, + stringToSentenceStructureData, + xmlStringToSentenceStructureData, +} from "./format.js"; +import { tokenizeText } from "./tokenize-text.js"; +import type { + SimplifiedAnnotationData, + SimplifiedSentenceStructureData, +} from "./simplified-schema.js"; + +type Result = + | { + success: true; + data: T; + } + | { + success: false; + message: string; + }; + +export function createSentenceStructureDataFromText(input: { + text: string; +}): SentenceStructureData { + return SentenceStructureDataSchema.parse({ + text: input.text, + words: tokenizeText(input.text), + ranges: [], + relations: [], + coordinations: [], + } satisfies SentenceStructureData); +} + +export function createSentenceStructureDataFromStringData( + string: string, +): Result<{ newSentenceStructureData: SentenceStructureData }> { + const newSentenceStructureData = + stringToSentenceStructureData.safeDecode(string); + + if (newSentenceStructureData.success) { + return { + success: true, + data: { + newSentenceStructureData: newSentenceStructureData.data, + }, + }; + } + const errorMessage = + newSentenceStructureData.error.issues.find( + (issue) => issue.code === "custom", + )?.message ?? null; + if (errorMessage) { + return { + success: false, + message: errorMessage, + }; + } + return { + success: false, + message: "フォーマットが正しくありません。", + }; +} + +export function createSentenceStructureDataFromXMLData( + xmlString: string, +): Result<{ newSentenceStructureData: SentenceStructureData }> { + const newSentenceStructureData = + xmlStringToSentenceStructureData.safeDecode(xmlString); + + if (newSentenceStructureData.success) { + return { + success: true, + data: { + newSentenceStructureData: newSentenceStructureData.data, + }, + }; + } + const errorMessage = + newSentenceStructureData.error.issues.find( + (issue) => issue.code === "custom", + )?.message ?? null; + if (errorMessage) { + return { + success: false, + message: errorMessage, + }; + } + return { + success: false, + message: "フォーマットが正しくありません。", + }; +} + +export function createSentenceStructureDataFromSimplifiedAnnotationData( + text: string, + simplifiedAnnotationData: SimplifiedAnnotationData, +): Result<{ newSentenceStructureData: SentenceStructureData }> { + const simplifiedSentenceStructureData: SimplifiedSentenceStructureData = { + text: text, + words: tokenizeText(text), + ...simplifiedAnnotationData, + }; + const newSentenceStructureData = + simplifiedSentenceStructureDataToSentenceStructureData.safeDecode( + simplifiedSentenceStructureData, + ); + + if (newSentenceStructureData.success) { + return { + success: true, + data: { + newSentenceStructureData: newSentenceStructureData.data, + }, + }; + } + const errorMessage = + newSentenceStructureData.error.issues.find( + (issue) => issue.code === "custom", + )?.message ?? null; + if (errorMessage) { + return { + success: false, + message: errorMessage, + }; + } + throw newSentenceStructureData.error; +} + +export function sentenceStructureDataToString( + sentenceStructureData: SentenceStructureData, +): string { + return stringToSentenceStructureData.encode(sentenceStructureData); +} + +export function sentenceStructureDataToXMLString( + sentenceStructureData: SentenceStructureData, +): string { + return xmlStringToSentenceStructureData.encode(sentenceStructureData); +} + +export function createSentenceElementRange( + sentenceStructureData: SentenceStructureData, + input: { + type: SentenceElementRangeType; + startWordIndex: number; + endWordIndex: number; + }, +): Result<{ + newSentenceStructureData: SentenceStructureData; + rangeId: string; +}> { + const rangeId = crypto.randomUUID(); + const newSentenceStructureData = SentenceStructureDataSchema.safeParse({ + ...sentenceStructureData, + ranges: [ + ...sentenceStructureData.ranges, + { + kind: "sentence-element", + type: input.type, + id: rangeId, + startWordIndex: input.startWordIndex, + endWordIndex: input.endWordIndex, + sentenceElementName: null, + }, + ], + } satisfies SentenceStructureData); + + if (newSentenceStructureData.success) { + return { + success: true, + data: { + newSentenceStructureData: newSentenceStructureData.data, + rangeId, + }, + }; + } + const errorMessage = + newSentenceStructureData.error.issues.find( + (issue) => issue.code === "custom", + )?.message ?? null; + if (errorMessage) { + return { + success: false, + message: errorMessage, + }; + } + throw newSentenceStructureData.error; +} + +export function createSentenceStructureRange( + sentenceStructureData: SentenceStructureData, + input: { + type: SentenceStructureRangeType; + startWordIndex: number; + endWordIndex: number; + }, +): Result<{ + newSentenceStructureData: SentenceStructureData; + rangeId: string; +}> { + const rangeId = crypto.randomUUID(); + const newSentenceStructureData = SentenceStructureDataSchema.safeParse({ + ...sentenceStructureData, + ranges: [ + ...sentenceStructureData.ranges, + { + kind: "sentence-structure", + type: input.type, + id: rangeId, + startWordIndex: input.startWordIndex, + endWordIndex: input.endWordIndex, + sentenceElementName: null, + }, + ], + } satisfies SentenceStructureData); + + if (newSentenceStructureData.success) { + return { + success: true, + data: { + newSentenceStructureData: newSentenceStructureData.data, + rangeId, + }, + }; + } + const errorMessage = + newSentenceStructureData.error.issues.find( + (issue) => issue.code === "custom", + )?.message ?? null; + if (errorMessage) { + return { + success: false, + message: errorMessage, + }; + } + throw newSentenceStructureData.error; +} + +export function _createRelationRange( + sentenceStructureData: SentenceStructureData, + input: { + startWordIndex: number; + endWordIndex: number; + }, +): Result<{ + newSentenceStructureData: SentenceStructureData; + rangeId: string; +}> { + const rangeId = crypto.randomUUID(); + const newSentenceStructureData = SentenceStructureDataSchema.safeParse({ + ...sentenceStructureData, + ranges: [ + ...sentenceStructureData.ranges, + { + kind: "relation", + type: "relation", + id: rangeId, + startWordIndex: input.startWordIndex, + endWordIndex: input.endWordIndex, + }, + ], + } satisfies SentenceStructureData); + + if (newSentenceStructureData.success) { + return { + success: true, + data: { + newSentenceStructureData: newSentenceStructureData.data, + rangeId, + }, + }; + } + const errorMessage = + newSentenceStructureData.error.issues.find( + (issue) => issue.code === "custom", + )?.message ?? null; + if (errorMessage) { + return { + success: false, + message: errorMessage, + }; + } + throw newSentenceStructureData.error; +} + +export function findRangeById( + sentenceStructureData: SentenceStructureData, + input: { rangeId: string }, +): Range | null { + return ( + sentenceStructureData.ranges.find((range) => range.id === input.rangeId) ?? + null + ); +} + +export function findRangeByStartAndEndWordIndex( + sentenceStructureData: SentenceStructureData, + input: { startWordIndex: number; endWordIndex: number }, +): Range | null { + return ( + sentenceStructureData.ranges.find( + (range) => + range.startWordIndex === input.startWordIndex && + range.endWordIndex === input.endWordIndex, + ) ?? null + ); +} + +export function updateSentenceElementName( + sentenceStructureData: SentenceStructureData, + input: { + rangeId: string; + sentenceElementName: RangeType extends SentenceElementRangeType + ? (typeof sentenceElementRangeTypeToAllowedSentenceElementNameOptionsMap)[RangeType][number] + : RangeType extends SentenceStructureRangeType + ? (typeof sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap)[RangeType][number] + : never; + }, +): SentenceStructureData { + return SentenceStructureDataSchema.parse({ + ...sentenceStructureData, + ranges: sentenceStructureData.ranges.map((range) => + range.id === input.rangeId + ? ({ + ...range, + sentenceElementName: input.sentenceElementName, + } as Range) + : range, + ), + } satisfies SentenceStructureData); +} + +export function deleteRange( + sentenceStructureData: SentenceStructureData, + input: { rangeId: string }, +): SentenceStructureData { + return SentenceStructureDataSchema.parse({ + ...sentenceStructureData, + ranges: sentenceStructureData.ranges.filter( + (range) => range.id !== input.rangeId, + ), + relations: sentenceStructureData.relations.filter( + (relation) => + relation.fromRangeId !== input.rangeId && + relation.toRangeId !== input.rangeId, + ), + } satisfies SentenceStructureData); +} + +export function createRelation( + sentenceStructureData: SentenceStructureData, + input: { + fromRange: { startWordIndex: number; endWordIndex: number }; + toRange: { startWordIndex: number; endWordIndex: number }; + }, +): Result<{ newSentenceStructureData: SentenceStructureData }> { + const fromRangeResult: Result<{ + newSentenceStructureData: SentenceStructureData; + rangeId: string; + }> = (() => { + const existingFromRange = findRangeByStartAndEndWordIndex( + sentenceStructureData, + { + startWordIndex: input.fromRange.startWordIndex, + endWordIndex: input.fromRange.endWordIndex, + }, + ); + if (existingFromRange) { + return { + success: true, + data: { + newSentenceStructureData: sentenceStructureData, + rangeId: existingFromRange.id, + }, + }; + } + return _createRelationRange(sentenceStructureData, { + startWordIndex: input.fromRange.startWordIndex, + endWordIndex: input.fromRange.endWordIndex, + }); + })(); + if (!fromRangeResult.success) { + return { + success: false, + message: fromRangeResult.message, + }; + } + + const toRangeResult: Result<{ + newSentenceStructureData: SentenceStructureData; + rangeId: string; + }> = (() => { + const existingToRange = findRangeByStartAndEndWordIndex( + fromRangeResult.data.newSentenceStructureData, + { + startWordIndex: input.toRange.startWordIndex, + endWordIndex: input.toRange.endWordIndex, + }, + ); + if (existingToRange) { + return { + success: true, + data: { + newSentenceStructureData: + fromRangeResult.data.newSentenceStructureData, + rangeId: existingToRange.id, + }, + }; + } + return _createRelationRange(fromRangeResult.data.newSentenceStructureData, { + startWordIndex: input.toRange.startWordIndex, + endWordIndex: input.toRange.endWordIndex, + }); + })(); + if (!toRangeResult.success) { + return { + success: false, + message: toRangeResult.message, + }; + } + + return { + success: true, + data: { + newSentenceStructureData: SentenceStructureDataSchema.parse({ + ...toRangeResult.data.newSentenceStructureData, + relations: [ + ...toRangeResult.data.newSentenceStructureData.relations, + { + id: crypto.randomUUID(), + fromRangeId: fromRangeResult.data.rangeId, + toRangeId: toRangeResult.data.rangeId, + }, + ], + } satisfies SentenceStructureData), + }, + }; +} + +export function findRelationById( + sentenceStructureData: SentenceStructureData, + input: { relationId: string }, +): Relation | null { + return ( + sentenceStructureData.relations.find( + (relation) => relation.id === input.relationId, + ) ?? null + ); +} + +export function deleteRelation( + sentenceStructureData: SentenceStructureData, + input: { relationId: string }, +): SentenceStructureData { + return SentenceStructureDataSchema.parse({ + ...sentenceStructureData, + relations: sentenceStructureData.relations.filter( + (relation) => relation.id !== input.relationId, + ), + } satisfies SentenceStructureData); +} + +export function createCoordination( + sentenceStructureData: SentenceStructureData, + input: { + children: { + type: CoordinationChildType; + startWordIndex: number; + endWordIndex: number; + }[]; + }, +): Result<{ + newSentenceStructureData: SentenceStructureData; +}> { + const newSentenceStructureData = SentenceStructureDataSchema.safeParse({ + ...sentenceStructureData, + coordinations: [ + ...sentenceStructureData.coordinations, + { + id: crypto.randomUUID(), + children: input.children + .sort((a, b) => a.startWordIndex - b.startWordIndex) + .map((child, index) => ({ + type: child.type, + index, + startWordIndex: child.startWordIndex, + endWordIndex: child.endWordIndex, + })), + }, + ], + } satisfies SentenceStructureData); + if (newSentenceStructureData.success) { + return { + success: true, + data: { + newSentenceStructureData: newSentenceStructureData.data, + }, + }; + } + const errorMessage = + newSentenceStructureData.error.issues.find( + (issue) => issue.code === "custom", + )?.message ?? null; + if (errorMessage) { + return { + success: false, + message: errorMessage, + }; + } + throw newSentenceStructureData.error; +} + +export function findCoordinationById( + sentenceStructureData: SentenceStructureData, + input: { coordinationId: string }, +): Coordination | null { + return ( + sentenceStructureData.coordinations.find( + (coordination) => coordination.id === input.coordinationId, + ) ?? null + ); +} + +export function findCoordinationByStartAndEndWordIndex( + sentenceStructureData: SentenceStructureData, + input: { startWordIndex: number; endWordIndex: number }, +): Coordination | null { + return ( + sentenceStructureData.coordinations.find( + (coordination) => + coordination.children.at(0)!.startWordIndex === input.startWordIndex && + coordination.children.at(-1)!.endWordIndex === input.endWordIndex, + ) ?? null + ); +} + +export function deleteCoordination( + sentenceStructureData: SentenceStructureData, + input: { coordinationId: string }, +): SentenceStructureData { + return SentenceStructureDataSchema.parse({ + ...sentenceStructureData, + coordinations: sentenceStructureData.coordinations.filter( + (coordination) => coordination.id !== input.coordinationId, + ), + } satisfies SentenceStructureData); +} diff --git a/packages/sentence-structure-data/package.json b/packages/sentence-structure-data/package.json new file mode 100644 index 0000000..ecedf55 --- /dev/null +++ b/packages/sentence-structure-data/package.json @@ -0,0 +1,19 @@ +{ + "name": "@sentence-structure-diagram-app/sentence-structure-data", + "version": "0.1.0", + "type": "module", + "main": "./dist/index.js", + "scripts": { + "build": "tsc", + "build:watch": "tsc --watch", + "clean": "rm -r dist" + }, + "dependencies": { + "compromise": "^14.14.4", + "fast-xml-parser": "^5.3.2", + "zod": "^4.1.13" + }, + "devDependencies": { + "typescript": "^5.9.3" + } +} diff --git a/packages/sentence-structure-data/schema.ts b/packages/sentence-structure-data/schema.ts new file mode 100644 index 0000000..3210325 --- /dev/null +++ b/packages/sentence-structure-data/schema.ts @@ -0,0 +1,353 @@ +import * as z from "zod"; + +const WordSchema = z.object({ + index: z.int().nonnegative(), + text: z.string(), +}); +export type Word = z.infer; + +export const sentenceElementNameOptions = ["S", "V", "O", "C", "M"] as const; +export type SentenceElementName = (typeof sentenceElementNameOptions)[number]; + +export const sentenceElementRangeTypeOptions = [ + "core-sentence-element", +] as const; +export type SentenceElementRangeType = + (typeof sentenceElementRangeTypeOptions)[number]; +export const sentenceElementRangeTypeToAllowedSentenceElementNameOptionsMap = { + "core-sentence-element": ["S", "V", "O", "C"] as const, +} satisfies Record; + +export const sentenceStructureRangeTypeOptions = [ + "modifier", + "phrase", + "clause", +] as const; +export type SentenceStructureRangeType = + (typeof sentenceStructureRangeTypeOptions)[number]; +export const sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap = + { + modifier: ["M"] as const, + phrase: ["S", "O", "C", "M"] as const, + clause: ["S", "O", "C", "M"] as const, + } satisfies Record; + +const relationRangeTypeOption = "relation" as const; +export type RelationRangeType = typeof relationRangeTypeOption; + +export type RangeType = + | SentenceElementRangeType + | SentenceStructureRangeType + | RelationRangeType; + +const RangeSchema = z.union([ + ...sentenceElementRangeTypeOptions.map( + (sentenceElementRangeTypeOption) => + ({ + "core-sentence-element": z.object({ + kind: z.literal("sentence-element"), + type: z.literal("core-sentence-element"), + id: z.uuid(), + startWordIndex: z.int().nonnegative(), + endWordIndex: z.int().nonnegative(), + sentenceElementName: z.nullable( + z.literal( + sentenceElementRangeTypeToAllowedSentenceElementNameOptionsMap[ + "core-sentence-element" + ], + ), + ), + }), + })[sentenceElementRangeTypeOption], + ), + ...sentenceStructureRangeTypeOptions.map( + (sentenceStructureRangeTypeOption) => + ({ + modifier: z.object({ + kind: z.literal("sentence-structure"), + type: z.literal("modifier"), + id: z.uuid(), + startWordIndex: z.int().nonnegative(), + endWordIndex: z.int().nonnegative(), + sentenceElementName: z.nullable( + z.literal( + sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap[ + "modifier" + ], + ), + ), + }), + phrase: z.object({ + kind: z.literal("sentence-structure"), + type: z.literal("phrase"), + id: z.uuid(), + startWordIndex: z.int().nonnegative(), + endWordIndex: z.int().nonnegative(), + sentenceElementName: z.nullable( + z.literal( + sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap[ + "phrase" + ], + ), + ), + }), + clause: z.object({ + kind: z.literal("sentence-structure"), + type: z.literal("clause"), + id: z.uuid(), + startWordIndex: z.int().nonnegative(), + endWordIndex: z.int().nonnegative(), + sentenceElementName: z.nullable( + z.literal( + sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap[ + "clause" + ], + ), + ), + }), + })[sentenceStructureRangeTypeOption], + ), + z.object({ + kind: z.literal(relationRangeTypeOption), + type: z.literal("relation"), + id: z.uuid(), + startWordIndex: z.int().nonnegative(), + endWordIndex: z.int().nonnegative(), + }), +]); +export type Range = z.infer; + +const RelationSchema = z.object({ + id: z.uuid(), + fromRangeId: z.uuid(), + toRangeId: z.uuid(), +}); +export type Relation = z.infer; + +export const coordinationChildTypeOptions = [ + "coordinating conjunction", + "correlative conjunction", + "conjunct", +] as const; +export type CoordinationChildType = + (typeof coordinationChildTypeOptions)[number]; + +const CoordinationChildSchema = z.object({ + type: z.literal(coordinationChildTypeOptions), + index: z.int().nonnegative(), + startWordIndex: z.int().nonnegative(), + endWordIndex: z.int().nonnegative(), +}); +export type CoordinationChild = z.infer; + +const CoordinationSchema = z.object({ + id: z.uuid(), + children: z.array(CoordinationChildSchema), +}); +export type Coordination = z.infer; + +export const SentenceStructureDataSchema = z + .object({ + text: z.string(), + words: z.array(WordSchema), + ranges: z.array(RangeSchema), + relations: z.array(RelationSchema), + coordinations: z.array(CoordinationSchema), + }) + .refine( + (sentenceStructureData) => + sentenceStructureData.words.every((word, index) => word.index === index), + { error: "単語のインデックスが0から始まる連続した整数ではありません。" }, + ) + .refine( + (sentenceStructureData) => + sentenceStructureData.ranges.every( + (range) => + range.startWordIndex <= range.endWordIndex && + range.endWordIndex < sentenceStructureData.words.length, + ), + { error: "範囲を示す単語のインデックスが不正な値です。" }, + ) + .refine( + (sentenceStructureData) => + sentenceStructureData.ranges.every((range) => + sentenceStructureData.ranges.every( + (otherRange) => + !( + range.id !== otherRange.id && + range.startWordIndex === otherRange.startWordIndex && + range.endWordIndex === otherRange.endWordIndex + ), + ), + ), + { + error: "開始位置と終了位置が同じ範囲が複数存在します。", + }, + ) + .refine( + (sentenceStructureData) => + sentenceStructureData.ranges.every((range) => + sentenceStructureData.ranges.every( + (otherRange) => + range.id === otherRange.id || + otherRange.endWordIndex < range.startWordIndex || + (range.startWordIndex <= otherRange.startWordIndex && + otherRange.endWordIndex <= range.endWordIndex) || + (otherRange.startWordIndex <= range.startWordIndex && + range.endWordIndex <= otherRange.endWordIndex) || + range.endWordIndex < otherRange.startWordIndex, + ), + ), + { error: "範囲が部分的に重なっています。" }, + ) + .refine( + (sentenceStructureData) => + sentenceStructureData.ranges + .filter((range) => range.kind === "relation") + .every((relationRange) => + sentenceStructureData.relations.some( + (relation) => + relation.fromRangeId === relationRange.id || + relation.toRangeId === relationRange.id, + ), + ), + { error: "範囲(関係)が関係の始点または終点として使われていません。" }, + ) + .refine( + (sentenceStructureData) => + sentenceStructureData.relations.every( + (relation) => + sentenceStructureData.ranges.some( + (range) => range.id === relation.fromRangeId, + ) && + sentenceStructureData.ranges.some( + (range) => range.id === relation.toRangeId, + ), + ), + { error: "関係が存在しない範囲を参照しています。" }, + ) + .refine( + (sentenceStructureData) => + sentenceStructureData.relations.every((relation) => + sentenceStructureData.relations.every( + (otherRelation) => + !( + relation.id !== otherRelation.id && + relation.fromRangeId === otherRelation.fromRangeId && + relation.toRangeId === otherRelation.toRangeId + ), + ), + ), + { + error: "同じ始点と終点を持つ関係が複数存在します。", + }, + ) + .refine( + (sentenceStructureData) => + sentenceStructureData.coordinations.every((coordination) => + coordination.children.every((child, index) => child.index === index), + ), + { + error: + "並列構造の子要素のインデックスが0から始まる連続した整数ではありません。", + }, + ) + .refine( + (sentenceStructureData) => + sentenceStructureData.coordinations.every( + (coordination) => 3 <= coordination.children.length, + ), + { error: "並列構造の子要素は3つ以上必要です。" }, + ) + .refine( + (sentenceStructureData) => + sentenceStructureData.coordinations.every((coordination) => + coordination.children.every( + (child) => + child.startWordIndex <= child.endWordIndex && + child.endWordIndex < sentenceStructureData.words.length, + ), + ), + { error: "並列構造の子要素の単語のインデックスが不正な値です。" }, + ) + .refine( + (sentenceStructureData) => + sentenceStructureData.coordinations.every((coordination) => { + for (let i = 1; i < coordination.children.length; i++) { + if ( + coordination.children[i - 1]!.endWordIndex + 1 !== + coordination.children[i]!.startWordIndex + ) { + return false; + } + } + return true; + }), + { error: "並列構造の子要素が連続していません。" }, + ) + .refine( + (sentenceStructureData) => + sentenceStructureData.coordinations.every((coordination) => + coordination.children.every((child) => + sentenceStructureData.ranges.every( + (range) => + range.endWordIndex < child.startWordIndex || + (child.startWordIndex <= range.startWordIndex && + range.endWordIndex <= child.endWordIndex) || + (range.startWordIndex <= child.startWordIndex && + child.endWordIndex <= range.endWordIndex) || + child.endWordIndex < range.startWordIndex, + ), + ), + ), + { error: "並列構造の子要素が範囲と部分的に重なっています。" }, + ) + .refine( + (sentenceStructureData) => + sentenceStructureData.coordinations.every((coordination) => + sentenceStructureData.coordinations.every((otherCoordination) => { + if (coordination.id === otherCoordination.id) return true; + const coordinationStart = coordination.children.at(0)!.startWordIndex; + const coordinationEnd = coordination.children.at(-1)!.endWordIndex; + const otherCoordinationStart = + otherCoordination.children.at(0)!.startWordIndex; + const otherCoordinationEnd = + otherCoordination.children.at(-1)!.endWordIndex; + return !( + coordinationStart === otherCoordinationStart && + coordinationEnd === otherCoordinationEnd + ); + }), + ), + { error: "開始位置と終了位置が同じ並列構造が複数存在します。" }, + ) + .refine( + (sentenceStructureData) => + sentenceStructureData.coordinations.every((coordination) => + sentenceStructureData.coordinations.every((otherCoordination) => { + if (coordination.id === otherCoordination.id) return true; + const coordinationStart = coordination.children.at(0)!.startWordIndex; + const coordinationEnd = coordination.children.at(-1)!.endWordIndex; + const otherCoordinationStart = + otherCoordination.children.at(0)!.startWordIndex; + const otherCoordinationEnd = + otherCoordination.children.at(-1)!.endWordIndex; + if ( + coordinationEnd - coordinationStart < + otherCoordinationEnd - otherCoordinationStart + ) + return true; + return ( + otherCoordinationEnd < coordinationStart || + coordination.children.some( + (child) => + child.startWordIndex <= otherCoordinationStart && + otherCoordinationEnd <= child.endWordIndex, + ) || + coordinationEnd < otherCoordinationStart + ); + }), + ), + { error: "並列構造が部分的に重なっています。" }, + ); +export type SentenceStructureData = z.infer; diff --git a/packages/sentence-structure-data/simplified-schema.ts b/packages/sentence-structure-data/simplified-schema.ts new file mode 100644 index 0000000..f7b3156 --- /dev/null +++ b/packages/sentence-structure-data/simplified-schema.ts @@ -0,0 +1,219 @@ +import * as z from "zod"; +import { + sentenceElementRangeTypeOptions, + sentenceElementRangeTypeToAllowedSentenceElementNameOptionsMap, + sentenceStructureRangeTypeOptions, + sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap, +} from "./schema.js"; + +export const SimplifiedAnnotationDataSchema = z.object({ + ranges: z.array( + z + .union([ + ...sentenceElementRangeTypeOptions.map( + (sentenceElementRangeTypeOption) => + ({ + "core-sentence-element": z + .object({ + type: z.literal("文の主要素").describe("範囲の種類"), + index: z + .int() + .nonnegative() + .describe("範囲を識別するためのインデックス"), + startWordIndex: z + .int() + .nonnegative() + .describe("範囲に含まれる先頭の単語のインデックス"), + endWordIndex: z + .int() + .nonnegative() + .describe("範囲に含まれる末尾の単語のインデックス"), + sentenceElementName: z + .nullable( + z.literal( + sentenceElementRangeTypeToAllowedSentenceElementNameOptionsMap[ + "core-sentence-element" + ], + ), + ) + .describe("文の要素の名前"), + }) + .describe("文の主要素の範囲"), + })[sentenceElementRangeTypeOption], + ), + ...sentenceStructureRangeTypeOptions.map( + (sentenceStructureRangeTypeOption) => + ({ + modifier: z + .object({ + type: z.literal("修飾語").describe("範囲の種類"), + index: z + .int() + .nonnegative() + .describe("範囲を識別するためのインデックス"), + startWordIndex: z + .int() + .nonnegative() + .describe("範囲に含まれる先頭の単語のインデックス"), + endWordIndex: z + .int() + .nonnegative() + .describe("範囲に含まれる末尾の単語のインデックス"), + sentenceElementName: z + .nullable( + z.literal( + sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap[ + "modifier" + ], + ), + ) + .describe("文の要素の名前"), + }) + .describe("修飾語の範囲"), + phrase: z + .object({ + type: z.literal("句").describe("範囲の種類"), + index: z + .int() + .nonnegative() + .describe("範囲を識別するためのインデックス"), + startWordIndex: z + .int() + .nonnegative() + .describe("範囲に含まれる先頭の単語のインデックス"), + endWordIndex: z + .int() + .nonnegative() + .describe("範囲に含まれる末尾の単語のインデックス"), + sentenceElementName: z + .nullable( + z.literal( + sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap[ + "phrase" + ], + ), + ) + .describe("文の要素の名前"), + }) + .describe("句の範囲"), + clause: z + .object({ + type: z.literal("節").describe("範囲の種類"), + index: z + .int() + .nonnegative() + .describe("範囲を識別するためのインデックス"), + startWordIndex: z + .int() + .nonnegative() + .describe("範囲に含まれる先頭の単語のインデックス"), + endWordIndex: z + .int() + .nonnegative() + .describe("範囲に含まれる末尾の単語のインデックス"), + sentenceElementName: z + .nullable( + z.literal( + sentenceStructureRangeTypeToAllowedSentenceElementNameOptionsMap[ + "clause" + ], + ), + ) + .describe("文の要素の名前"), + }) + .describe("節の範囲"), + })[sentenceStructureRangeTypeOption], + ), + z + .object({ + type: z.literal("関係").describe("範囲の種類"), + index: z + .int() + .nonnegative() + .describe("範囲を識別するためのインデックス"), + startWordIndex: z + .int() + .nonnegative() + .describe("範囲に含まれる先頭の単語のインデックス"), + endWordIndex: z + .int() + .nonnegative() + .describe("範囲に含まれる末尾の単語のインデックス"), + }) + .describe("関係の範囲"), + ]) + .describe("範囲"), + ), + relations: z.array( + z + .object({ + fromRangeIndex: z + .int() + .nonnegative() + .describe("関係の始点となる範囲のインデックス"), + toRangeIndex: z + .int() + .nonnegative() + .describe("関係の終点となる範囲のインデックス"), + }) + .describe("関係"), + ), + coordinations: z.array( + z + .object({ + children: z.array( + z + .object({ + type: z + .literal(["等位接続詞", "相関接続詞", "並列要素"]) + .describe("子要素の種類"), + startWordIndex: z + .int() + .nonnegative() + .describe("子要素に含まれる先頭の単語のインデックス"), + endWordIndex: z + .int() + .nonnegative() + .describe("子要素に含まれる末尾の単語のインデックス"), + }) + .describe("子要素"), + ), + }) + .describe("並列構造"), + ), +}); +export type SimplifiedAnnotationData = z.infer< + typeof SimplifiedAnnotationDataSchema +>; + +export const SimplifiedSentenceStructureDataSchema = + SimplifiedAnnotationDataSchema.extend({ + text: z.string(), + words: z.array( + z.object({ + index: z.int().nonnegative(), + text: z.string(), + }), + ), + }) + .refine( + (simplifiedSentenceStructureData) => + simplifiedSentenceStructureData.ranges + .sort((a, b) => a.index - b.index) + .every((range, index) => range.index === index), + { error: "範囲のインデックスが0から始まる連続した整数ではありません。" }, + ) + .refine( + (simplifiedSentenceStructureData) => + simplifiedSentenceStructureData.relations.every( + (relation) => + relation.fromRangeIndex < + simplifiedSentenceStructureData.ranges.length && + relation.toRangeIndex < + simplifiedSentenceStructureData.ranges.length, + ), + { error: "関係が存在しない範囲を参照しています。" }, + ); +export type SimplifiedSentenceStructureData = z.infer< + typeof SimplifiedSentenceStructureDataSchema +>; diff --git a/packages/sentence-structure-data/tokenize-text.ts b/packages/sentence-structure-data/tokenize-text.ts new file mode 100644 index 0000000..93725dc --- /dev/null +++ b/packages/sentence-structure-data/tokenize-text.ts @@ -0,0 +1,25 @@ +// import nlp from "compromise"; +import type { Word } from "./schema.js"; + +const segmenter = new Intl.Segmenter("en", { granularity: "word" }); +export function tokenizeText(text: string): Word[] { + if (text.trim() === "") return []; + + return [...segmenter.segment(text)] + .filter((segment) => segment.segment.trim() !== "") + .map((segment, index) => ({ + index: index, + text: segment.segment, + })); +} + +// export function tokenizeText(text: string): Word[] { +// const tokenizedText: string[] = nlp(text) +// .terms() +// .json() +// .map((term: { text: string }) => term.text); +// return tokenizedText.map((text, index) => ({ +// index: index, +// text: text, +// })); +// } diff --git a/packages/sentence-structure-data/tsconfig.json b/packages/sentence-structure-data/tsconfig.json new file mode 100644 index 0000000..d6a2b39 --- /dev/null +++ b/packages/sentence-structure-data/tsconfig.json @@ -0,0 +1,44 @@ +{ + // Visit https://aka.ms/tsconfig to read more about this file + "compilerOptions": { + // File Layout + // "rootDir": "./src", + "outDir": "./dist", + + // Environment Settings + // See also https://aka.ms/tsconfig/module + "module": "nodenext", + "target": "esnext", + "types": [], + // For nodejs: + // "lib": ["esnext"], + // "types": ["node"], + // and npm install -D @types/node + + // Other Outputs + "sourceMap": true, + "declaration": true, + "declarationMap": true, + + // Stricter Typechecking Options + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + + // Style Options + // "noImplicitReturns": true, + // "noImplicitOverride": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + // "noFallthroughCasesInSwitch": true, + // "noPropertyAccessFromIndexSignature": true, + + // Recommended Options + "strict": true, + "jsx": "react-jsx", + "verbatimModuleSyntax": true, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "skipLibCheck": true + } +} diff --git a/packages/sentence-structure-diagram-configurations/.gitignore b/packages/sentence-structure-diagram-configurations/.gitignore new file mode 100644 index 0000000..9b1c8b1 --- /dev/null +++ b/packages/sentence-structure-diagram-configurations/.gitignore @@ -0,0 +1 @@ +/dist diff --git a/packages/sentence-structure-diagram-configurations/default-configurations.ts b/packages/sentence-structure-diagram-configurations/default-configurations.ts new file mode 100644 index 0000000..ffef31a --- /dev/null +++ b/packages/sentence-structure-diagram-configurations/default-configurations.ts @@ -0,0 +1,26 @@ +import type { Configurations } from "./schema.js"; + +export const defaultConfigurations: Configurations = { + color: { + primaryColor: "#1976d2", + textColor: "#000000", + }, + sentenceStructureRangeTypeToBracketNameMap: { + modifier: "(parenthesis)", + phrase: "", + clause: "[square-bracket]", + }, + sentenceElementNameToSentenceElementSymbolMap: { + S: "S", + V: "V", + C: "C", + O: "O", + M: "M", + }, + sentenceElementPositionType: { + sentenceElementRangeSentenceElementPositionType: "bottom-center", + sentenceStructureRangeSentenceElementPositionType: "bottom-left", + }, + relationShapeType: "curved", + layoutMode: "structured", +}; diff --git a/packages/sentence-structure-diagram-configurations/format.ts b/packages/sentence-structure-diagram-configurations/format.ts new file mode 100644 index 0000000..b3877b8 --- /dev/null +++ b/packages/sentence-structure-diagram-configurations/format.ts @@ -0,0 +1,22 @@ +import * as z from "zod"; +import { XMLBuilder, XMLParser } from "fast-xml-parser"; +import { ConfigurationsSchema } from "./schema.js"; + +export const stringToConfigurations = z.codec( + z.string(), + ConfigurationsSchema, + { + decode: (string) => JSON.parse(string), + encode: (configurations) => JSON.stringify(configurations, null, 2), + }, +); + +export const xmlStringToConfigurations = z.codec( + z.string(), + ConfigurationsSchema, + { + decode: (xml) => new XMLParser().parse(xml), + encode: (configurations) => + new XMLBuilder({ format: true }).build(configurations), + }, +); diff --git a/packages/sentence-structure-diagram-configurations/index.ts b/packages/sentence-structure-diagram-configurations/index.ts new file mode 100644 index 0000000..6459ab8 --- /dev/null +++ b/packages/sentence-structure-diagram-configurations/index.ts @@ -0,0 +1,9 @@ +export type { + BracketName, + SentenceElementPositionType, + RelationShapeType, + LayoutMode, + Configurations, +} from "./schema.js"; +export { defaultConfigurations } from "./default-configurations.js"; +export { stringToConfigurations, xmlStringToConfigurations } from "./format.js"; diff --git a/packages/sentence-structure-diagram-configurations/package.json b/packages/sentence-structure-diagram-configurations/package.json new file mode 100644 index 0000000..188cf55 --- /dev/null +++ b/packages/sentence-structure-diagram-configurations/package.json @@ -0,0 +1,19 @@ +{ + "name": "@sentence-structure-diagram-app/sentence-structure-diagram-configurations", + "version": "0.1.0", + "type": "module", + "main": "./dist/index.js", + "scripts": { + "build": "tsc", + "build:watch": "tsc --watch", + "clean": "rm -r dist" + }, + "dependencies": { + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", + "fast-xml-parser": "^5.3.3", + "zod": "^4.2.1" + }, + "devDependencies": { + "typescript": "^5.9.3" + } +} diff --git a/packages/sentence-structure-diagram-configurations/schema.ts b/packages/sentence-structure-diagram-configurations/schema.ts new file mode 100644 index 0000000..5de0bca --- /dev/null +++ b/packages/sentence-structure-diagram-configurations/schema.ts @@ -0,0 +1,51 @@ +import * as z from "zod"; +import { + sentenceElementNameOptions, + sentenceStructureRangeTypeOptions, +} from "@sentence-structure-diagram-app/sentence-structure-data"; + +const BracketNameSchema = z.literal([ + "(parenthesis)", + "", + "{curly-bracket}", + "[square-bracket]", +]); +export type BracketName = z.infer; + +const SentenceElementPositionTypeSchema = z.literal([ + "bottom-center", + "bottom-left", +]); +export type SentenceElementPositionType = z.infer< + typeof SentenceElementPositionTypeSchema +>; + +const RelationShapeTypeSchema = z.literal(["curved", "right-angle"]); +export type RelationShapeType = z.infer; + +const LayoutModeSchema = z.literal(["linear", "structured"]); +export type LayoutMode = z.infer; + +export const ConfigurationsSchema = z.object({ + color: z.object({ + primaryColor: z.string(), + textColor: z.string(), + }), + sentenceStructureRangeTypeToBracketNameMap: z.record( + z.literal(sentenceStructureRangeTypeOptions), + BracketNameSchema, + ), + sentenceElementNameToSentenceElementSymbolMap: z.record( + z.literal(sentenceElementNameOptions), + z.string(), + ), + sentenceElementPositionType: z.object({ + sentenceElementRangeSentenceElementPositionType: + SentenceElementPositionTypeSchema, + sentenceStructureRangeSentenceElementPositionType: + SentenceElementPositionTypeSchema, + }), + relationShapeType: RelationShapeTypeSchema, + layoutMode: LayoutModeSchema, +}); +export type Configurations = z.infer; diff --git a/packages/sentence-structure-diagram-configurations/tsconfig.json b/packages/sentence-structure-diagram-configurations/tsconfig.json new file mode 100644 index 0000000..d6a2b39 --- /dev/null +++ b/packages/sentence-structure-diagram-configurations/tsconfig.json @@ -0,0 +1,44 @@ +{ + // Visit https://aka.ms/tsconfig to read more about this file + "compilerOptions": { + // File Layout + // "rootDir": "./src", + "outDir": "./dist", + + // Environment Settings + // See also https://aka.ms/tsconfig/module + "module": "nodenext", + "target": "esnext", + "types": [], + // For nodejs: + // "lib": ["esnext"], + // "types": ["node"], + // and npm install -D @types/node + + // Other Outputs + "sourceMap": true, + "declaration": true, + "declarationMap": true, + + // Stricter Typechecking Options + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + + // Style Options + // "noImplicitReturns": true, + // "noImplicitOverride": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + // "noFallthroughCasesInSwitch": true, + // "noPropertyAccessFromIndexSignature": true, + + // Recommended Options + "strict": true, + "jsx": "react-jsx", + "verbatimModuleSyntax": true, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "skipLibCheck": true + } +} diff --git a/packages/sentence-structure-diagram-data/.gitignore b/packages/sentence-structure-diagram-data/.gitignore new file mode 100644 index 0000000..9b1c8b1 --- /dev/null +++ b/packages/sentence-structure-diagram-data/.gitignore @@ -0,0 +1 @@ +/dist diff --git a/packages/sentence-structure-diagram-data/extract-position.ts b/packages/sentence-structure-diagram-data/extract-position.ts new file mode 100644 index 0000000..abf1e79 --- /dev/null +++ b/packages/sentence-structure-diagram-data/extract-position.ts @@ -0,0 +1,74 @@ +import type { + SentenceStructureDiagramNode, + SentenceStructureDiagramTree, +} from "@sentence-structure-diagram-app/sentence-structure-diagram-tree"; + +type WordPosition = { + wordIndex: number; + start: number; + end: number; + top: number; + bottom: number; +}; + +export function extractWordPositions( + treePosition: SentenceStructureDiagramTree, +): WordPosition[] { + const wordPositions: WordPosition[] = []; + + function traverse(node: SentenceStructureDiagramNode): void { + if (!("children" in node)) { + wordPositions.push({ + wordIndex: node.word.index, + start: node.position.start, + end: node.position.end, + top: node.position.top, + bottom: node.position.bottom, + }); + return; + } + for (const child of node.children) { + traverse(child); + } + } + + traverse(treePosition); + + return wordPositions.sort((a, b) => a.wordIndex - b.wordIndex); +} + +type SpanPosition = { + start: number; + end: number; + top: number; + bottom: number; +}[]; + +export function extractSpanPosition( + wordPositions: WordPosition[], + startWordIndex: number, + endWordIndex: number, +): SpanPosition { + const wordPositionsInRange = wordPositions.filter( + (wordPosition) => + startWordIndex <= wordPosition.wordIndex && + wordPosition.wordIndex <= endWordIndex, + ); + + const rangePosition: SpanPosition = []; + + for (const wordPosition of wordPositionsInRange) { + if (wordPosition.bottom === rangePosition.at(-1)?.bottom) { + rangePosition.at(-1)!.end = wordPosition.end; + } else { + rangePosition.push({ + start: wordPosition.start, + end: wordPosition.end, + top: wordPosition.top, + bottom: wordPosition.bottom, + }); + } + } + + return rangePosition; +} diff --git a/packages/sentence-structure-diagram-data/generate.ts b/packages/sentence-structure-diagram-data/generate.ts new file mode 100644 index 0000000..257ffd1 --- /dev/null +++ b/packages/sentence-structure-diagram-data/generate.ts @@ -0,0 +1,172 @@ +import { + findRangeById, + type SentenceStructureData, +} from "@sentence-structure-diagram-app/sentence-structure-data"; +import type { Configurations } from "@sentence-structure-diagram-app/sentence-structure-diagram-configurations"; +import { createTree } from "@sentence-structure-diagram-app/sentence-structure-tree"; +import { createSentenceStructureDiagramTree } from "@sentence-structure-diagram-app/sentence-structure-diagram-tree"; +import type { SentenceStructureDiagramData } from "./types.js"; +import { + extractSpanPosition, + extractWordPositions, +} from "./extract-position.js"; +import { resolveConfigurations } from "./resolve-configurations.js"; + +export function convertSentenceStructureDataToSentenceStructureDiagramData( + sentenceStructureData: SentenceStructureData, + maxWidth: number, + measureTextWidth: (text: string) => number, + customConfigurations: Partial, +): SentenceStructureDiagramData { + const resolvedConfigurations = resolveConfigurations(customConfigurations); + + const tree = createTree(sentenceStructureData); + const treePosition = createSentenceStructureDiagramTree( + tree, + maxWidth, + measureTextWidth, + resolvedConfigurations.layoutMode, + ); + const wordPositions = extractWordPositions(treePosition); + + return { + position: { + left: treePosition.position.start, + right: treePosition.position.end, + top: treePosition.position.top, + bottom: treePosition.position.bottom, + }, + color: { + primaryColor: resolvedConfigurations.color.primaryColor, + textColor: resolvedConfigurations.color.textColor, + }, + words: sentenceStructureData.words.map((word) => ({ + index: word.index, + text: word.text, + openingBrackets: sentenceStructureData.ranges + .filter((range) => range.kind === "sentence-structure") + .filter((range) => range.startWordIndex === word.index) + .sort((a, b) => b.endWordIndex - a.endWordIndex) + .map( + (range) => + resolvedConfigurations.sentenceStructureRangeTypeToBracketsMap[ + range.type + ].openingBracket, + ), + closingBrackets: sentenceStructureData.ranges + .filter((range) => range.kind === "sentence-structure") + .filter((range) => range.endWordIndex === word.index) + .sort((a, b) => b.startWordIndex - a.startWordIndex) + .map( + (range) => + resolvedConfigurations.sentenceStructureRangeTypeToBracketsMap[ + range.type + ].closingBracket, + ), + position: { + left: wordPositions[word.index]!.start, + right: wordPositions[word.index]!.end, + top: wordPositions[word.index]!.top, + bottom: wordPositions[word.index]!.bottom, + }, + })), + underlines: sentenceStructureData.ranges + .filter((range) => range.type === "core-sentence-element") + .map((range) => { + const rangePosition = extractSpanPosition( + wordPositions, + range.startWordIndex, + range.endWordIndex, + ); + return { + rangeId: range.id, + position: rangePosition.map((position) => ({ + start: position.start, + end: position.end, + bottom: position.bottom + 4, + })), + }; + }), + sentenceElements: sentenceStructureData.ranges + .filter((range) => range.kind !== "relation") + .map((range) => { + const rangePosition = extractSpanPosition( + wordPositions, + range.startWordIndex, + range.endWordIndex, + ); + return { + rangeId: range.id, + symbol: range.sentenceElementName + ? resolvedConfigurations + .sentenceElementNameToSentenceElementSymbolMap[ + range.sentenceElementName + ] + : null, + position: + range.kind === "sentence-element" + ? resolvedConfigurations.sentenceElementPosition.determineSentenceElementRangeSentenceElementPosition( + rangePosition, + ) + : resolvedConfigurations.sentenceElementPosition.determineSentenceStructureRangeSentenceElementPosition( + rangePosition, + ), + }; + }), + relations: sentenceStructureData.relations.map((relation) => { + const fromRange = findRangeById(sentenceStructureData, { + rangeId: relation.fromRangeId, + }); + const toRange = findRangeById(sentenceStructureData, { + rangeId: relation.toRangeId, + }); + if (!fromRange || !toRange) { + throw new Error( + `Relation ${relation.id} has invalid range references.`, + ); + } + + const fromRangePosition = extractSpanPosition( + wordPositions, + fromRange.startWordIndex, + fromRange.endWordIndex, + ); + const toRangePosition = extractSpanPosition( + wordPositions, + toRange.startWordIndex, + toRange.endWordIndex, + ); + + return { + relationId: relation.id, + svgPathData: resolvedConfigurations.determineRelationSvgPathData( + fromRangePosition, + toRangePosition, + ), + }; + }), + coordinations: sentenceStructureData.coordinations.map((coordination) => { + const coordinationPosition = { + position: extractSpanPosition( + wordPositions, + coordination.children.at(0)!.startWordIndex, + coordination.children.at(-1)!.endWordIndex, + ), + childrenPositions: coordination.children.map((child) => + extractSpanPosition( + wordPositions, + child.startWordIndex, + child.endWordIndex, + ), + ), + }; + return { + coordinationId: coordination.id, + svgPathData: + resolvedConfigurations.determineCoordinationSvgPathData( + coordinationPosition, + ), + }; + }), + }; +} diff --git a/packages/sentence-structure-diagram-data/index.ts b/packages/sentence-structure-diagram-data/index.ts new file mode 100644 index 0000000..0c08cf0 --- /dev/null +++ b/packages/sentence-structure-diagram-data/index.ts @@ -0,0 +1,2 @@ +export { convertSentenceStructureDataToSentenceStructureDiagramData } from "./generate.js"; +export type { SentenceStructureDiagramData } from "./types.js"; diff --git a/packages/sentence-structure-diagram-data/package.json b/packages/sentence-structure-diagram-data/package.json new file mode 100644 index 0000000..c5da8cc --- /dev/null +++ b/packages/sentence-structure-diagram-data/package.json @@ -0,0 +1,21 @@ +{ + "name": "@sentence-structure-diagram-app/sentence-structure-diagram-data", + "version": "0.1.0", + "type": "module", + "main": "./dist/index.js", + "scripts": { + "build": "tsc", + "build:watch": "tsc --watch", + "clean": "rm -r dist" + }, + "dependencies": { + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-configurations": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-tree": "^0.1.0", + "fast-xml-parser": "^5.3.2", + "zod": "^4.1.13" + }, + "devDependencies": { + "typescript": "^5.9.3" + } +} diff --git a/packages/sentence-structure-diagram-data/resolve-configurations.ts b/packages/sentence-structure-diagram-data/resolve-configurations.ts new file mode 100644 index 0000000..dfa3de3 --- /dev/null +++ b/packages/sentence-structure-diagram-data/resolve-configurations.ts @@ -0,0 +1,300 @@ +import type { + SentenceElementName, + SentenceStructureRangeType, +} from "@sentence-structure-diagram-app/sentence-structure-data"; +import { + defaultConfigurations, + type BracketName, + type Configurations, + type LayoutMode, + type RelationShapeType, + type SentenceElementPositionType, +} from "@sentence-structure-diagram-app/sentence-structure-diagram-configurations"; + +type SpanPosition = { + start: number; + end: number; + top: number; + bottom: number; +}[]; + +type RangePosition = SpanPosition; + +type CoordinationPosition = { + position: SpanPosition; + childrenPositions: SpanPosition[]; +}; + +type ResolvedConfigurations = { + color: { + primaryColor: string; + textColor: string; + }; + sentenceStructureRangeTypeToBracketsMap: Record< + SentenceStructureRangeType, + { openingBracket: string; closingBracket: string } + >; + sentenceElementNameToSentenceElementSymbolMap: Record< + SentenceElementName, + string + >; + sentenceElementPosition: { + determineSentenceElementRangeSentenceElementPosition: ( + rangePosition: RangePosition, + ) => { x: number; y: number }; + determineSentenceStructureRangeSentenceElementPosition: ( + rangePosition: RangePosition, + ) => { x: number; y: number }; + }; + determineRelationSvgPathData: ( + fromRangePosition: RangePosition, + toRangePosition: RangePosition, + ) => string; + determineCoordinationSvgPathData: ( + coordinationPosition: CoordinationPosition, + ) => string; + layoutMode: LayoutMode; +}; + +function convertBracketNameToBrackets(bracketName: BracketName) { + switch (bracketName) { + case "(parenthesis)": + return { openingBracket: "(", closingBracket: ")" }; + case "": + return { openingBracket: "<", closingBracket: ">" }; + case "{curly-bracket}": + return { openingBracket: "{", closingBracket: "}" }; + case "[square-bracket]": + return { openingBracket: "[", closingBracket: "]" }; + } +} + +function determineSentenceElementPosition( + sentenceElementPositionType: SentenceElementPositionType, + rangePosition: RangePosition, +) { + switch (sentenceElementPositionType) { + case "bottom-center": + return { + x: (rangePosition[0]!.start + rangePosition[0]!.end) / 2, + y: rangePosition[0]!.bottom + 8, + }; + case "bottom-left": + return { + x: rangePosition[0]!.start - 4, + y: rangePosition[0]!.bottom + 8, + }; + } +} + +function determineRelationSvgPathData( + relationShapeType: RelationShapeType, + fromRangePosition: RangePosition, + toRangePosition: RangePosition, +) { + const fromPoint = { + x: + fromRangePosition[0]!.start + + (fromRangePosition[0]!.end - fromRangePosition[0]!.start) / 2, + y: fromRangePosition[0]!.top, + }; + const toPoint = { + x: + toRangePosition[0]!.start + + (toRangePosition[0]!.end - toRangePosition[0]!.start) / 2, + y: toRangePosition[0]!.top, + }; + + const height = 40; + + const curveTopY = Math.min(fromPoint.y, toPoint.y) - height; + const arrowSize = 4; + const arrowPosition = { + left: toPoint.x - arrowSize, + right: toPoint.x + arrowSize, + top: toPoint.y - arrowSize, + }; + const curveSvgPathData = [ + // 曲線 + `M ${fromPoint.x},${fromPoint.y}`, + `C ${fromPoint.x},${curveTopY} ${toPoint.x},${curveTopY} ${toPoint.x},${toPoint.y}`, + // 矢印 + `M ${arrowPosition.left},${arrowPosition.top}`, + `L ${toPoint.x},${toPoint.y}`, + `L ${arrowPosition.right},${arrowPosition.top}`, + ].join(" "); + + const straightSvgPathData = [ + // 直線 + `M ${fromPoint.x},${fromPoint.y}`, + `L ${fromPoint.x},${fromPoint.y - height / 2}`, + `L ${toPoint.x},${toPoint.y - height / 2}`, + `L ${toPoint.x},${toPoint.y}`, + // 矢印 + `M ${arrowPosition.left},${arrowPosition.top}`, + `L ${toPoint.x},${toPoint.y}`, + `L ${arrowPosition.right},${arrowPosition.top}`, + ].join(" "); + + switch (relationShapeType) { + case "curved": + return curveSvgPathData; + case "right-angle": + if (fromPoint.y === toPoint.y) { + return straightSvgPathData; + } else { + return curveSvgPathData; + } + } +} + +function determineCoordinationSvgPathData( + layoutMode: LayoutMode, + coordinationPosition: CoordinationPosition, +) { + switch (layoutMode) { + case "linear": { + const verticalLines = coordinationPosition.childrenPositions.map( + (position) => ({ + start: (position.at(0)!.start + position.at(0)!.end) / 2, + top: position.at(0)!.top - 20, + bottom: position.at(0)!.top, + }), + ); + const overLines = coordinationPosition.position.map( + (position, index) => ({ + start: + index === 0 + ? (coordinationPosition.childrenPositions.at(0)!.at(0)!.start + + coordinationPosition.childrenPositions.at(0)!.at(0)!.end) / + 2 + : position.start, + end: + index === coordinationPosition.position.length - 1 + ? (coordinationPosition.childrenPositions.at(-1)!.at(-1)!.start + + coordinationPosition.childrenPositions.at(-1)!.at(-1)!.end) / + 2 + : position.end, + top: position.top - 20, + }), + ); + return ( + verticalLines + .map( + (verticalLine) => + `M ${verticalLine.start} ${verticalLine.top} L ${verticalLine.start} ${verticalLine.bottom}`, + ) + .join(" ") + + " " + + overLines + .map( + (overLine) => + `M ${overLine.start} ${overLine.top} L ${overLine.end} ${overLine.top}`, + ) + .join(" ") + ); + } + case "structured": + const verticalLine = { + start: coordinationPosition.position.at(0)!.start - 10, + top: + (coordinationPosition.position.at(0)!.top + + coordinationPosition.position.at(0)!.bottom) / + 2, + bottom: + (coordinationPosition.position.at(-1)!.top + + coordinationPosition.position.at(-1)!.bottom) / + 2 + + 8, + }; + const topHorizontalLine = { + start: verticalLine.start, + end: verticalLine.start + 10, + top: verticalLine.top, + }; + const bottomHorizontalLine = { + start: verticalLine.start, + end: verticalLine.start + 10, + top: verticalLine.bottom, + }; + return [ + // 縦線 + `M ${verticalLine.start} ${verticalLine.top} L ${verticalLine.start} ${verticalLine.bottom}`, + // 上の横線 + `M ${topHorizontalLine.start} ${topHorizontalLine.top} L ${topHorizontalLine.end} ${topHorizontalLine.top}`, + // 下の横線 + `M ${bottomHorizontalLine.start} ${bottomHorizontalLine.top} L ${bottomHorizontalLine.end} ${bottomHorizontalLine.top}`, + ].join(" "); + } +} + +export function resolveConfigurations( + customConfigurations: Partial, +): ResolvedConfigurations { + const configurations: Configurations = { + color: customConfigurations.color + ? customConfigurations.color + : defaultConfigurations.color, + sentenceStructureRangeTypeToBracketNameMap: + customConfigurations.sentenceStructureRangeTypeToBracketNameMap + ? customConfigurations.sentenceStructureRangeTypeToBracketNameMap + : defaultConfigurations.sentenceStructureRangeTypeToBracketNameMap, + sentenceElementNameToSentenceElementSymbolMap: + customConfigurations.sentenceElementNameToSentenceElementSymbolMap + ? customConfigurations.sentenceElementNameToSentenceElementSymbolMap + : defaultConfigurations.sentenceElementNameToSentenceElementSymbolMap, + sentenceElementPositionType: + customConfigurations.sentenceElementPositionType + ? customConfigurations.sentenceElementPositionType + : defaultConfigurations.sentenceElementPositionType, + relationShapeType: customConfigurations.relationShapeType + ? customConfigurations.relationShapeType + : defaultConfigurations.relationShapeType, + layoutMode: customConfigurations.layoutMode + ? customConfigurations.layoutMode + : defaultConfigurations.layoutMode, + }; + + return { + color: configurations.color, + sentenceStructureRangeTypeToBracketsMap: { + modifier: convertBracketNameToBrackets( + configurations.sentenceStructureRangeTypeToBracketNameMap.modifier, + ), + phrase: convertBracketNameToBrackets( + configurations.sentenceStructureRangeTypeToBracketNameMap.phrase, + ), + clause: convertBracketNameToBrackets( + configurations.sentenceStructureRangeTypeToBracketNameMap.clause, + ), + }, + sentenceElementNameToSentenceElementSymbolMap: + configurations.sentenceElementNameToSentenceElementSymbolMap, + sentenceElementPosition: { + determineSentenceElementRangeSentenceElementPosition: (rangePosition) => + determineSentenceElementPosition( + configurations.sentenceElementPositionType + .sentenceElementRangeSentenceElementPositionType, + rangePosition, + ), + determineSentenceStructureRangeSentenceElementPosition: (rangePosition) => + determineSentenceElementPosition( + configurations.sentenceElementPositionType + .sentenceStructureRangeSentenceElementPositionType, + rangePosition, + ), + }, + determineRelationSvgPathData: (fromRangePosition, toRangePosition) => + determineRelationSvgPathData( + configurations.relationShapeType, + fromRangePosition, + toRangePosition, + ), + determineCoordinationSvgPathData: (coordinationPosition) => + determineCoordinationSvgPathData( + configurations.layoutMode, + coordinationPosition, + ), + layoutMode: configurations.layoutMode, + }; +} diff --git a/packages/sentence-structure-diagram-data/tsconfig.json b/packages/sentence-structure-diagram-data/tsconfig.json new file mode 100644 index 0000000..d6a2b39 --- /dev/null +++ b/packages/sentence-structure-diagram-data/tsconfig.json @@ -0,0 +1,44 @@ +{ + // Visit https://aka.ms/tsconfig to read more about this file + "compilerOptions": { + // File Layout + // "rootDir": "./src", + "outDir": "./dist", + + // Environment Settings + // See also https://aka.ms/tsconfig/module + "module": "nodenext", + "target": "esnext", + "types": [], + // For nodejs: + // "lib": ["esnext"], + // "types": ["node"], + // and npm install -D @types/node + + // Other Outputs + "sourceMap": true, + "declaration": true, + "declarationMap": true, + + // Stricter Typechecking Options + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + + // Style Options + // "noImplicitReturns": true, + // "noImplicitOverride": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + // "noFallthroughCasesInSwitch": true, + // "noPropertyAccessFromIndexSignature": true, + + // Recommended Options + "strict": true, + "jsx": "react-jsx", + "verbatimModuleSyntax": true, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "skipLibCheck": true + } +} diff --git a/packages/sentence-structure-diagram-data/types.ts b/packages/sentence-structure-diagram-data/types.ts new file mode 100644 index 0000000..ab65ba0 --- /dev/null +++ b/packages/sentence-structure-diagram-data/types.ts @@ -0,0 +1,48 @@ +export type SentenceStructureDiagramData = { + position: { + left: number; + right: number; + top: number; + bottom: number; + }; + color: { + primaryColor: string; + textColor: string; + }; + words: { + index: number; + text: string; + openingBrackets: string[]; + closingBrackets: string[]; + position: { + left: number; + right: number; + top: number; + bottom: number; + }; + }[]; + underlines: { + rangeId: string; + position: { + start: number; + end: number; + bottom: number; + }[]; + }[]; + sentenceElements: { + rangeId: string; + symbol: string | null; + position: { + x: number; + y: number; + }; + }[]; + relations: { + relationId: string; + svgPathData: string; + }[]; + coordinations: { + coordinationId: string; + svgPathData: string; + }[]; +}; diff --git a/packages/sentence-structure-diagram-svg/.gitignore b/packages/sentence-structure-diagram-svg/.gitignore new file mode 100644 index 0000000..9b1c8b1 --- /dev/null +++ b/packages/sentence-structure-diagram-svg/.gitignore @@ -0,0 +1 @@ +/dist diff --git a/packages/sentence-structure-diagram-svg/SentenceStructureDiagram.tsx b/packages/sentence-structure-diagram-svg/SentenceStructureDiagram.tsx new file mode 100644 index 0000000..1508805 --- /dev/null +++ b/packages/sentence-structure-diagram-svg/SentenceStructureDiagram.tsx @@ -0,0 +1,142 @@ +import { Fragment } from "react"; +import { + sentenceStructureDataToXMLString, + type SentenceStructureData, +} from "@sentence-structure-diagram-app/sentence-structure-data"; +import { + xmlStringToConfigurations, + type Configurations, +} from "@sentence-structure-diagram-app/sentence-structure-diagram-configurations"; +import { convertSentenceStructureDataToSentenceStructureDiagramData } from "@sentence-structure-diagram-app/sentence-structure-diagram-data"; + +type SentenceStructureDiagramProps = { + sentenceStructureData: SentenceStructureData; + maxWidth: number; + measureTextWidth: (text: string) => number; + configurations: Configurations; +}; + +export default function SentenceStructureDiagram( + props: SentenceStructureDiagramProps, +) { + const sentenceStructureDiagramData = + convertSentenceStructureDataToSentenceStructureDiagramData( + props.sentenceStructureData, + props.maxWidth, + props.measureTextWidth, + props.configurations, + ); + return ( + + + ${sentenceStructureDataToXMLString(props.sentenceStructureData)} + + + ${xmlStringToConfigurations.encode(props.configurations)} + +`, + }} + > + {/* 単語と括弧類 */} + {sentenceStructureDiagramData.words.map((word) => ( + + {word.openingBrackets.length > 0 && ( + + {word.openingBrackets.join("")} + + )} + + {word.text} + + {word.closingBrackets.length > 0 && ( + + {word.closingBrackets.join("")} + + )} + + ))} + {/* 下線 */} + {sentenceStructureDiagramData.underlines.map((underline) => + underline.position.map((position, index) => ( + + )), + )} + {/* 文の要素の記号 */} + {sentenceStructureDiagramData.sentenceElements.map( + (sentenceElement) => + sentenceElement.symbol && ( + + {sentenceElement.symbol} + + ), + )} + {/* 矢印 */} + {sentenceStructureDiagramData.relations.map((relation) => ( + + ))} + {/* 並列 */} + {sentenceStructureDiagramData.coordinations.map((coordination) => ( + + ))} + + ); +} diff --git a/packages/sentence-structure-diagram-svg/generate.ts b/packages/sentence-structure-diagram-svg/generate.ts new file mode 100644 index 0000000..4528cb0 --- /dev/null +++ b/packages/sentence-structure-diagram-svg/generate.ts @@ -0,0 +1,28 @@ +import ReactDOMServer from "react-dom/server"; +// import prettier from "prettier/standalone"; +// import prettierPluginHtml from "prettier/plugins/html"; +import type { SentenceStructureData } from "@sentence-structure-diagram-app/sentence-structure-data"; +import type { Configurations } from "@sentence-structure-diagram-app/sentence-structure-diagram-configurations"; +import SentenceStructureDiagram from "./SentenceStructureDiagram.js"; + +export function generateSvgString( + sentenceStructureData: SentenceStructureData, + maxWidth: number, + measureTextWidth: (text: string) => number, + configurations: Configurations, +): string { + // return await prettier.format( + return ReactDOMServer.renderToStaticMarkup( + SentenceStructureDiagram({ + sentenceStructureData, + maxWidth, + measureTextWidth, + configurations, + }), + ); + // { + // parser: "html", + // plugins: [prettierPluginHtml], + // }, + // ); +} diff --git a/packages/sentence-structure-diagram-svg/index.ts b/packages/sentence-structure-diagram-svg/index.ts new file mode 100644 index 0000000..6619bc8 --- /dev/null +++ b/packages/sentence-structure-diagram-svg/index.ts @@ -0,0 +1 @@ +export { generateSvgString } from "./generate.js"; diff --git a/packages/sentence-structure-diagram-svg/package.json b/packages/sentence-structure-diagram-svg/package.json new file mode 100644 index 0000000..2012eda --- /dev/null +++ b/packages/sentence-structure-diagram-svg/package.json @@ -0,0 +1,22 @@ +{ + "name": "@sentence-structure-diagram-app/sentence-structure-diagram-svg", + "version": "0.1.0", + "type": "module", + "main": "./dist/index.js", + "scripts": { + "build": "tsc", + "build:watch": "tsc --watch", + "clean": "rm -r dist" + }, + "dependencies": { + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-configurations": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-data": "^0.1.0", + "prettier": "^3.7.4", + "react": "^19.2.3", + "react-dom": "^19.2.3" + }, + "devDependencies": { + "typescript": "^5.9.3" + } +} diff --git a/packages/sentence-structure-diagram-svg/tsconfig.json b/packages/sentence-structure-diagram-svg/tsconfig.json new file mode 100644 index 0000000..d6a2b39 --- /dev/null +++ b/packages/sentence-structure-diagram-svg/tsconfig.json @@ -0,0 +1,44 @@ +{ + // Visit https://aka.ms/tsconfig to read more about this file + "compilerOptions": { + // File Layout + // "rootDir": "./src", + "outDir": "./dist", + + // Environment Settings + // See also https://aka.ms/tsconfig/module + "module": "nodenext", + "target": "esnext", + "types": [], + // For nodejs: + // "lib": ["esnext"], + // "types": ["node"], + // and npm install -D @types/node + + // Other Outputs + "sourceMap": true, + "declaration": true, + "declarationMap": true, + + // Stricter Typechecking Options + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + + // Style Options + // "noImplicitReturns": true, + // "noImplicitOverride": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + // "noFallthroughCasesInSwitch": true, + // "noPropertyAccessFromIndexSignature": true, + + // Recommended Options + "strict": true, + "jsx": "react-jsx", + "verbatimModuleSyntax": true, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "skipLibCheck": true + } +} diff --git a/packages/sentence-structure-diagram-tree/.gitignore b/packages/sentence-structure-diagram-tree/.gitignore new file mode 100644 index 0000000..9b1c8b1 --- /dev/null +++ b/packages/sentence-structure-diagram-tree/.gitignore @@ -0,0 +1 @@ +/dist diff --git a/packages/sentence-structure-diagram-tree/constants.ts b/packages/sentence-structure-diagram-tree/constants.ts new file mode 100644 index 0000000..d2990c9 --- /dev/null +++ b/packages/sentence-structure-diagram-tree/constants.ts @@ -0,0 +1,4 @@ +export const lineHeight = 16; +export const lineSpacing = 48; +export const wordSpacing = 32; +export const padding = 48; diff --git a/packages/sentence-structure-diagram-tree/create-linear-sentence-structure-diagram-tree.ts b/packages/sentence-structure-diagram-tree/create-linear-sentence-structure-diagram-tree.ts new file mode 100644 index 0000000..18c4706 --- /dev/null +++ b/packages/sentence-structure-diagram-tree/create-linear-sentence-structure-diagram-tree.ts @@ -0,0 +1,268 @@ +import type { + SentenceStructureCoordinationChildNode, + SentenceStructureCoordinationNode, + SentenceStructureNode, + SentenceStructureRangeNode, + SentenceStructureTree, + SentenceStructureWordNode, +} from "@sentence-structure-diagram-app/sentence-structure-tree"; +import type { + SentenceStructureDiagramCoordinationChildNode, + SentenceStructureDiagramCoordinationNode, + SentenceStructureDiagramNode, + SentenceStructureDiagramTree, + SentenceStructureDiagramRangeNode, + SentenceStructureDiagramWordNode, +} from "./types.js"; +import { lineHeight, lineSpacing, padding, wordSpacing } from "./constants.js"; + +type Input = { + sentenceStructureNode: T; + nextNodeStartPosition: { + start: number; + top: number; + }; + nextLineStartPosition: { + start: number; + top: number; + }; + maxWidth: number; + measureTextWidth: (text: string) => number; +}; + +type Result = { + sentenceStructureDiagramNode: T; + nextNodeStartPosition: { + start: number; + top: number; + }; + nextLineStartPosition: { + start: number; + top: number; + }; +}; + +function createSentenceStructureDiagramWordNode( + input: Input, +): Result { + let nextNodeStartPosition = { ...input.nextNodeStartPosition }; + let nextLineStartPosition = { ...input.nextLineStartPosition }; + if ( + input.maxWidth < + input.nextNodeStartPosition.start + + input.measureTextWidth(input.sentenceStructureNode.word.text) + + padding + ) { + nextNodeStartPosition = { ...input.nextLineStartPosition }; + nextLineStartPosition.top += lineHeight + lineSpacing; + } + return { + sentenceStructureDiagramNode: { + word: input.sentenceStructureNode.word, + position: { + start: nextNodeStartPosition.start, + end: + nextNodeStartPosition.start + + input.measureTextWidth(input.sentenceStructureNode.word.text), + top: nextNodeStartPosition.top, + bottom: nextNodeStartPosition.top + lineHeight, + }, + }, + nextNodeStartPosition: { + start: + nextNodeStartPosition.start + + input.measureTextWidth(input.sentenceStructureNode.word.text) + + wordSpacing, + top: nextNodeStartPosition.top, + }, + nextLineStartPosition, + }; +} + +function createSentenceStructureDiagramRangeNode( + input: Input, +): Result { + let nextNodeStartPosition = { ...input.nextNodeStartPosition }; + let nextLineStartPosition = { ...input.nextLineStartPosition }; + const children: SentenceStructureDiagramRangeNode["children"] = []; + for (const child of input.sentenceStructureNode.children) { + const result = + child.type === "word" + ? createSentenceStructureDiagramWordNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: input.maxWidth, + measureTextWidth: input.measureTextWidth, + }) + : child.type === "range" + ? createSentenceStructureDiagramRangeNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: input.maxWidth, + measureTextWidth: input.measureTextWidth, + }) + : createSentenceStructureDiagramCoordinationNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: input.maxWidth, + measureTextWidth: input.measureTextWidth, + }); + children.push(result.sentenceStructureDiagramNode); + nextNodeStartPosition = { ...result.nextNodeStartPosition }; + nextLineStartPosition = { ...result.nextLineStartPosition }; + } + + return { + sentenceStructureDiagramNode: { + range: input.sentenceStructureNode.range, + position: { + end: nextNodeStartPosition.start - wordSpacing, + top: input.nextNodeStartPosition.top, + bottom: nextLineStartPosition.top - lineSpacing, + }, + children: children, + }, + nextNodeStartPosition, + nextLineStartPosition, + }; +} + +function createSentenceStructureDiagramCoordinationChildNode( + input: Input, +): Result { + let nextNodeStartPosition = { ...input.nextNodeStartPosition }; + let nextLineStartPosition = { ...input.nextLineStartPosition }; + const children: SentenceStructureDiagramRangeNode["children"] = []; + for (const child of input.sentenceStructureNode.children) { + const result = + child.type === "word" + ? createSentenceStructureDiagramWordNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: input.maxWidth, + measureTextWidth: input.measureTextWidth, + }) + : child.type === "range" + ? createSentenceStructureDiagramRangeNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: input.maxWidth, + measureTextWidth: input.measureTextWidth, + }) + : createSentenceStructureDiagramCoordinationNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: input.maxWidth, + measureTextWidth: input.measureTextWidth, + }); + children.push(result.sentenceStructureDiagramNode); + nextNodeStartPosition = { ...result.nextNodeStartPosition }; + nextLineStartPosition = { ...result.nextLineStartPosition }; + } + + return { + sentenceStructureDiagramNode: { + coordinationChild: input.sentenceStructureNode.coordinationChild, + position: { + end: nextNodeStartPosition.start - wordSpacing, + top: input.nextNodeStartPosition.top, + bottom: nextLineStartPosition.top - lineSpacing, + }, + children: children, + }, + nextNodeStartPosition, + nextLineStartPosition, + }; +} + +function createSentenceStructureDiagramCoordinationNode( + input: Input, +): Result { + let nextNodeStartPosition = { ...input.nextNodeStartPosition }; + let nextLineStartPosition = { ...input.nextLineStartPosition }; + const children: SentenceStructureDiagramCoordinationNode["children"] = []; + for (const child of input.sentenceStructureNode.children) { + const result = createSentenceStructureDiagramCoordinationChildNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: input.maxWidth, + measureTextWidth: input.measureTextWidth, + }); + children.push(result.sentenceStructureDiagramNode); + nextNodeStartPosition = { ...result.nextNodeStartPosition }; + nextLineStartPosition = { ...result.nextLineStartPosition }; + } + + return { + sentenceStructureDiagramNode: { + coordination: input.sentenceStructureNode.coordination, + position: { + end: nextNodeStartPosition.start - wordSpacing, + top: input.nextNodeStartPosition.top, + bottom: nextLineStartPosition.top - lineSpacing, + }, + children, + }, + nextNodeStartPosition, + nextLineStartPosition, + }; +} + +export function createLinearSentenceStructureDiagramTree( + sentenceStructureTree: SentenceStructureTree, + maxWidth: number, + measureTextWidth: (text: string) => number, +): SentenceStructureDiagramTree { + let nextNodeStartPosition = { start: padding, top: padding }; + let nextLineStartPosition = { + start: padding, + top: padding + lineHeight + lineSpacing, + }; + const children: SentenceStructureDiagramTree["children"] = []; + for (const child of sentenceStructureTree.children) { + const result = + child.type === "word" + ? createSentenceStructureDiagramWordNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth, + measureTextWidth, + }) + : child.type === "range" + ? createSentenceStructureDiagramRangeNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth, + measureTextWidth, + }) + : createSentenceStructureDiagramCoordinationNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth, + measureTextWidth, + }); + children.push(result.sentenceStructureDiagramNode); + nextNodeStartPosition = { ...result.nextNodeStartPosition }; + nextLineStartPosition = { ...result.nextLineStartPosition }; + } + + return { + position: { + start: 0, + end: maxWidth, + top: 0, + bottom: nextLineStartPosition.top - lineSpacing + padding, + }, + children: children, + }; +} diff --git a/packages/sentence-structure-diagram-tree/create-structured-sentence-structure-diagram-tree.ts b/packages/sentence-structure-diagram-tree/create-structured-sentence-structure-diagram-tree.ts new file mode 100644 index 0000000..55b2f81 --- /dev/null +++ b/packages/sentence-structure-diagram-tree/create-structured-sentence-structure-diagram-tree.ts @@ -0,0 +1,382 @@ +import type { + SentenceStructureCoordinationChildNode, + SentenceStructureCoordinationNode, + SentenceStructureNode, + SentenceStructureRangeNode, + SentenceStructureTree, + SentenceStructureWordNode, +} from "@sentence-structure-diagram-app/sentence-structure-tree"; +import type { + SentenceStructureDiagramCoordinationChildNode, + SentenceStructureDiagramCoordinationNode, + SentenceStructureDiagramNode, + SentenceStructureDiagramTree, + SentenceStructureDiagramRangeNode, + SentenceStructureDiagramWordNode, +} from "./types.js"; +import { lineHeight, lineSpacing, padding, wordSpacing } from "./constants.js"; + +type Input = { + sentenceStructureNode: T; + nextNodeStartPosition: { + start: number; + top: number; + }; + nextLineStartPosition: { + start: number; + top: number; + }; + maxWidth: number; + measureTextWidth: (text: string) => number; +}; + +type Result = { + sentenceStructureDiagramNode: T; + nextNodeStartPosition: { + start: number; + top: number; + }; + nextLineStartPosition: { + start: number; + top: number; + }; +}; + +function createSentenceStructureDiagramWordNode( + input: Input, +): Result { + return { + sentenceStructureDiagramNode: { + word: input.sentenceStructureNode.word, + position: { + start: input.nextNodeStartPosition.start, + end: + input.nextNodeStartPosition.start + + input.measureTextWidth(input.sentenceStructureNode.word.text), + top: input.nextNodeStartPosition.top, + bottom: input.nextNodeStartPosition.top + lineHeight, + }, + }, + nextNodeStartPosition: { + start: + input.nextNodeStartPosition.start + + input.measureTextWidth(input.sentenceStructureNode.word.text) + + wordSpacing, + top: input.nextNodeStartPosition.top, + }, + nextLineStartPosition: input.nextLineStartPosition, + }; +} + +function createSentenceStructureDiagramRangeNode( + input: Input, +): Result { + let nextNodeStartPosition = { ...input.nextNodeStartPosition }; + let nextLineStartPosition = { + start: nextNodeStartPosition.start, + top: input.nextLineStartPosition.top, + }; + const children: SentenceStructureDiagramRangeNode["children"] = []; + for (const child of input.sentenceStructureNode.children) { + const childWidth = + child.type === "word" + ? createSentenceStructureDiagramWordNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: Infinity, + measureTextWidth: input.measureTextWidth, + }).sentenceStructureDiagramNode.position.end - + nextNodeStartPosition.start + : child.type === "range" + ? createSentenceStructureDiagramRangeNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: Infinity, + measureTextWidth: input.measureTextWidth, + }).sentenceStructureDiagramNode.position.end - + nextNodeStartPosition.start + : createSentenceStructureDiagramCoordinationNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: Infinity, + measureTextWidth: input.measureTextWidth, + }).sentenceStructureDiagramNode.position.end - + nextNodeStartPosition.start; + if ( + nextNodeStartPosition.start !== input.nextNodeStartPosition.start && + input.maxWidth < nextNodeStartPosition.start + childWidth + padding + ) { + nextNodeStartPosition = { ...nextLineStartPosition }; + nextLineStartPosition.top += lineHeight + lineSpacing; + } + const result = + child.type === "word" + ? createSentenceStructureDiagramWordNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: input.maxWidth, + measureTextWidth: input.measureTextWidth, + }) + : child.type === "range" + ? createSentenceStructureDiagramRangeNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: input.maxWidth, + measureTextWidth: input.measureTextWidth, + }) + : createSentenceStructureDiagramCoordinationNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: input.maxWidth, + measureTextWidth: input.measureTextWidth, + }); + children.push(result.sentenceStructureDiagramNode); + nextNodeStartPosition = { ...result.nextNodeStartPosition }; + nextLineStartPosition = { ...result.nextLineStartPosition }; + } + + return { + sentenceStructureDiagramNode: { + range: input.sentenceStructureNode.range, + position: { + end: nextNodeStartPosition.start - wordSpacing, + top: input.nextNodeStartPosition.top, + bottom: nextLineStartPosition.top - lineSpacing, + }, + children: children, + }, + nextNodeStartPosition, + nextLineStartPosition: { + start: input.nextLineStartPosition.start, + top: nextLineStartPosition.top, + }, + }; +} + +function createSentenceStructureDiagramCoordinationChildNode( + input: Input, +): Result { + let nextNodeStartPosition = { ...input.nextNodeStartPosition }; + let nextLineStartPosition = { + start: nextNodeStartPosition.start, + top: input.nextLineStartPosition.top, + }; + const children: SentenceStructureDiagramCoordinationChildNode["children"] = + []; + for (const child of input.sentenceStructureNode.children) { + const childWidth = + child.type === "word" + ? createSentenceStructureDiagramWordNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: Infinity, + measureTextWidth: input.measureTextWidth, + }).sentenceStructureDiagramNode.position.end - + nextNodeStartPosition.start + : child.type === "range" + ? createSentenceStructureDiagramRangeNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: Infinity, + measureTextWidth: input.measureTextWidth, + }).sentenceStructureDiagramNode.position.end - + nextNodeStartPosition.start + : createSentenceStructureDiagramCoordinationNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: Infinity, + measureTextWidth: input.measureTextWidth, + }).sentenceStructureDiagramNode.position.end - + nextNodeStartPosition.start; + if ( + nextNodeStartPosition.start !== input.nextNodeStartPosition.start && + input.maxWidth < nextNodeStartPosition.start + childWidth + padding + ) { + nextNodeStartPosition = { ...nextLineStartPosition }; + nextLineStartPosition.top += lineHeight + lineSpacing; + } + const result = + child.type === "word" + ? createSentenceStructureDiagramWordNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: input.maxWidth, + measureTextWidth: input.measureTextWidth, + }) + : child.type === "range" + ? createSentenceStructureDiagramRangeNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: input.maxWidth, + measureTextWidth: input.measureTextWidth, + }) + : createSentenceStructureDiagramCoordinationNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: input.maxWidth, + measureTextWidth: input.measureTextWidth, + }); + children.push(result.sentenceStructureDiagramNode); + nextNodeStartPosition = { ...result.nextNodeStartPosition }; + nextLineStartPosition = { ...result.nextLineStartPosition }; + } + + return { + sentenceStructureDiagramNode: { + coordinationChild: input.sentenceStructureNode.coordinationChild, + position: { + end: nextNodeStartPosition.start - wordSpacing, + top: input.nextNodeStartPosition.top, + bottom: nextLineStartPosition.top - lineSpacing, + }, + children: children, + }, + nextNodeStartPosition, + nextLineStartPosition: { + start: input.nextLineStartPosition.start, + top: nextLineStartPosition.top, + }, + }; +} + +function createSentenceStructureDiagramCoordinationNode( + input: Input, +): Result { + let nextNodeStartPosition = { ...input.nextNodeStartPosition }; + let nextLineStartPosition = { + start: nextNodeStartPosition.start, + top: nextNodeStartPosition.top + lineHeight + lineSpacing, + }; + const children: SentenceStructureDiagramCoordinationNode["children"] = []; + for (const child of input.sentenceStructureNode.children) { + const result = createSentenceStructureDiagramCoordinationChildNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: input.maxWidth, + measureTextWidth: input.measureTextWidth, + }); + children.push(result.sentenceStructureDiagramNode); + nextNodeStartPosition = { ...result.nextLineStartPosition }; + nextLineStartPosition.top += lineHeight + lineSpacing; + } + + return { + sentenceStructureDiagramNode: { + coordination: input.sentenceStructureNode.coordination, + position: { + end: Math.max(...children.map((child) => child.position.end)), + top: input.nextNodeStartPosition.top, + bottom: nextLineStartPosition.top - lineSpacing, + }, + children, + }, + nextNodeStartPosition: { + start: + Math.max(...children.map((child) => child.position.end)) + wordSpacing, + top: input.nextNodeStartPosition.top, + }, + nextLineStartPosition: { + start: input.nextLineStartPosition.start, + top: nextLineStartPosition.top, + }, + }; +} + +export function createStructuredSentenceStructureDiagramTree( + sentenceStructureTree: SentenceStructureTree, + maxWidth: number, + measureTextWidth: (text: string) => number, +): SentenceStructureDiagramTree { + let nextNodeStartPosition = { start: padding, top: padding }; + let nextLineStartPosition = { + start: padding, + top: padding + lineHeight + lineSpacing, + }; + const children: SentenceStructureDiagramTree["children"] = []; + for (const child of sentenceStructureTree.children) { + const childWidth = + child.type === "word" + ? createSentenceStructureDiagramWordNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: Infinity, + measureTextWidth: measureTextWidth, + }).sentenceStructureDiagramNode.position.end - + nextNodeStartPosition.start + : child.type === "range" + ? createSentenceStructureDiagramRangeNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: Infinity, + measureTextWidth: measureTextWidth, + }).sentenceStructureDiagramNode.position.end - + nextNodeStartPosition.start + : createSentenceStructureDiagramCoordinationNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: Infinity, + measureTextWidth: measureTextWidth, + }).sentenceStructureDiagramNode.position.end - + nextNodeStartPosition.start; + if ( + padding !== nextNodeStartPosition.start && + maxWidth < nextNodeStartPosition.start + childWidth + padding + ) { + nextNodeStartPosition = { ...nextLineStartPosition }; + nextLineStartPosition.top += lineHeight + lineSpacing; + } + const result = + child.type === "word" + ? createSentenceStructureDiagramWordNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: maxWidth, + measureTextWidth: measureTextWidth, + }) + : child.type === "range" + ? createSentenceStructureDiagramRangeNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: maxWidth, + measureTextWidth: measureTextWidth, + }) + : createSentenceStructureDiagramCoordinationNode({ + sentenceStructureNode: child, + nextNodeStartPosition, + nextLineStartPosition, + maxWidth: maxWidth, + measureTextWidth: measureTextWidth, + }); + children.push(result.sentenceStructureDiagramNode); + nextNodeStartPosition = { ...result.nextNodeStartPosition }; + nextLineStartPosition = { ...result.nextLineStartPosition }; + } + + return { + position: { + start: 0, + end: maxWidth, + top: 0, + bottom: nextLineStartPosition.top - lineSpacing + padding, + }, + children: children, + }; +} diff --git a/packages/sentence-structure-diagram-tree/create-tree.ts b/packages/sentence-structure-diagram-tree/create-tree.ts new file mode 100644 index 0000000..85b8711 --- /dev/null +++ b/packages/sentence-structure-diagram-tree/create-tree.ts @@ -0,0 +1,26 @@ +import type { SentenceStructureTree } from "@sentence-structure-diagram-app/sentence-structure-tree"; +import type { LayoutMode } from "@sentence-structure-diagram-app/sentence-structure-diagram-configurations"; +import type { SentenceStructureDiagramTree } from "./types.js"; +import { createStructuredSentenceStructureDiagramTree } from "./create-structured-sentence-structure-diagram-tree.js"; +import { createLinearSentenceStructureDiagramTree } from "./create-linear-sentence-structure-diagram-tree.js"; + +export function createSentenceStructureDiagramTree( + tree: SentenceStructureTree, + maxWidth: number, + measureTextWidth: (text: string) => number, + layoutMode: LayoutMode, +): SentenceStructureDiagramTree { + if (layoutMode === "structured") { + return createStructuredSentenceStructureDiagramTree( + tree, + maxWidth, + measureTextWidth, + ); + } else { + return createLinearSentenceStructureDiagramTree( + tree, + maxWidth, + measureTextWidth, + ); + } +} diff --git a/packages/sentence-structure-diagram-tree/index.ts b/packages/sentence-structure-diagram-tree/index.ts new file mode 100644 index 0000000..04f379c --- /dev/null +++ b/packages/sentence-structure-diagram-tree/index.ts @@ -0,0 +1,9 @@ +export { createSentenceStructureDiagramTree } from "./create-tree.js"; +export type { + SentenceStructureDiagramWordNode, + SentenceStructureDiagramRangeNode, + SentenceStructureDiagramCoordinationChildNode, + SentenceStructureDiagramCoordinationNode, + SentenceStructureDiagramTree, + SentenceStructureDiagramNode, +} from "./types.js"; diff --git a/packages/sentence-structure-diagram-tree/package.json b/packages/sentence-structure-diagram-tree/package.json new file mode 100644 index 0000000..dbe0906 --- /dev/null +++ b/packages/sentence-structure-diagram-tree/package.json @@ -0,0 +1,19 @@ +{ + "name": "@sentence-structure-diagram-app/sentence-structure-diagram-tree", + "version": "0.1.0", + "type": "module", + "main": "./dist/index.js", + "scripts": { + "build": "tsc", + "build:watch": "tsc --watch", + "clean": "rm -r dist" + }, + "dependencies": { + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-diagram-configurations": "^0.1.0", + "@sentence-structure-diagram-app/sentence-structure-tree": "^0.1.0" + }, + "devDependencies": { + "typescript": "^5.9.3" + } +} diff --git a/packages/sentence-structure-diagram-tree/tsconfig.json b/packages/sentence-structure-diagram-tree/tsconfig.json new file mode 100644 index 0000000..d6a2b39 --- /dev/null +++ b/packages/sentence-structure-diagram-tree/tsconfig.json @@ -0,0 +1,44 @@ +{ + // Visit https://aka.ms/tsconfig to read more about this file + "compilerOptions": { + // File Layout + // "rootDir": "./src", + "outDir": "./dist", + + // Environment Settings + // See also https://aka.ms/tsconfig/module + "module": "nodenext", + "target": "esnext", + "types": [], + // For nodejs: + // "lib": ["esnext"], + // "types": ["node"], + // and npm install -D @types/node + + // Other Outputs + "sourceMap": true, + "declaration": true, + "declarationMap": true, + + // Stricter Typechecking Options + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + + // Style Options + // "noImplicitReturns": true, + // "noImplicitOverride": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + // "noFallthroughCasesInSwitch": true, + // "noPropertyAccessFromIndexSignature": true, + + // Recommended Options + "strict": true, + "jsx": "react-jsx", + "verbatimModuleSyntax": true, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "skipLibCheck": true + } +} diff --git a/packages/sentence-structure-diagram-tree/types.ts b/packages/sentence-structure-diagram-tree/types.ts new file mode 100644 index 0000000..292b911 --- /dev/null +++ b/packages/sentence-structure-diagram-tree/types.ts @@ -0,0 +1,75 @@ +import type { + Coordination, + CoordinationChild, + Range, + Word, +} from "@sentence-structure-diagram-app/sentence-structure-data"; + +export type SentenceStructureDiagramWordNode = { + word: Word; + position: { + start: number; + end: number; + top: number; + bottom: number; + }; +}; + +export type SentenceStructureDiagramRangeNode = { + range: Range; + position: { + end: number; + top: number; + bottom: number; + }; + children: ( + | SentenceStructureDiagramWordNode + | SentenceStructureDiagramRangeNode + | SentenceStructureDiagramCoordinationNode + )[]; +}; + +export type SentenceStructureDiagramCoordinationChildNode = { + coordinationChild: CoordinationChild; + position: { + end: number; + top: number; + bottom: number; + }; + children: ( + | SentenceStructureDiagramWordNode + | SentenceStructureDiagramRangeNode + | SentenceStructureDiagramCoordinationNode + )[]; +}; + +export type SentenceStructureDiagramCoordinationNode = { + coordination: Coordination; + position: { + end: number; + top: number; + bottom: number; + }; + children: SentenceStructureDiagramCoordinationChildNode[]; +}; + +export type SentenceStructureDiagramTree = { + position: { + start: number; + end: number; + top: number; + bottom: number; + }; + children: ( + | SentenceStructureDiagramWordNode + | SentenceStructureDiagramRangeNode + | SentenceStructureDiagramCoordinationNode + )[]; +}; + +export type SentenceStructureDiagramNode = + | SentenceStructureDiagramWordNode + | SentenceStructureDiagramRangeNode + | SentenceStructureDiagramCoordinationChildNode + | SentenceStructureDiagramCoordinationNode + | SentenceStructureDiagramTree; diff --git a/packages/sentence-structure-tree/.gitignore b/packages/sentence-structure-tree/.gitignore new file mode 100644 index 0000000..9b1c8b1 --- /dev/null +++ b/packages/sentence-structure-tree/.gitignore @@ -0,0 +1 @@ +/dist diff --git a/packages/sentence-structure-tree/create-tree.ts b/packages/sentence-structure-tree/create-tree.ts new file mode 100644 index 0000000..548a7e3 --- /dev/null +++ b/packages/sentence-structure-tree/create-tree.ts @@ -0,0 +1,299 @@ +import { + findCoordinationByStartAndEndWordIndex, + findRangeByStartAndEndWordIndex, + type Coordination, + type SentenceStructureData, + type Word, +} from "@sentence-structure-diagram-app/sentence-structure-data"; +import type { + SentenceStructureCoordinationNode, + SentenceStructureRangeNode, + SentenceStructureTree, + SentenceStructureWordNode, +} from "./types.js"; + +function createSentenceStructureWordNode( + word: Word, +): SentenceStructureWordNode { + return { + type: "word", + word, + }; +} + +function createSpanChildren( + sentenceStructureData: SentenceStructureData, + span: { + startWordIndex: number; + endWordIndex: number; + }, +): ( + | SentenceStructureWordNode + | SentenceStructureRangeNode + | SentenceStructureCoordinationNode +)[] { + const children: ( + | SentenceStructureWordNode + | SentenceStructureRangeNode + | SentenceStructureCoordinationNode + )[] = []; + + let currentWordIndex = span.startWordIndex; + while (currentWordIndex <= span.endWordIndex) { + const nestedRange = + sentenceStructureData.ranges + .filter( + (range) => + range.startWordIndex !== span.startWordIndex || + range.endWordIndex !== span.endWordIndex, + ) + .filter( + (range) => + range.startWordIndex === currentWordIndex && + range.endWordIndex <= span.endWordIndex, + ) + .sort((a, b) => b.endWordIndex - a.endWordIndex) + .at(0) ?? null; + const nestedCoordination = + sentenceStructureData.coordinations + .filter( + (coordination) => + coordination.children.at(0)!.startWordIndex === currentWordIndex && + coordination.children.at(-1)!.endWordIndex <= span.endWordIndex, + ) + .sort( + (a, b) => + b.children.at(-1)!.endWordIndex - a.children.at(-1)!.endWordIndex, + ) + .at(0) ?? null; + if (!nestedRange && !nestedCoordination) { + children.push( + createSentenceStructureWordNode( + sentenceStructureData.words[currentWordIndex]!, + ), + ); + currentWordIndex++; + } else if (nestedRange && !nestedCoordination) { + children.push({ + type: "range", + range: nestedRange, + children: createSpanChildren(sentenceStructureData, nestedRange), + }); + currentWordIndex = nestedRange.endWordIndex + 1; + } else if (!nestedRange && nestedCoordination) { + children.push( + createSentenceStructureCoordinationNode( + sentenceStructureData, + nestedCoordination, + ), + ); + currentWordIndex = nestedCoordination.children.at(-1)!.endWordIndex + 1; + } else if (nestedRange && nestedCoordination) { + const nestedRangeEndWordIndex = nestedRange.endWordIndex; + const nestedCoordinationEndWordIndex = + nestedCoordination.children.at(-1)!.endWordIndex; + if (nestedCoordinationEndWordIndex < nestedRangeEndWordIndex) { + children.push({ + type: "range", + range: nestedRange, + children: createSpanChildren(sentenceStructureData, nestedRange), + }); + currentWordIndex = nestedRangeEndWordIndex + 1; + } else { + children.push( + createSentenceStructureCoordinationNode( + sentenceStructureData, + nestedCoordination, + ), + ); + currentWordIndex = nestedCoordinationEndWordIndex + 1; + } + } + } + + return children; +} + +function createSentenceStructureCoordinationNode( + sentenceStructureData: SentenceStructureData, + coordination: Coordination, +): SentenceStructureCoordinationNode { + const children: SentenceStructureCoordinationNode["children"] = + coordination.children.map((child) => { + const nestedRange = findRangeByStartAndEndWordIndex( + sentenceStructureData, + { + startWordIndex: child.startWordIndex, + endWordIndex: child.endWordIndex, + }, + ); + const nestedCoordination = findCoordinationByStartAndEndWordIndex( + sentenceStructureData, + { + startWordIndex: child.startWordIndex, + endWordIndex: child.endWordIndex, + }, + ); + if (nestedRange && nestedCoordination) { + return { + type: "coordination-child", + coordinationChild: child, + children: [ + { + type: "range", + range: nestedRange, + children: [ + createSentenceStructureCoordinationNode( + sentenceStructureData, + nestedCoordination, + ), + ], + }, + ], + }; + } else if (nestedRange) { + return { + type: "coordination-child", + coordinationChild: child, + children: [ + child.startWordIndex === child.endWordIndex + ? { + type: "range", + range: nestedRange, + children: [ + createSentenceStructureWordNode( + sentenceStructureData.words[child.startWordIndex]!, + ), + ], + } + : { + type: "range", + range: nestedRange, + children: createSpanChildren( + sentenceStructureData, + nestedRange, + ), + }, + ], + }; + } else if (nestedCoordination) { + return { + type: "coordination-child", + coordinationChild: child, + children: [ + createSentenceStructureCoordinationNode( + sentenceStructureData, + nestedCoordination, + ), + ], + }; + } else { + return child.startWordIndex === child.endWordIndex + ? { + type: "coordination-child", + coordinationChild: child, + children: [ + createSentenceStructureWordNode( + sentenceStructureData.words[child.startWordIndex]!, + ), + ], + } + : { + type: "coordination-child", + coordinationChild: child, + children: createSpanChildren(sentenceStructureData, child), + }; + } + }); + + return { + type: "coordination", + coordination, + children, + }; +} + +export function createTree( + sentenceStructureData: SentenceStructureData, +): SentenceStructureTree { + const nestedRange = findRangeByStartAndEndWordIndex(sentenceStructureData, { + startWordIndex: 0, + endWordIndex: sentenceStructureData.words.length - 1, + }); + const nestedCoordination = findCoordinationByStartAndEndWordIndex( + sentenceStructureData, + { + startWordIndex: 0, + endWordIndex: sentenceStructureData.words.length - 1, + }, + ); + if (sentenceStructureData.words.length === 0) { + return { + type: "tree", + children: [], + }; + } + if (nestedRange && nestedCoordination) { + return { + type: "tree", + children: [ + { + type: "range", + range: nestedRange, + children: [ + createSentenceStructureCoordinationNode( + sentenceStructureData, + nestedCoordination, + ), + ], + }, + ], + }; + } else if (nestedRange) { + return { + type: "tree", + children: [ + sentenceStructureData.words.length === 1 + ? { + type: "range", + range: nestedRange, + children: [ + createSentenceStructureWordNode( + sentenceStructureData.words[0]!, + ), + ], + } + : { + type: "range", + range: nestedRange, + children: createSpanChildren(sentenceStructureData, nestedRange), + }, + ], + }; + } else if (nestedCoordination) { + return { + type: "tree", + children: [ + createSentenceStructureCoordinationNode( + sentenceStructureData, + nestedCoordination, + ), + ], + }; + } else { + return sentenceStructureData.words.length === 1 + ? { + type: "tree", + children: [ + createSentenceStructureWordNode(sentenceStructureData.words[0]!), + ], + } + : { + type: "tree", + children: createSpanChildren(sentenceStructureData, { + startWordIndex: 0, + endWordIndex: sentenceStructureData.words.length - 1, + }), + }; + } +} diff --git a/packages/sentence-structure-tree/index.ts b/packages/sentence-structure-tree/index.ts new file mode 100644 index 0000000..ada3a88 --- /dev/null +++ b/packages/sentence-structure-tree/index.ts @@ -0,0 +1,9 @@ +export { createTree } from "./create-tree.js"; +export type { + SentenceStructureWordNode, + SentenceStructureRangeNode, + SentenceStructureCoordinationChildNode, + SentenceStructureCoordinationNode, + SentenceStructureTree, + SentenceStructureNode, +} from "./types.js"; diff --git a/packages/sentence-structure-tree/package.json b/packages/sentence-structure-tree/package.json new file mode 100644 index 0000000..ab018cf --- /dev/null +++ b/packages/sentence-structure-tree/package.json @@ -0,0 +1,17 @@ +{ + "name": "@sentence-structure-diagram-app/sentence-structure-tree", + "version": "0.1.0", + "type": "module", + "main": "./dist/index.js", + "scripts": { + "build": "tsc", + "build:watch": "tsc --watch", + "clean": "rm -r dist" + }, + "dependencies": { + "@sentence-structure-diagram-app/sentence-structure-data": "^0.1.0" + }, + "devDependencies": { + "typescript": "^5.9.3" + } +} diff --git a/packages/sentence-structure-tree/tsconfig.json b/packages/sentence-structure-tree/tsconfig.json new file mode 100644 index 0000000..d6a2b39 --- /dev/null +++ b/packages/sentence-structure-tree/tsconfig.json @@ -0,0 +1,44 @@ +{ + // Visit https://aka.ms/tsconfig to read more about this file + "compilerOptions": { + // File Layout + // "rootDir": "./src", + "outDir": "./dist", + + // Environment Settings + // See also https://aka.ms/tsconfig/module + "module": "nodenext", + "target": "esnext", + "types": [], + // For nodejs: + // "lib": ["esnext"], + // "types": ["node"], + // and npm install -D @types/node + + // Other Outputs + "sourceMap": true, + "declaration": true, + "declarationMap": true, + + // Stricter Typechecking Options + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + + // Style Options + // "noImplicitReturns": true, + // "noImplicitOverride": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + // "noFallthroughCasesInSwitch": true, + // "noPropertyAccessFromIndexSignature": true, + + // Recommended Options + "strict": true, + "jsx": "react-jsx", + "verbatimModuleSyntax": true, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "skipLibCheck": true + } +} diff --git a/packages/sentence-structure-tree/types.ts b/packages/sentence-structure-tree/types.ts new file mode 100644 index 0000000..ff3f8f4 --- /dev/null +++ b/packages/sentence-structure-tree/types.ts @@ -0,0 +1,53 @@ +import { + type Coordination, + type CoordinationChild, + type Range, + type Word, +} from "@sentence-structure-diagram-app/sentence-structure-data"; + +export type SentenceStructureWordNode = { + type: "word"; + word: Word; +}; + +export type SentenceStructureRangeNode = { + type: "range"; + range: Range; + children: ( + | SentenceStructureWordNode + | SentenceStructureRangeNode + | SentenceStructureCoordinationNode + )[]; +}; + +export type SentenceStructureCoordinationChildNode = { + type: "coordination-child"; + coordinationChild: CoordinationChild; + children: ( + | SentenceStructureWordNode + | SentenceStructureRangeNode + | SentenceStructureCoordinationNode + )[]; +}; + +export type SentenceStructureCoordinationNode = { + type: "coordination"; + coordination: Coordination; + children: SentenceStructureCoordinationChildNode[]; +}; + +export type SentenceStructureTree = { + type: "tree"; + children: ( + | SentenceStructureWordNode + | SentenceStructureRangeNode + | SentenceStructureCoordinationNode + )[]; +}; + +export type SentenceStructureNode = + | SentenceStructureWordNode + | SentenceStructureRangeNode + | SentenceStructureCoordinationChildNode + | SentenceStructureCoordinationNode + | SentenceStructureTree; diff --git a/render.yaml b/render.yaml new file mode 100644 index 0000000..b636cca --- /dev/null +++ b/render.yaml @@ -0,0 +1,13 @@ +services: + - type: web + runtime: node + name: sentence-structure-diagram-app-backend + region: singapore + plan: free + buildCommand: npm ci && npm run build + startCommand: cd packages/backend && npm start + envVars: + - key: WEB_ORIGIN + value: https://chvmvd.github.io + - key: GEMINI_API_KEY + sync: false