diff --git a/.eslintrc.json b/.eslintrc.json index 30be6b6..aa4c83b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -22,6 +22,6 @@ //"@typescript-eslint/no-unused-vars": "warn" "jest/no-conditional-expect": "off" }, - "ignorePatterns": ["dist", "jest.config.js"] + "ignorePatterns": ["dist", "jest.config.js", "httpdocs"] } diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 4d0f1e0..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,25 +0,0 @@ -# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs - -name: Node.js CI - -on: - push: - branches: [ "main", "dev" ] - pull_request: - branches: [ "dev" ] - -jobs: - build-node: - runs-on: ubuntu-latest - container: node:20 - steps: - - run: node --version - - uses: actions/checkout@v3 - - run: npm ci - - run: npm run build:prod --if-present - - name: Start server - run: | - npm start & - sleep 5 # Give server some time to start - - run: npm test diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index 3aed66a..95578d7 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -15,8 +15,7 @@ jobs: with: node-version: 16 - run: npm ci # or yarn install - - uses: sibiraj-s/action-eslint@v3 - with: - eslint-args: '--ignore-path=.gitignore --quiet' - extensions: 'js,jsx,ts,tsx' - annotations: true + - name: Lint server-side code + run: npx eslint src/ --fix + - name: Lint client-side code + run: npx eslint httpdocs/js/ --fix diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..e31f256 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,48 @@ +name: Tests + +on: + workflow_dispatch: + push: + branches: [ "dev", "main" ] + pull_request: + branches: [ "dev", "main" ] + +jobs: + build: + runs-on: ubuntu-latest + env: + NODE_ENV: ${{ vars.NODE_ENV }} + LOCALHOST: ${{ vars.LOCALHOST }} + LOCALHOSTV6: ${{ vars.LOCALHOSTV6 }} + KEYA: ${{ secrets.KEYA }} + KEYB: ${{ secrets.KEYB }} + USER_TEST: ${{ secrets.USER_TEST }} + + steps: + - uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: '20' + cache: 'npm' + - run: echo "NODE_ENV = $NODE_ENV" + - run: npm ci + - run: npm run build --if-present + - name: Start server + run: | + sudo NODE_ENV=$NODE_ENV LOCALHOST=$LOCALHOST LOCALHOSTV6=$LOCALHOSTV6 KEYA=$KEYA KEYB=$KEYB USER_TEST=$USER_TEST npm start & + sleep 15 # Give server some time to start + - name: Check if server is running + run: | + curl --fail http://localhost:80 || exit 1 + - name: Run app tests + run: npm run test:app + - name: Run login tests + run: npm run test:login + - name: Run unit tests + run: npm run test:unit + - name: Run integration tests + run: npm run test:integration + + + \ No newline at end of file diff --git a/.gitignore b/.gitignore index c6bba59..c53815f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +data/ + # Logs logs *.log diff --git a/.vscode/launch.json b/.vscode/launch.json index c56ec22..8425a2b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,21 +1,15 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Debug server.ts", - "skipFiles": [ - "/**" - ], - "program": "${workspaceFolder}\\src\\app.ts", - "preLaunchTask": "tsc: build - tsconfig.json", - "outFiles": [ - "${workspaceFolder}/dist/**/*.js" - ] - } - ] + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Debug TypeScript", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}\\src\\app.ts", + "preLaunchTask": "build" + } + ] } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..864577f --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,18 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "type": "shell", + "command": "npx tsc -p tsconfig.json", + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "silent" + }, + "problemMatcher": "$tsc" + } + ] +} diff --git a/httpdocs/color-table.svg b/httpdocs/color-table.svg new file mode 100644 index 0000000..d299941 --- /dev/null +++ b/httpdocs/color-table.svg @@ -0,0 +1,159 @@ + + + + + + HEX + + + main + + + info + + + alert + + + success + + + neutral + + + + OKLCH + + + + + 900 + + + + 750 + + + + 625 + + + 500 + + + 375 + + + 250 + + + 100 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/httpdocs/css/colors.css b/httpdocs/css/colors.css new file mode 100644 index 0000000..d77a7fd --- /dev/null +++ b/httpdocs/css/colors.css @@ -0,0 +1,95 @@ +/* +created by atmos https://app.atmos.style/65cc9eaec76d443c0a796d4b + +** base configuration colors ** +Main: #f90 +Info: #231aee +Danger: #ff0000 +Success: #59ec04 +Neutral: #131211 +*/ + +[class*=color] { + --lightness: 67.66%; + --hue: 64.55; + --chroma: 0.007; + color: oklch(var(--lightness) var(--chroma) var(--hue)); + + &[class*=l1] {--lightness: 10%;} + &[class*=l2] {--lightness: 25%;} + &[class*=l3] {--lightness: 37.5%;} + &[class*=l4] {--lightness: 50%;} + &[class*=l5] {--lightness: 62.5%;} + &[class*=l6] {--lightness: 77.2%;} + &[class*=l7] {--lightness: 90%;} + + &[class*=main] { + --lightness: 77.2%; + --chroma: 0.1738; + --hue: 64.55; + + &[class*=l1] {--chroma: 0.02;} + &[class*=l2] {--chroma: 0.056;} + &[class*=l3] {--chroma: 0.085;} + &[class*=l4] {--chroma: 0.114;} + &[class*=l5] {--chroma: 0.142;} + &[class*=l6] {--chroma: 0.1738;} /* base */ + &[class*=l7] {--chroma: 0.06;} + } + + &[class*=info] { + --lightness: 44.87%; + --chroma: 0.2838; + --hue: 268.0; + + &[class*=l1] {--chroma: 0.055;} + &[class*=l2] {--chroma: 0.158;} + &[class*=l3] {--chroma: 0.237;} + &[class*=l4] {--chroma: 0.2838;} /* base */ + &[class*=l5] {--chroma: 0.19;} + &[class*=l6] {--chroma: 0.109;} + &[class*=l7] {--chroma: 0.04;} + } + + &[class*=alert] { + --lightness: 62.8%; + --chroma: 0.2577; + --hue: 29.23; + + &[class*=l1] {--chroma: 0.036;} + &[class*=l2] {--chroma: 0.103;} + &[class*=l3] {--chroma: 0.154;} + &[class*=l4] {--chroma: 0.195;} + &[class*=l5] {--chroma: 0.2577;} /* base */ + &[class*=l6] {--chroma: 0.133;} + &[class*=l7] {--chroma: 0.045;} + } + + &[class*=success] { + --lightness: 83%; + --chroma: 0.2607; + --hue: 138.96; + + &[class*=l1] {--chroma: 0.029;} + &[class*=l2] {--chroma: 0.083;} + &[class*=l3] {--chroma: 0.124;} + &[class*=l4] {--chroma: 0.157;} + &[class*=l5] {--chroma: 0.208;} + &[class*=l6] {--chroma: 0.2607;} /* base */ + &[class*=l7] {--chroma: 0.201;} + } + + &[class*=neutral] { + --lightness: 18.3%; + --chroma: 0.0026; + --hue: 67.66; + + &[class*=l1] {--chroma: 0.001;} + &[class*=l2] {--chroma: 0.0026;} /* base */ + &[class*=l3] {--chroma: 0.006;} + &[class*=l4] {--chroma: 0.007;} + &[class*=l5] {--chroma: 0.009;} + &[class*=l6] {--chroma: 0.011;} + &[class*=l7] {--chroma: 0.004;} + } +} diff --git a/httpdocs/css/login.css b/httpdocs/css/login.css new file mode 100644 index 0000000..b9d1d7c --- /dev/null +++ b/httpdocs/css/login.css @@ -0,0 +1,20 @@ +form { + margin-inline: auto; + display: flex; + flex-wrap: wrap; + justify-content: space-between; + max-width: 500px; + gap: 10px; +} +input, button { + flex-grow: 1; +} +textarea, h1 { + flex-basis: 100%; +} +textarea { + height: 50vh; +} +h1 { + text-align: center; +} diff --git a/httpdocs/favicon.ico b/httpdocs/favicon.ico new file mode 100644 index 0000000..040a17f Binary files /dev/null and b/httpdocs/favicon.ico differ diff --git a/httpdocs/icon.png b/httpdocs/icon.png new file mode 100644 index 0000000..827ec59 Binary files /dev/null and b/httpdocs/icon.png differ diff --git a/httpdocs/js/.eslintrc.json b/httpdocs/js/.eslintrc.json new file mode 100644 index 0000000..487f0f6 --- /dev/null +++ b/httpdocs/js/.eslintrc.json @@ -0,0 +1,12 @@ +{ + "root": true, + "env": { + "browser": true, + "es2021": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": 2021, + "sourceType": "module" + } +} diff --git a/httpdocs/js/login.js b/httpdocs/js/login.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/httpdocs/js/login.js @@ -0,0 +1 @@ + diff --git a/jest.config.js b/jest.config.js index 2adbbae..0a57f54 100644 --- a/jest.config.js +++ b/jest.config.js @@ -6,4 +6,5 @@ module.exports = { moduleNameMapper: { '^@src/(.*)$': '/src/$1', }, + bail: true }; \ No newline at end of file diff --git a/nodemon-static.json b/nodemon-static.json new file mode 100644 index 0000000..221cf6c --- /dev/null +++ b/nodemon-static.json @@ -0,0 +1,8 @@ +{ + "watch": [ + "httpdocs" + ], + "ext": "*", + "ignore": [], + "exec": "cp -R httpdocs/ dist/" +} \ No newline at end of file diff --git a/nodemon-ts.json b/nodemon-ts.json new file mode 100644 index 0000000..ad7eb01 --- /dev/null +++ b/nodemon-ts.json @@ -0,0 +1,9 @@ +{ + "watch": ["src"], + "ext": ".ts", + "ignore": [], + "exec": "tsc && node dist/app.js", + "events": { + "start": "clear" + } +} \ No newline at end of file diff --git a/nodemon.json b/nodemon.json deleted file mode 100644 index 92b25d2..0000000 --- a/nodemon.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "watch": ["src"], - "ext": ".ts", - "ignore": [], - "exec": "tsc && node dist/app.js" -} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 808a918..2953ac7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,27 +8,42 @@ "name": "lorex", "version": "0.0.1", "dependencies": { - "express": "^4.18.2", + "bcrypt": "^5.1.1", + "chalk": "^4.1.2", + "compression": "^1.7.4", + "ejs": "^3.1.9", + "express": "^4.19.2", + "express-rate-limit": "^7.2.0", + "express-slow-down": "^2.0.1", "express-validator": "^7.0.1", + "helmet": "^7.1.0", "hpp": "^0.2.3", - "module-alias": "^2.2.3" + "jsonwebtoken": "^9.0.2", + "module-alias": "^2.2.3", + "toobusy-js": "^0.5.1" }, "devDependencies": { "@jest/globals": "^29.7.0", - "@tsconfig/node20": "^20.1.2", + "@tsconfig/node20": "^20.1.4", "@types/bcrypt": "^5.0.2", + "@types/compression": "^1.7.5", "@types/express": "^4.17.21", "@types/hpp": "^0.2.5", "@types/jest": "^29.5.11", - "@types/node": "^20.10.6", + "@types/jsonwebtoken": "^9.0.6", + "@types/node": "^20.11.30", + "@types/toobusy-js": "^0.5.4", "@typescript-eslint/eslint-plugin": "^6.18.1", "@typescript-eslint/parser": "^6.18.1", "axios": "^1.6.5", + "concurrently": "^8.2.2", "dotenv": "^16.3.1", "eslint": "^8.56.0", "eslint-plugin-jest": "^27.6.3", + "install": "^0.13.0", "jest": "^29.7.0", "nodemon": "^3.0.2", + "npm": "^10.5.0", "ts-jest": "^29.1.2", "ts-node": "^10.9.2", "typescript": "^5.3.3" @@ -647,6 +662,18 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", + "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", @@ -1361,6 +1388,61 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1445,9 +1527,9 @@ "dev": true }, "node_modules/@tsconfig/node20": { - "version": "20.1.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.2.tgz", - "integrity": "sha512-madaWq2k+LYMEhmcp0fs+OGaLFk0OenpHa4gmI4VEmCKX4PJntQ6fnnGADVFrVkBj0wIdAlQnK/MrlYTHsa1gQ==", + "version": "20.1.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.4.tgz", + "integrity": "sha512-sqgsT69YFeLWf5NtJ4Xq/xAF8p4ZQHlmGW74Nu2tD4+g5fAsposc4ZfaaPixVu4y01BEiDCWLRDCvDM5JOsRxg==", "dev": true }, "node_modules/@types/babel__core": { @@ -1510,6 +1592,15 @@ "@types/node": "*" } }, + "node_modules/@types/compression": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.5.tgz", + "integrity": "sha512-AAQvK5pxMpaT+nDvhHrsBhLSYG5yQdtkaJE1WYieSNY2mVFKAgmU4ks65rkZD5oqnGCFLyQpUr1CqI4DmUMyDg==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -1607,6 +1698,15 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz", + "integrity": "sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -1614,9 +1714,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.10.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.6.tgz", - "integrity": "sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==", + "version": "20.11.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", + "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -1667,6 +1767,12 @@ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, + "node_modules/@types/toobusy-js": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@types/toobusy-js/-/toobusy-js-0.5.4.tgz", + "integrity": "sha512-hsKMbYiaL3ZWx7B3FYyN0rEJexw7I1HgKbNToX3ZZJv6373to954wlA7zrXR3/XoVwZnFwWqFguBs91sNzJGKQ==", + "dev": true + }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -1997,8 +2103,7 @@ "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "node_modules/accepts": { "version": "1.3.8", @@ -2042,6 +2147,38 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2089,7 +2226,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -2098,7 +2234,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -2122,6 +2257,23 @@ "node": ">= 8" } }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -2148,6 +2300,11 @@ "node": ">=8" } }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -2259,8 +2416,20 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } }, "node_modules/binary-extensions": { "version": "2.2.0", @@ -2272,12 +2441,12 @@ } }, "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -2285,7 +2454,7 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -2298,7 +2467,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2369,6 +2537,11 @@ "node-int64": "^0.4.0" } }, + "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==" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -2384,13 +2557,18 @@ } }, "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2438,7 +2616,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2454,7 +2631,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -2463,7 +2639,6 @@ "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, "dependencies": { "has-flag": "^4.0.0" }, @@ -2507,6 +2682,14 @@ "fsevents": "~2.3.2" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -2562,7 +2745,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -2573,8 +2755,15 @@ "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==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } }, "node_modules/combined-stream": { "version": "1.0.8", @@ -2588,11 +2777,107 @@ "node": ">= 0.8" } }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "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 + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/concurrently": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", + "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.2", + "date-fns": "^2.30.0", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "spawn-command": "0.0.2", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": "^14.13.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/concurrently/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/concurrently/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" }, "node_modules/content-disposition": { "version": "0.5.4", @@ -2620,9 +2905,9 @@ "dev": true }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "engines": { "node": ">= 0.6" } @@ -2673,6 +2958,22 @@ "node": ">= 8" } }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -2711,16 +3012,19 @@ } }, "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/delayed-stream": { @@ -2732,6 +3036,11 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -2749,6 +3058,14 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -2812,11 +3129,33 @@ "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, + "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==", + "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==" }, + "node_modules/ejs": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", + "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.640", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.640.tgz", @@ -2838,8 +3177,7 @@ "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/encodeurl": { "version": "1.0.2", @@ -2858,6 +3196,25 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "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==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -3301,16 +3658,16 @@ } }, "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -3341,6 +3698,34 @@ "node": ">= 0.10.0" } }, + "node_modules/express-rate-limit": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.2.0.tgz", + "integrity": "sha512-T7nul1t4TNyfZMJ7pKRKkdeVJWa2CqB8NA1P8BwYaoDI5QSBZARv5oMS43J7b7I5P+4asjVXjb7ONuwDKucahg==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": "4 || 5 || ^5.0.0-beta.1" + } + }, + "node_modules/express-slow-down": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/express-slow-down/-/express-slow-down-2.0.1.tgz", + "integrity": "sha512-zRogSZhNXJYKDBekhgFfFXGrOngH7Fub7Mx2g8OQ4RUBwSJP/3TVEKMgSGR/WlneT0mJ6NBUnidHhIELGVPe3w==", + "dependencies": { + "express-rate-limit": "7" + }, + "engines": { + "node": ">= 16" + }, + "peerDependencies": { + "express": ">= 4" + } + }, "node_modules/express-validator": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-7.0.1.tgz", @@ -3417,24 +3802,51 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" + "minimatch": "^5.0.1" } }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dependencies": { - "debug": "2.6.9", + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "on-finished": "2.4.1", @@ -3483,9 +3895,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "dev": true, "funding": [ { @@ -3532,11 +3944,32 @@ "node": ">= 0.6" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", @@ -3560,6 +3993,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -3579,15 +4031,19 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { + "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3617,7 +4073,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -3713,20 +4168,20 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "engines": { "node": ">= 0.4" }, @@ -3745,10 +4200,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", "dependencies": { "function-bind": "^1.1.2" }, @@ -3756,6 +4216,14 @@ "node": ">= 0.4" } }, + "node_modules/helmet": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.1.0.tgz", + "integrity": "sha512-g+HZqgfbpXdCkme/Cd/mZkV0aV3BZZZSugecH03kl38m/Kmdx8jKjBikpDj2cr+Iynv4KpYEviojNdTJActJAg==", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/hpp": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/hpp/-/hpp-0.2.3.tgz", @@ -3789,6 +4257,39 @@ "node": ">= 0.8" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -3872,7 +4373,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -3883,6 +4383,15 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/install": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/install/-/install-0.13.0.tgz", + "integrity": "sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -3934,7 +4443,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -4115,6 +4623,23 @@ "node": ">=8" } }, + "node_modules/jake": { + "version": "10.8.7", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", + "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -4745,6 +5270,51 @@ "node": ">=6" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/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==" + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -4811,6 +5381,36 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -4823,11 +5423,15 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -4957,7 +5561,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4965,6 +5568,48 @@ "node": "*" } }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/module-alias": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.3.tgz", @@ -4989,6 +5634,30 @@ "node": ">= 0.6" } }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -5076,6 +5745,163 @@ "node": ">=0.10.0" } }, + "node_modules/npm": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.5.0.tgz", + "integrity": "sha512-Ejxwvfh9YnWVU2yA5FzoYLTW52vxHCz+MHrOFg9Cc8IFgF/6f5AGPAvb5WTay5DIUP1NIfN3VBZ0cLlGO0Ys+A==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/run-script", + "@sigstore/tuf", + "abbrev", + "archy", + "cacache", + "chalk", + "ci-info", + "cli-columns", + "cli-table3", + "columnify", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmhook", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "ms", + "node-gyp", + "nopt", + "normalize-package-data", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "npmlog", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "semver", + "spdx-expression-parse", + "ssri", + "supports-color", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which", + "write-file-atomic" + ], + "dev": true, + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^7.2.1", + "@npmcli/config": "^8.0.2", + "@npmcli/fs": "^3.1.0", + "@npmcli/map-workspaces": "^3.0.4", + "@npmcli/package-json": "^5.0.0", + "@npmcli/promise-spawn": "^7.0.1", + "@npmcli/run-script": "^7.0.4", + "@sigstore/tuf": "^2.3.1", + "abbrev": "^2.0.0", + "archy": "~1.0.0", + "cacache": "^18.0.2", + "chalk": "^5.3.0", + "ci-info": "^4.0.0", + "cli-columns": "^4.0.0", + "cli-table3": "^0.6.3", + "columnify": "^1.6.0", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^7.0.1", + "ini": "^4.1.1", + "init-package-json": "^6.0.0", + "is-cidr": "^5.0.3", + "json-parse-even-better-errors": "^3.0.1", + "libnpmaccess": "^8.0.1", + "libnpmdiff": "^6.0.3", + "libnpmexec": "^7.0.4", + "libnpmfund": "^5.0.1", + "libnpmhook": "^10.0.0", + "libnpmorg": "^6.0.1", + "libnpmpack": "^6.0.3", + "libnpmpublish": "^9.0.2", + "libnpmsearch": "^7.0.0", + "libnpmteam": "^6.0.0", + "libnpmversion": "^5.0.1", + "make-fetch-happen": "^13.0.0", + "minimatch": "^9.0.3", + "minipass": "^7.0.4", + "minipass-pipeline": "^1.2.4", + "ms": "^2.1.2", + "node-gyp": "^10.0.1", + "nopt": "^7.2.0", + "normalize-package-data": "^6.0.0", + "npm-audit-report": "^5.0.0", + "npm-install-checks": "^6.3.0", + "npm-package-arg": "^11.0.1", + "npm-pick-manifest": "^9.0.0", + "npm-profile": "^9.0.0", + "npm-registry-fetch": "^16.1.0", + "npm-user-validate": "^2.0.0", + "npmlog": "^7.0.1", + "p-map": "^4.0.0", + "pacote": "^17.0.6", + "parse-conflict-json": "^3.0.1", + "proc-log": "^3.0.0", + "qrcode-terminal": "^0.12.0", + "read": "^2.1.0", + "semver": "^7.6.0", + "spdx-expression-parse": "^3.0.1", + "ssri": "^10.0.5", + "supports-color": "^9.4.0", + "tar": "^6.2.0", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^3.0.0", + "validate-npm-package-name": "^5.0.0", + "which": "^4.0.0", + "write-file-atomic": "^5.0.1" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -5088,73 +5914,2707 @@ "node": ">=8" } }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/npm/node_modules/@colors/colors": { + "version": "1.5.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" } }, - "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==", + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "ee-first": "1.1.1" + "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": ">= 0.8" + "node": ">=12" } }, - "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==", + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", "dev": true, - "dependencies": { - "wrappy": "1" + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/onetime": { + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "mimic-fn": "^2.1.0" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">= 0.8.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "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==", + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "7.4.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^3.1.0", + "@npmcli/installed-package-contents": "^2.0.2", + "@npmcli/map-workspaces": "^3.0.2", + "@npmcli/metavuln-calculator": "^7.0.0", + "@npmcli/name-from-folder": "^2.0.0", + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.0.0", + "@npmcli/query": "^3.1.0", + "@npmcli/run-script": "^7.0.2", + "bin-links": "^4.0.1", + "cacache": "^18.0.0", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^7.0.1", + "json-parse-even-better-errors": "^3.0.0", + "json-stringify-nice": "^1.1.4", + "minimatch": "^9.0.0", + "nopt": "^7.0.0", + "npm-install-checks": "^6.2.0", + "npm-package-arg": "^11.0.1", + "npm-pick-manifest": "^9.0.0", + "npm-registry-fetch": "^16.0.0", + "npmlog": "^7.0.1", + "pacote": "^17.0.4", + "parse-conflict-json": "^3.0.0", + "proc-log": "^3.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^3.0.2", + "semver": "^7.3.7", + "ssri": "^10.0.5", + "treeverse": "^3.0.0", + "walk-up-path": "^3.0.1" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "8.2.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^3.0.2", + "ci-info": "^4.0.0", + "ini": "^4.1.0", + "nopt": "^7.0.0", + "proc-log": "^3.0.0", + "read-package-json-fast": "^3.0.2", + "semver": "^7.3.5", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/disparity-colors": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ansi-styles": "^4.3.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/disparity-colors/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "5.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^7.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^9.0.0", + "proc-log": "^3.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "bin": { + "installed-package-contents": "lib/index.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "3.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^2.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0", + "read-package-json-fast": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^18.0.0", + "json-parse-even-better-errors": "^3.0.0", + "pacote": "^17.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^5.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^7.0.0", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "7.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/query": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "7.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "node-gyp": "^10.0.0", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/@sigstore/bundle": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/core": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.3.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/sign": { + "version": "2.2.3", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.2.0", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.0", + "make-fetch-happen": "^13.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "2.3.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.0", + "tuf-js": "^2.2.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/verify": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.2.0", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@tufjs/models": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/aggregate-error": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/are-we-there-yet": { + "version": "4.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "4.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "read-cmd-shim": "^4.0.0", + "write-file-atomic": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/builtins": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "18.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/chalk": { + "version": "5.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.0.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "4.0.3", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^5.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/clean-stack": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cli-table3": { + "version": "0.6.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/npm/node_modules/clone": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "6.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/color-support": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/npm/node_modules/columnify": { + "version": "1.6.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "strip-ansi": "^6.0.1", + "wcwidth": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/console-control-strings": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.3.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/defaults": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/diff": { + "version": "5.2.0", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/npm/node_modules/foreground-child": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "3.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/function-bind": { + "version": "1.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/npm/node_modules/gauge": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^4.0.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "10.3.10", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/has-unicode": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hasown": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "7.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "7.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "6.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ini": { + "version": "4.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^11.0.0", + "promzard": "^1.0.0", + "read": "^2.0.0", + "read-package-json": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/ip-address": { + "version": "9.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/npm/node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "5.0.3", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "4.0.3" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/is-core-module": { + "version": "2.13.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/is-lambda": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/jackspeak": { + "version": "2.3.6", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/jsbn": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "6.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.5.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^11.0.1", + "npm-registry-fetch": "^16.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "6.0.7", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^7.2.1", + "@npmcli/disparity-colors": "^3.0.0", + "@npmcli/installed-package-contents": "^2.0.2", + "binary-extensions": "^2.2.0", + "diff": "^5.1.0", + "minimatch": "^9.0.0", + "npm-package-arg": "^11.0.1", + "pacote": "^17.0.4", + "tar": "^6.2.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "7.0.8", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^7.2.1", + "@npmcli/run-script": "^7.0.2", + "ci-info": "^4.0.0", + "npm-package-arg": "^11.0.1", + "npmlog": "^7.0.1", + "pacote": "^17.0.4", + "proc-log": "^3.0.0", + "read": "^2.0.0", + "read-package-json-fast": "^3.0.2", + "semver": "^7.3.7", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "5.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^7.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmhook": { + "version": "10.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^16.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "6.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^16.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "6.0.7", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^7.2.1", + "@npmcli/run-script": "^7.0.2", + "npm-package-arg": "^11.0.1", + "pacote": "^17.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "9.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ci-info": "^4.0.0", + "normalize-package-data": "^6.0.0", + "npm-package-arg": "^11.0.1", + "npm-registry-fetch": "^16.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.7", + "sigstore": "^2.2.0", + "ssri": "^10.0.5" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "7.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^16.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^16.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "5.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^5.0.3", + "@npmcli/run-script": "^7.0.2", + "json-parse-even-better-errors": "^3.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "10.2.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "13.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "9.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "7.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "3.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-json-stream": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/npm/node_modules/minipass-json-stream/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/negotiator": { + "version": "0.6.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "10.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^4.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "7.2.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "6.3.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "11.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^7.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^6.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^11.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^16.0.0", + "proc-log": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "16.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^11.0.0", + "proc-log": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npmlog": { + "version": "7.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^4.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^5.0.0", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/pacote": { + "version": "17.0.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^5.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/promise-spawn": "^7.0.0", + "@npmcli/run-script": "^7.0.0", + "cacache": "^18.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^11.0.0", + "npm-packlist": "^8.0.0", + "npm-pick-manifest": "^9.0.0", + "npm-registry-fetch": "^16.0.0", + "proc-log": "^3.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^7.0.0", + "read-package-json-fast": "^3.0.0", + "sigstore": "^2.2.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^3.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "1.10.1", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "6.0.15", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-inflight": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "^2.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "~1.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/read-package-json": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^10.2.2", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.6.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/set-blocking": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/sigstore": { + "version": "2.2.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.2.0", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.0", + "@sigstore/sign": "^2.2.3", + "@sigstore/tuf": "^2.3.1", + "@sigstore/verify": "^1.1.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.8.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 16.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "socks": "^2.7.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.2.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.5.0", + "dev": true, + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.17", + "dev": true, + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/ssri": { + "version": "10.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "inBundle": true, + "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/npm/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "dev": true, + "inBundle": true, + "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/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "9.4.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.2.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "2.0.0", + "debug": "^4.3.4", + "make-fetch-happen": "^13.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/wcwidth": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/npm/node_modules/which": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/which/node_modules/isexe": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/wide-align": { + "version": "1.1.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", + "dev": true, + "inBundle": true, + "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/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "dev": true, + "inBundle": true, + "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/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "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/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "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==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "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==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "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, + "dependencies": { + "yocto-queue": "^0.1.0" }, "engines": { "node": ">=10" @@ -5238,7 +8698,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -5503,9 +8962,9 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -5522,6 +8981,19 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -5534,6 +9006,12 @@ "node": ">=8.10.0" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -5613,7 +9091,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -5647,6 +9124,21 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/rxjs/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -5675,7 +9167,6 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -5728,15 +9219,22 @@ "node": ">= 0.8.0" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5768,14 +9266,27 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5784,8 +9295,7 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/simple-update-notifier": { "version": "2.0.0", @@ -5833,6 +9343,12 @@ "source-map": "^0.6.0" } }, + "node_modules/spawn-command": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", + "dev": true + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -5868,6 +9384,14 @@ "node": ">= 0.8" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -5885,7 +9409,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -5899,7 +9422,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -5961,6 +9483,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tar": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", + "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -6016,6 +9554,14 @@ "node": ">=0.6" } }, + "node_modules/toobusy-js": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/toobusy-js/-/toobusy-js-0.5.1.tgz", + "integrity": "sha512-GiCux/c8G2TV0FTDgtxnXOxmSAndaI/9b1YxT14CqyeBDtTZAcJLx9KlXT3qECi8D0XCc78T4sN/7gWtjRyCaA==", + "engines": { + "node": ">=0.9.1" + } + }, "node_modules/touch": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", @@ -6028,6 +9574,20 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, "node_modules/ts-api-utils": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", @@ -6264,6 +9824,11 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -6327,6 +9892,20 @@ "makeerror": "1.0.12" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6342,6 +9921,14 @@ "node": ">= 8" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -6362,8 +9949,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write-file-atomic": { "version": "4.0.2", @@ -6390,8 +9976,7 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { "version": "17.7.2", diff --git a/package.json b/package.json index 43db4ea..4e3216f 100644 --- a/package.json +++ b/package.json @@ -5,40 +5,63 @@ "main": "index.js", "scripts": { "prebuild": "rm -rf dist/*", - "build": "npx tsc && cp -R httpdocs/ dist/", - "build:prod": "npx tsc -p ./tsconfig.prod.json && cp -R httpdocs/ dist/", + "build": "npx tsc", + "build:prod": "npx tsc -p ./tsconfig.prod.json", + "postbuild": "cp -R httpdocs/ dist/", "start": "node dist/app.js", - "dev": "rm -rf dist/* && cp -R httpdocs/ dist/ && nodemon src/app.ts", + "dev": "npm run prebuild && npm run postbuild && concurrently \"npm:dev:*\"", + "dev:ts": "nodemon --config nodemon-ts.json", + "dev:static": "nodemon --config nodemon-static.json", "lint": "eslint . --fix", - "test": "jest" + "lint:client": "eslint httpdocs/js/ --fix", + "test": "jest", + "test:app": "jest src/tests/app.test.ts", + "test:login": "jest src/tests/login.test.ts", + "test:unit": "jest src/tests/unit.test.ts", + "test:integration": "jest src/tests/integration.test.ts" }, "keywords": [], "author": "Type-Style", "devDependencies": { "@jest/globals": "^29.7.0", - "@tsconfig/node20": "^20.1.2", + "@tsconfig/node20": "^20.1.4", "@types/bcrypt": "^5.0.2", + "@types/compression": "^1.7.5", "@types/express": "^4.17.21", "@types/hpp": "^0.2.5", "@types/jest": "^29.5.11", - "@types/node": "^20.10.6", + "@types/jsonwebtoken": "^9.0.6", + "@types/node": "^20.11.30", + "@types/toobusy-js": "^0.5.4", "@typescript-eslint/eslint-plugin": "^6.18.1", "@typescript-eslint/parser": "^6.18.1", "axios": "^1.6.5", + "concurrently": "^8.2.2", "dotenv": "^16.3.1", "eslint": "^8.56.0", "eslint-plugin-jest": "^27.6.3", + "install": "^0.13.0", "jest": "^29.7.0", "nodemon": "^3.0.2", + "npm": "^10.5.0", "ts-jest": "^29.1.2", "ts-node": "^10.9.2", "typescript": "^5.3.3" }, "dependencies": { - "express": "^4.18.2", + "bcrypt": "^5.1.1", + "chalk": "^4.1.2", + "compression": "^1.7.4", + "ejs": "^3.1.9", + "express": "^4.19.2", + "express-rate-limit": "^7.2.0", + "express-slow-down": "^2.0.1", "express-validator": "^7.0.1", + "helmet": "^7.1.0", "hpp": "^0.2.3", - "module-alias": "^2.2.3" + "jsonwebtoken": "^9.0.2", + "module-alias": "^2.2.3", + "toobusy-js": "^0.5.1" }, "_moduleAliases": { "@src": "dist" diff --git a/src/app.test.ts b/src/app.test.ts deleted file mode 100644 index 0682f2d..0000000 --- a/src/app.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import axios from 'axios'; - -describe('Server Status', () => { - it('The server is running', async () => { - let serverStatus; - try { - const response = await axios.get('http://localhost'); - serverStatus = response.status; - } catch (error) { - console.error(error); - } - - expect(serverStatus).toBe(200); - }) -}) \ No newline at end of file diff --git a/src/app.ts b/src/app.ts index 382c255..17afecb 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,34 +1,108 @@ require('module-alias/register'); import { config } from 'dotenv'; import express from 'express'; +import path from 'path'; +import toobusy from 'toobusy-js'; +import compression from 'compression'; +import helmet from 'helmet'; import hpp from 'hpp'; -import cache from './cache'; -import * as error from "./error"; +import cache from './middleware/cache'; +import * as error from "./middleware/error"; import writeRouter from '@src/controller/write'; -import path from 'path'; +import readRouter from '@src/controller/read'; +import loginRouter from '@src/controller/login'; import logger from '@src/scripts/logger'; - +import { baseRateLimiter, cleanup as cleanupRateLimitedIps } from './middleware/limit'; +import { cleanupCSRF } from "@src/scripts/token"; // configurations -config(); +config(); // dotenv + const app = express(); -app.use(hpp()); + +app.set('view engine', 'ejs'); + +app.use((req, res, next) => { // monitor eventloop to block requests if busy + if (toobusy()) { + res.status(503).set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Retry-After': '60' }).send("I'm busy right now, sorry."); + } else { next(); } +}); +app.use((req, res, next) => { // clean up IPv6 Addresses + if (req.ip) { + res.locals.ip = req.ip.startsWith('::ffff:') ? req.ip.substring(7) : req.ip; + next(); + } else { + const message = "No IP provided"; + logger.error(message); + res.status(400).send(message); + } +}) + +app.use(helmet({ contentSecurityPolicy: { directives: { "default-src": "'self'", "img-src": "*" } } })); app.use(cache); +app.use(compression()) +app.use(hpp()); +app.use(baseRateLimiter); +app.use((req, res, next) => { // limit body for specific http methods + if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method)) { + return express.urlencoded({ limit: '0.5kb', extended: true })(req, res, next); + } + next(); +}); + // routes app.get('/', (req, res) => { - res.send('Hello World, via TypeScript and Node.js!'); + logger.log(req.ip + " - " + res.locals.ip, true); + res.send('Hello World, via TypeScript and Node.js! ' + `ENV: ${process.env.NODE_ENV}`); }); + app.use('/write', writeRouter); +app.use('/read', readRouter); +app.use('/login', loginRouter); // use httpdocs as static folder -app.use('/', express.static(path.join(__dirname, 'httpdocs'))) +app.use('/', express.static(path.join(__dirname, 'httpdocs'), { + extensions: ['html', 'txt', "pdf"], + index: ["start.html", "start.txt"], +})); // error handling app.use(error.notFound); app.use(error.handler); // init server -app.listen(80, () => { - logger.log(`Server running //localhost:80`); +const server = app.listen(80, () => { + logger.log(`Server running //localhost:80, ENV: ${process.env.NODE_ENV}`, true); +}); + +// scheduled cleanup +setInterval(() => { + cleanupCSRF(); + cleanupRateLimitedIps(); +}, 1000 * 60 * 5); + +// catching shutdowns +['SIGINT', 'SIGTERM', 'exit'].forEach((signal) => { + process.on(signal, () => { + function logAndExit() { + // calling .shutdown allows your process to exit normally + toobusy.shutdown(); + logger.log(`Server shutdown on signal: ${signal} //localhost:80`, true); + process.exit(); + } + if (signal != "exit") { // give the server time to shutdown before closing + server.close(logAndExit); + } else { + logger.log(`Server shutdown immediate: ${signal} //localhost:80`, true); + } + }); +}); + +// last resort error handling +process.on('uncaughtException', function (err) { + console.error('Caught exception:', err); + logger.error(err); + server.close(); + process.exit(1); }); \ No newline at end of file diff --git a/src/controller/login.ts b/src/controller/login.ts new file mode 100644 index 0000000..6a47f4c --- /dev/null +++ b/src/controller/login.ts @@ -0,0 +1,58 @@ +import express, { Request, Response, NextFunction } from 'express'; +import { create as createError } from '@src/middleware/error'; +import { crypt, compare } from '@src/scripts/crypt'; +import { loginSlowDown, loginLimiter, baseSlowDown, baseRateLimiter } from '@src/middleware/limit'; +import { createJWT, createCSRF, validateCSRF } from '@src/scripts/token'; + + +const router = express.Router(); + +router.get("/", baseSlowDown, baseRateLimiter, async function login(req: Request, res: Response, next: NextFunction) { + loginLimiter(req, res, () => { + const csrfToken = createCSRF(res, next); + res.locals = {...res.locals, text: 'start', csrfToken: csrfToken}; + res.render("login-form"); + }); +}); + +router.post("/", loginSlowDown, async function postLogin(req: Request, res: Response, next: NextFunction) { + loginLimiter(req, res, async () => { + let validLogin = false; + const token = req.body.csrfToken; + const user = req.body.user; + const password = req.body.password; + let userFound = false; + if (!user || !password) { return createError(res, 422, "Body does not contain all expected information", next); } + if (!token || !validateCSRF(req.body.csrfToken)) { return createError(res, 403, "Invalid CSRF Token", next); } + + // Loop through all environment variables + for (const key in process.env) { + if (!key.startsWith('USER')) { continue; } + if (key.substring(5) == user) { + userFound = true; + const hash = process.env[key]; + if (hash) { + validLogin = await compare(password, hash); + } + } + } + + // only allow test user in test environment + if (user == "TEST" && validLogin && process.env.NODE_ENV == "production") { + validLogin = false; + } + + if (validLogin) { + const token = createJWT(req, res); + res.json({ "token": token }); + } else { + if (!userFound) { + await crypt(password); // If no matching user is found, perform a dummy password comparison to prevent timing attacks + } + return createError(res, 403, `Invalid credentials`, next); + } + }); +}); + + +export default router; \ No newline at end of file diff --git a/src/controller/read.ts b/src/controller/read.ts new file mode 100644 index 0000000..bd4b88c --- /dev/null +++ b/src/controller/read.ts @@ -0,0 +1,37 @@ +import express, { Request, Response, NextFunction } from 'express'; +import * as file from '@src/scripts/file'; +import { create as createError } from '@src/middleware/error'; +import { validationResult, query } from 'express-validator'; +import { isLoggedIn } from '@src/middleware/logged-in'; + +const router = express.Router(); + +router.get('/', + isLoggedIn, + [query('index').isInt().withMessage("not an integer") + .isLength({ max: 3 }).withMessage("not in range") + .toInt()], + async function getRead(req: Request, res: Response, next: NextFunction) { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return createError(res, 400, JSON.stringify({ errors: errors.array() }), next) + } + + const fileObj: File.Obj = file.getFile(res, next); + fileObj.content = await file.readAsJson(res, fileObj.path, next) + if (!fileObj.content || !Array.isArray(fileObj.content.entries)) { + return createError(res, undefined, `File corrupt: ${fileObj.path}`, next); + } + + let entries = fileObj.content.entries; + + if (req.query.index) { + entries = entries.slice(Number(req.query.index)); + } + + res.json({ entries }); + }); + + + +export default router; diff --git a/src/controller/write.test.ts b/src/controller/write.test.ts deleted file mode 100644 index f447d22..0000000 --- a/src/controller/write.test.ts +++ /dev/null @@ -1,100 +0,0 @@ -import axios, { AxiosError } from 'axios'; - -describe('HEAD /write', () => { - it('with all parameters correctly set it should succeed', async () => { - const timestamp = new Date().getTime(); - const response = await axios.head(`http://localhost/write?user=xx&lat=45.000&lon=90.000×tamp=${timestamp}&hdop=50.0&altitude=5000.000&speed=150.000&heading=180.0&key=test`); - expect(response.status).toBe(200); - }); - - it('without key it sends 403', async () => { - try { - const timestamp = new Date().getTime(); - await axios.head(`http://localhost/write?user=xx&lat=45.000&lon=90.000×tamp=${timestamp}&hdop=50.0&altitude=5000.000&speed=150.000&heading=180.0`); - } catch (error) { - const axiosError = error as AxiosError; - expect(axiosError.response!.status).toBe(403); - } - }); - - it('with user length not equal to 2 it sends 422', async () => { - try { - const timestamp = new Date().getTime(); - await axios.head(`http://localhost/write?user=x&lat=45.000&lon=90.000×tamp=${timestamp}&hdop=50.0&altitude=5000.000&speed=150.000&heading=180.0&key=test`); - } catch (error) { - const axiosError = error as AxiosError; - expect(axiosError.response!.status).toBe(422); - } - }); - - - it('with lat not between -90 and 90 it sends 422', async () => { - try { - const timestamp = new Date().getTime(); - await axios.head(`http://localhost/write?user=xx&lat=91.000&lon=90.000×tamp=${timestamp}&hdop=50.0&altitude=5000.000&speed=150.000&heading=180.0&key=test`); - } catch (error) { - const axiosError = error as AxiosError; - expect(axiosError.response!.status).toBe(422); - } - }); - - it('with lon not between -180 and 180 it sends 422', async () => { - try { - const timestamp = new Date().getTime(); - await axios.head(`http://localhost/write?user=xx&lat=45.000&lon=181.000×tamp=${timestamp}&hdop=50.0&altitude=5000.000&speed=150.000&heading=180.0&key=test`); - } catch (error) { - const axiosError = error as AxiosError; - expect(axiosError.response!.status).toBe(422); - } - }); - - it('with timestamp to old sends 422', async () => { - try { - const timestamp = new Date().getTime() - 24 * 60 * 60 * 1000 * 2; // two days ago - await axios.head(`http://localhost/write?user=xx&lat=45.000&lon=90.000×tamp=${timestamp}&hdop=101.0&altitude=5000.000&speed=150.000&heading=180.0&key=test`); - } catch (error) { - const axiosError = error as AxiosError; - expect(axiosError.response!.status).toBe(422); - } - }) - - it('with hdop not between 0 and 100 it sends 422', async () => { - try { - const timestamp = new Date().getTime(); - await axios.head(`http://localhost/write?user=xx&lat=45.000&lon=90.000×tamp=${timestamp}&hdop=101.0&altitude=5000.000&speed=150.000&heading=180.0&key=test`); - } catch (error) { - const axiosError = error as AxiosError; - expect(axiosError.response!.status).toBe(422); - } - }); - - it('with altitude not between 0 and 10000 it sends 422', async () => { - try { - const timestamp = new Date().getTime(); - await axios.head(`http://localhost/write?user=xx&lat=45.000&lon=90.000×tamp=${timestamp}&hdop=50.0&altitude=10001.000&speed=150.000&heading=180.0&key=test`); - } catch (error) { - const axiosError = error as AxiosError; - expect(axiosError.response!.status).toBe(422); - } - }); - - it('with speed not between 0 and 300 it sends 422', async () => { - try { - const timestamp = new Date().getTime(); - await axios.head(`http://localhost/write?user=xx&lat=45.000&lon=90.000×tamp=${timestamp}&hdop=50.0&altitude=5000.000&speed=301.000&heading=180.0&key=test`); - } catch (error) { - const axiosError = error as AxiosError; - expect(axiosError.response!.status).toBe(422); - } - }); - - it('with heading not between 0 and 360 it sends 422', async () => { - try { - const timestamp = new Date().getTime(); - await axios.head(`http://localhost/write?user=xx&lat=45.000&lon=90.000×tamp=${timestamp}&hdop=50.0&altitude=5000.000&speed=150.000&heading=361.0&key=test`); - } catch (error) { - const axiosError = error as AxiosError; - expect(axiosError.response!.status).toBe(422); - } - }); -}); diff --git a/src/controller/write.ts b/src/controller/write.ts index b62fd57..28aa439 100644 --- a/src/controller/write.ts +++ b/src/controller/write.ts @@ -1,17 +1,25 @@ import express, { Request, Response, NextFunction } from 'express'; import { entry } from '@src/models/entry'; import { validationResult } from 'express-validator'; +import { create as createError } from '@src/middleware/error'; +import { baseSlowDown, errorRateLimiter } from '@src/middleware/limit'; // example call: /write?user=xx&lat=00.000&lon=00.000×tamp=1704063600000&hdop=0.0&altitude=0.000&speed=0.000&heading=000.0 -function errorChecking (req:Request, res:Response, next:NextFunction) { + +function errorChecking(req: Request, res: Response, next: NextFunction) { const errors = validationResult(req); if (!errors.isEmpty()) { - const errorAsJson = { errors: errors.array()}; - const errorAsString = new Error(JSON.stringify(errorAsJson)); - const hasKeyErrors = errors.array().some(error => error.msg.includes("Key")); - - res.status(hasKeyErrors ? 403 : 422); // send forbidden or unprocessable content - return next(errorAsString); + // if errors happend, then rateLimit to prevent key bruteforcing + errorRateLimiter(req, res, () => { + const errorAsJson = { errors: errors.array() }; + const errorAsString = JSON.stringify(errorAsJson); + const hasKeyErrors = errors.array().some(error => error.msg.includes("Key")); + + // send forbidden or unprocessable content + return createError(res, hasKeyErrors ? 403 : 422, errorAsString, next) + }); + + return; } if (req.method == "HEAD") { @@ -19,19 +27,23 @@ function errorChecking (req:Request, res:Response, next:NextFunction) { return; } - // Regular Save logic from here - - //entry.create(req, res); - //const test = process.env.TEST; - // res.send(req.query); - + next(); } +async function writeData(req: Request, res: Response, next: NextFunction) { + // Regular Save logic from here + await entry.create(req, res, next); -const router = express.Router(); -router.use(entry.validate); + if (!res.locals.error) { + res.send(req.query); + } else { + next(); + } +} -router.get('/', errorChecking); -router.head('/', errorChecking); + +const router = express.Router(); +router.get('/', baseSlowDown, entry.validate, errorChecking, writeData); +router.head('/', baseSlowDown, entry.validate, errorChecking); export default router; \ No newline at end of file diff --git a/src/middleware/cache.ts b/src/middleware/cache.ts new file mode 100644 index 0000000..c64ae4a --- /dev/null +++ b/src/middleware/cache.ts @@ -0,0 +1,15 @@ +import {Request, Response, NextFunction } from 'express'; + +const setCache = function (req: Request, res: Response, next: NextFunction) { + const seconds = 60 * 5; // 5 minuits + + // cache get requests but nothing else + if (req.method == "GET") { + res.set("Cache-control", `public, max-age=${seconds}`); + } else { + res.set("Cache-control", 'no-store'); + } + + next(); +} +export default setCache; \ No newline at end of file diff --git a/src/middleware/error.ts b/src/middleware/error.ts new file mode 100644 index 0000000..8db272e --- /dev/null +++ b/src/middleware/error.ts @@ -0,0 +1,45 @@ +import { Request, Response, NextFunction } from "express"; +import logger from '@src/scripts/logger'; + +export function create(res:Response, status:number = 500, message:string, next:NextFunction) { + /** + * takes httpStatusCode and Message and forwards to error Handling + */ + const error = new Error(message); + res.status(status); + res.locals.error = true; // to let other middleware know that an error was called + next(error) +} + +export function notFound(req: Request, res: Response, next: NextFunction) { + res.status(404); + const error = new Error(`🔍 - Not Found - ${req.originalUrl}`); + next(error); +} + +export function handler(err: HttpError, req: Request, res: Response, next: NextFunction) { + let statusCode = res.statusCode; + if (statusCode == 200) { + statusCode = err.statusCode || err.status || 500 + } + res.status(statusCode); + + let message; + try { + const jsonMessage = JSON.parse(err.message); + message = jsonMessage; + } catch (e) { + message = err.message; + } + + const responseBody:Response.Error = { + status: statusCode, + name: err.name, + message: message, + stack: process.env.NODE_ENV === "development" ? err.stack : "---" + }; + + logger.error(responseBody); + res.json(responseBody); + next(); +} diff --git a/src/middleware/limit.ts b/src/middleware/limit.ts new file mode 100644 index 0000000..1301071 --- /dev/null +++ b/src/middleware/limit.ts @@ -0,0 +1,71 @@ +import { Request, Response, NextFunction } from 'express'; +import { rateLimit, Options as rateLimiterOptions } from 'express-rate-limit'; +import { slowDown, Options as slowDownOptions } from 'express-slow-down'; +import logger from '@src/scripts/logger'; + +const ipsThatReachedLimit: RateLimit.obj = {}; // prevent logs from flooding + +/* +** configurations +*/ +const baseOptions: Partial = { + windowMs: 3 * 60 * 1000, + skip: (req, res) => (res.locals.ip == "127.0.0.1" || res.locals.ip == "::1") +} + +const baseSlowDownOptions: Partial = { + ...baseOptions, + delayAfter: 3, // no delay for amount of attempts + delayMs: (used: number) => (used - 3) * 125, // Add delay after delayAfter is reached +} + +const baseRateLimitOptions: Partial = { + ...baseOptions, + limit: 50, // Limit each IP per window + standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers + legacyHeaders: false, // Disable the `X-RateLimit-*` headers + handler: function rateHandler(req: Request, res: Response, next: NextFunction, options: rateLimiterOptions) { + if (!Object.prototype.hasOwnProperty.call(ipsThatReachedLimit, res.locals.ip)) { + logger.error(`[RateLimit] reached ${req.originalUrl}, ${res.locals.ip}, ${req.get('User-Agent')}`); + ipsThatReachedLimit[res.locals.ip] = { limitReachedOnError: true, time: Date.now() }; + } + res.status(options.statusCode).send(options.message); + }, + message: "Too many requests" +} + + + +/* +** exported section +*/ +export const baseSlowDown = slowDown(baseSlowDownOptions); + +export const loginSlowDown = slowDown({ + ...baseSlowDownOptions, + delayAfter: 1, // no delay for amount of attempts + delayMs: (used: number) => (used - 1) * 250, // Add delay after delayAfter is reached +}); + +export const baseRateLimiter = rateLimit(baseRateLimitOptions); + +export const errorRateLimiter = rateLimit({ + ...baseRateLimitOptions, + message: 'Too many requests with errors', +}); + +export const loginLimiter = rateLimit({ + ...baseRateLimitOptions, + limit: 3, + message: 'Too many attempts without valid login', +}); + + +export function cleanup() { + const oneHourAgo = Date.now() - 60 * 60 * 1000; + for (const ip in ipsThatReachedLimit) { + if (ipsThatReachedLimit[ip].time < oneHourAgo) { + delete ipsThatReachedLimit[ip]; + } + } +} \ No newline at end of file diff --git a/src/middleware/logged-in.ts b/src/middleware/logged-in.ts new file mode 100644 index 0000000..f6546ba --- /dev/null +++ b/src/middleware/logged-in.ts @@ -0,0 +1,13 @@ +import { Request, Response, NextFunction } from 'express'; +import { validateJWT } from '@src/scripts/token'; +import { create as createError } from '@src/middleware/error'; + + +export function isLoggedIn(req: Request, res: Response, next: NextFunction) { + const result = validateJWT(req); + if (!result.success) { + createError(res, result.status, result.message || "", next) + } else { + next(); + } +} diff --git a/src/models/entry.ts b/src/models/entry.ts index 73b7d29..e7fc107 100644 --- a/src/models/entry.ts +++ b/src/models/entry.ts @@ -1,47 +1,96 @@ -import { Request, Response} from 'express'; +import { NextFunction, Request, Response } from 'express'; import { checkExact, query } from 'express-validator'; -import { crypt } from '@src/scripts/crypt'; +import { compare } from '@src/scripts/crypt'; +import { create as createError } from '@src/middleware/error'; +import * as file from '@src/scripts/file'; +import { getTime } from '@src/scripts/time'; +import { getSpeed } from '@src/scripts/speed'; +import { getDistance } from '@src/scripts/distance'; +import { getAngle } from '@src/scripts/angle'; +import { getIgnore } from '@src/scripts/ignore'; +import logger from '@src/scripts/logger'; + export const entry = { - create: (req:Request, res:Response) => { - console.log(req.query); - console.log(res); - }, - validate: [ - query('user').isLength({ min: 2, max: 2 }), - query('lat').custom(checkNumber(-90, 90)), - query('lon').custom(checkNumber(-180, 180)), - query('timestamp').custom(checkTime), - query('hdop').custom(checkNumber(0, 100)), - query('altitude').custom(checkNumber(0, 10000)), - query('speed').custom(checkNumber(0, 300)), - query('heading').custom(checkNumber(0, 360)), + create: async (req: Request, res: Response, next: NextFunction) => { + const fileObj: File.Obj = file.getFile(res, next); + fileObj.content = await file.readAsJson(res, fileObj.path, next); + + if (!fileObj.content?.entries) { + return createError(res, 500, "File Content unavailable: " + fileObj.path, next); + } + const entries = fileObj.content.entries; + const lastEntry = fileObj.content.entries.at(-1); + const entry = {} as Models.IEntry; + + entry.altitude = Number(req.query.altitude); + entry.hdop = Number(req.query.hdop); + entry.heading = Number(req.query.heading); + entry.index = entries.length; + entry.lat = Number(req.query.lat); + entry.lon = Number(req.query.lon); + entry.user = req.query.user as string; + entry.ignore = false; + + if (lastEntry) { // so there is a previous entry + entry.time = getTime(Number(req.query.timestamp), lastEntry); + lastEntry.ignore = getIgnore(lastEntry, entry); + entry.angle = getAngle(lastEntry, entry); + entry.distance = getDistance(entry, lastEntry) + entry.speed = getSpeed(Number(req.query.speed), entry); + } else { + entry.angle = undefined; + entry.time = getTime(Number(req.query.timestamp)); + entry.speed = getSpeed(Number(req.query.speed)) + } + + if (entries.length >= 1000) { + logger.log(`File over 1000 lines: ${fileObj.path}`); + if (entry.hdop < 12 || (lastEntry && entry.hdop < lastEntry.hdop)) { + entries[entries.length - 1] = entry; // replace last entry + } + } else { + entries.push(entry); + } + + file.write(res, fileObj, next); + + }, + validate: [ + query('user').isLength({ min: 2, max: 2 }), + query('lat').custom(checkNumber(-90, 90)), + query('lon').custom(checkNumber(-180, 180)), + query('timestamp').custom(checkTime), + query('hdop').custom(checkNumber(0, 100)), + query('altitude').custom(checkNumber(0, 10000)), + query('speed').custom(checkNumber(0, 300)), + query('heading').custom(checkNumber(0, 360, "integer")), query("key").custom(checkKey), - checkExact() + checkExact() // INFO: if message or any string gets added remember to escape - ] + ] } -export function checkNumber(min:number, max:number) { - return (value:string) => { +export function checkNumber(min: number, max: number, type: string = "float") { + return (value: string) => { if (!value) { throw new Error('is required'); } - if (value.length > 12) { - throw new Error('Should have a maximum of 11 digits'); - } - - const number = parseFloat(value); + if (value.length > 12) { + throw new Error('Should have a maximum of 11 digits'); + } + + const number = type == "float" ? parseFloat(value) : parseInt(value); if (isNaN(number) || number < min || number > max) { throw new Error(`Value should be between ${min} and ${max}`); } return true; - }; + }; } -export function checkTime(value:string) { +export function checkTime(value: string) { const timestamp = parseFloat(value); - + // Check if it's a number if (isNaN(timestamp)) { throw new Error('Timestamp should be a number'); @@ -53,39 +102,29 @@ export function checkTime(value:string) { throw new Error('Timestamp should represent a valid date'); } - if (process.env.NODE_ENV == "development") { - return true; // dev testing convenience - } - const now = new Date(); const difference = now.getTime() - date.getTime(); const oneDayInMilliseconds = 24 * 60 * 60 * 1000; if (Math.abs(difference) >= oneDayInMilliseconds) { throw new Error('Timestamp should represent a date not further from server time than 1 day'); } - + return true } -function checkKey(value:string) { - if (process.env.NODE_ENV != "production" && value == "test") { - return true; // dev testing convenience - } - if (!value) { - throw new Error('Key required'); +async function checkKey(value: string) { + if (!value) { throw new Error('Key required'); } + if (!process.env.KEYB) { throw new Error('Configuration wrong'); } + if (process.env.NODE_ENV != "production" && value == "test") { + return true; // dev testing convenience } - value = decodeURIComponent(value); - - const hash = crypt(value); + const result = await compare(decodeURIComponent(value), process.env.KEYB); - if (process.env.KEYB != hash) { - if (process.env.NODE_ENV == "development") { - console.log(hash); - } + if (!result) { throw new Error('Key does not match'); } return true; -} \ No newline at end of file +} diff --git a/src/scripts/angle.ts b/src/scripts/angle.ts new file mode 100644 index 0000000..e6ea972 --- /dev/null +++ b/src/scripts/angle.ts @@ -0,0 +1,9 @@ +export function getAngle(lastEntry: Models.IEntry, entry: Models.IEntry): number { + const dLon = (entry.lon - lastEntry.lon) * Math.PI / 180; + const lat1 = lastEntry.lat * Math.PI / 180; + const lat2 = entry.lat * Math.PI / 180; + const y = Math.sin(dLon) * Math.cos(lat2); + const x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon); + const angle = (Math.atan2(y, x) * 180 / Math.PI + 360) % 360; + return angle; +} \ No newline at end of file diff --git a/src/scripts/crypt.ts b/src/scripts/crypt.ts index b14ef42..6bef0df 100644 --- a/src/scripts/crypt.ts +++ b/src/scripts/crypt.ts @@ -1,9 +1,18 @@ -import * as crypto from 'crypto'; +import * as bcrypt from "bcrypt"; +import crypto from "crypto"; -export const crypt = function (value:string) { +export const crypt = async function (password: string, quick = false) { + const extendedPassword = pepper(password); + return await bcrypt.hash(extendedPassword, quick ? 8 : 16); +}; + +export const compare = async function (password: string, hash: string) { + const extendedPassword = pepper(password); + return await bcrypt.compare(extendedPassword, hash) +} + +function pepper(password: string) { const key = process.env.KEYA; - if (!key) { - throw new Error('KEYA is not defined in the environment variables'); - } - return crypto.createHmac('sha256', key).update(value).digest("base64"); -}; \ No newline at end of file + if (!key) { throw new Error('KEYA is not defined in the environment variables'); } + return password + crypto.createHmac('sha256', key).digest("base64"); +} diff --git a/src/scripts/distance.ts b/src/scripts/distance.ts new file mode 100644 index 0000000..36c4637 --- /dev/null +++ b/src/scripts/distance.ts @@ -0,0 +1,30 @@ +export function getDistance(entry: Models.IEntry, lastEntry: Models.IEntry): Models.IDistance { + const horizontal = calculateDistance({ lat: entry.lat, lon: entry.lon }, { lat: lastEntry.lat, lon: lastEntry.lon }); + const vertical = entry.altitude - lastEntry.altitude; + const total = Math.sqrt(horizontal * horizontal + vertical * vertical); + + return { + horizontal: horizontal, + vertical: vertical, + total: total + } +} + +function toRad(x: number): number { + return x * Math.PI / 180; +} + +function calculateDistance(coord1: { lat: number, lon: number }, coord2: { lat: number, lon: number }): number { + const R = 6371000; // radius of the Earth in meters + const dLat = toRad(coord2.lat - coord1.lat); + const dLon = toRad(coord2.lon - coord1.lon); + + const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + + Math.cos(toRad(coord1.lat)) * Math.cos(toRad(coord2.lat)) * + Math.sin(dLon / 2) * Math.sin(dLon / 2); + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + + const distance = R * c; + + return distance; +} \ No newline at end of file diff --git a/src/scripts/file.ts b/src/scripts/file.ts new file mode 100644 index 0000000..a8b693b --- /dev/null +++ b/src/scripts/file.ts @@ -0,0 +1,62 @@ +import fs from 'fs'; +import path from 'path'; +import { promisify } from 'util'; +import { create as createError } from '@src/middleware/error'; +import { NextFunction, Response } from 'express'; +import logger from '@src/scripts/logger'; + +export const getFile = (res: Response, next: NextFunction): File.Obj => { + const date = new Date(); + const formattedDate = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`; + const dirPath = path.resolve(__dirname, '../data'); + const filePath = path.resolve(dirPath, `data-${formattedDate}.json`); + + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }); + logger.log("data folder did not exist, but created now"); + } + + let fileExisted = true; + if (!fs.existsSync(filePath)) { // check if file exist + fileExisted = false; + try { + fs.writeFileSync(filePath, '{"entries": []}'); + logger.log(`file: ${filePath} did not exist, but created now`); + } catch (err) { + createError(res, 500, "File cannot be written to", next); + } + } + + return { path: filePath, content: fileExisted ? undefined : JSON.parse('{"entries": []}') }; // if the file did not exist before, the content is emptyString +}; + + +const readFileAsync = promisify(fs.readFile); + +export async function readAsJson(res: Response, filePath: string, next: NextFunction): Promise { + const data = await readFileAsync(filePath, 'utf-8'); + + try { + return JSON.parse(data); + } catch (err) { + createError(res, 500, "File contains wrong content: " + filePath, next); + } +} + + +export const write = (res:Response, fileObj:File.Obj, next: NextFunction) => { + + if (!fs.existsSync(fileObj.path)) { // check if file exist + createError(res, 500, "Can not write to file that does not exist: " + fileObj.path, next); + } + try { + const content = JSON.stringify(fileObj.content, undefined, 2); + fs.writeFileSync(fileObj.path, content); + fileObj.content = JSON.parse(content); + logger.log(`written to file: ${fileObj.path} ${fileObj.content ? fileObj.content?.entries.length - 1 : ''}`); + } catch (err) { + createError(res, 500, `File (${fileObj.path}) cannot be written to`, next); + } + + return fileObj; // if the file did not exist before, the content is emptyString +}; diff --git a/src/scripts/ignore.ts b/src/scripts/ignore.ts new file mode 100644 index 0000000..3e92857 --- /dev/null +++ b/src/scripts/ignore.ts @@ -0,0 +1,13 @@ +export function getIgnore(lastEntry: Models.IEntry, entry: Models.IEntry): boolean { + let threshold = 6; // hdop not allowed to be higher + const maxThreshold = 25; + + const timing = Math.max(lastEntry.time.diff, entry.time.diff) + + // Threshold increases with older previous entries or farther future entries. + if (timing > 32) { + threshold += Math.min(lastEntry.time.diff / 60, maxThreshold); + } + + return lastEntry.hdop > threshold; +} \ No newline at end of file diff --git a/src/scripts/logger.ts b/src/scripts/logger.ts index b58f366..ddd608a 100644 --- a/src/scripts/logger.ts +++ b/src/scripts/logger.ts @@ -1,17 +1,51 @@ // primitive text logger import fs from 'fs'; -import path from 'path'; +import path from 'path'; +import chalk from "chalk"; + +const dirPath = path.resolve(__dirname, '../httpdocs/log'); +const logPath = path.resolve(dirPath, 'start.txt'); + +if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }); +} -const logPath = path.resolve(__dirname, '../httpdocs', 'log.txt'); const date = new Date().toLocaleString('de-DE', { hour12: false }); export default { - log: (message:string|JSON) => { + log: (message: string | JSON, showDateInConsole: boolean = false) => { + message = JSON.stringify(message); fs.appendFileSync(logPath, `${date} \t|\t ${message} \n`); - console.log(message); - }, - error: (message:string|JSON|Response.Error) => { - fs.appendFileSync(logPath, `${date} \t|\t ERROR: ${message} \n`); - console.error(message); + if (showDateInConsole) { + message = `${chalk.dim(date + ":")} ${message}`; + } + if (process.env.NODE_ENV != "production") { + console.log(message); + } }, -} \ No newline at end of file + error: (content: string | Response.Error) => { + // logfile + const applyErrorPrefix = !/^\[\w+\]/.test(typeof content == "string" ? content : content.message); + const logMessageTemplate = `${date} \t|\t${applyErrorPrefix ? ' [ERROR]' : ''} ${typeof content == "string" ? content : JSON.stringify(content.message) } \n`; + fs.appendFileSync(logPath, logMessageTemplate); + if (process.env.NODE_ENV == "production") { return; } + + // console + if (typeof content != "string" && Object.hasOwnProperty.call(content, "message")) { + const messageAsString = JSON.stringify(content.message); + if (content.stack) { // replace redundant information + content.stack = content.stack.replace(messageAsString, ""); + } + const consoleMessage = structuredClone(content); // create clone so response output is not "further" affected + consoleMessage.message = messageAsString; // gitbash output improvement (w/o objects in arrays appear as [Object]) + content = consoleMessage; + } else if (typeof content == "string") { + const prefix = content.match(/^\[\w+\]/); + if (prefix?.length) { + content = content.replace(prefix[0], chalk.red(prefix[0])); + } + } + console.error(content); + + } +} diff --git a/src/scripts/speed.ts b/src/scripts/speed.ts new file mode 100644 index 0000000..b0591c0 --- /dev/null +++ b/src/scripts/speed.ts @@ -0,0 +1,19 @@ +export function getSpeed(speed: number, entry?: Models.IEntry): Models.ISpeed { + const gps = speed; + let horizontal; + let vertical; + let total; + + if (entry) { + horizontal = entry.distance.horizontal / entry.time.diff; + vertical = entry.distance.vertical / entry.time.diff; + total = entry.distance.total / entry.time.diff; + } + + return { + gps: gps, + horizontal: horizontal, + vertical: vertical, + total: total + } +} \ No newline at end of file diff --git a/src/scripts/time.ts b/src/scripts/time.ts index e69de29..f6adbc7 100644 --- a/src/scripts/time.ts +++ b/src/scripts/time.ts @@ -0,0 +1,34 @@ +import logger from '@src/scripts/logger'; + +export function getTime(time: number, entry?: Models.IEntry): Models.ITime { + const now = new Date(); + const created = Number(time); + const recieved = now.getTime(); + const uploadDuration = (recieved - created) / 1000; + const createdString = now.toLocaleString("de-DE", { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric", + hour: '2-digit', + hour12: false, + minute: '2-digit', + second: '2-digit' + }); + const diff = entry ? (created - entry.time.created) / 1000 : undefined; + + if (uploadDuration < 0) { + logger.error(`upload Duration is negative: ${createdString}, index: ${entry ? entry.index + 1 : 0}`); + } + if (entry && entry.time.created > created) { // maybe this could happend due to the async nature, but due to uncertainty logging is enabled + logger.error(`previous timestamp is more recent: ${createdString}, index: ${entry?.index + 1}`); + } + + return { + created: created, + recieved: recieved, + uploadDuration: uploadDuration, + diff: diff, + createdString: createdString + } +} \ No newline at end of file diff --git a/src/scripts/token.ts b/src/scripts/token.ts new file mode 100644 index 0000000..f9f4d7a --- /dev/null +++ b/src/scripts/token.ts @@ -0,0 +1,87 @@ +import jwt from 'jsonwebtoken'; +import { NextFunction, Request, Response } from 'express'; +import crypto from 'crypto'; +import { create as createError } from '@src/middleware/error'; + + +const csrfTokens: Set = new Set(); + +export function createCSRF(res: Response, next: NextFunction): string { + if (csrfTokens.size > 100) { // Max Number of Tokens in memory + res.set('Retry-After', '300'); // 5 minutes + createError(res, 503, "Too many tokens", next); + } + + const token = crypto.randomBytes(16).toString('hex'); + const expiry = Date.now() + (5 * 60 * 1000); // Token expires in 5 minutes + const csrfToken: CSRFToken = { token, expiry }; + csrfTokens.add(csrfToken); + + return token; +} + +export function validateCSRF(token: string): boolean { + const currentTime = Date.now(); + let valid: boolean = false; + for (const entry of csrfTokens) { + if (entry.token === token) { + valid = entry.expiry > currentTime; + csrfTokens.delete(entry); + } + } + + return valid; +} + +export function cleanupCSRF() { + const currentTime = Date.now(); + for (const entry of csrfTokens) { + if (entry.expiry < currentTime) { + csrfTokens.delete(entry); + } + } +} + +export function validateJWT(req: Request) { + const key = process.env.KEYA; + const header = req.header('Authorization'); + const [type, token] = header ? header.split(' ') : ""; + let payload: string | jwt.JwtPayload = ""; + + // Guard; aka early return for common failures before verifying authorization + if (!key) { return { success: false, status: 500, message: 'Wrong Configuration' }; } + if (!header) { return { success: false, status: 401, message: 'No Authorization header' }; } + if (type !== 'Bearer' || !token) { return { success: false, status: 400, message: 'Invalid Authorization header' }; } + + try { + payload = jwt.verify(token, key); + } catch (err) { + let message = "could not verify"; + if (err instanceof Error) { + message = `${err.name} - ${err.message}`; + } + + return { success: false, status: 403, message: message }; + } + + // don't allow test user in production environment + if (typeof payload == "object" && payload.user == "TEST" && process.env.NODE_ENV == "production") { + return { success: false, status: 403, message: 'test user not allowed on production' }; + } + + return { success: true }; +} + +export function createJWT(req: Request, res: Response) { + const key = process.env.KEYA; + if (!key) { throw new Error('Configuration is wrong'); } + const today = new Date(); + const dateString = today.toLocaleDateString("de-DE", { weekday: "short", year: 'numeric', month: 'numeric', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' }); + const payload = { + date: dateString, + user: req.body.user + }; + const token = jwt.sign(payload, key, { expiresIn: 60 * 2 }); + res.locals.token = token; + return token; +} diff --git a/src/tests/app.test.ts b/src/tests/app.test.ts new file mode 100644 index 0000000..44ba08d --- /dev/null +++ b/src/tests/app.test.ts @@ -0,0 +1,42 @@ +import axios from 'axios'; +import qs from 'qs'; + +// random data of 0.75Kb pre GZIP +const randomData = qs.stringify({ + randomData: 'zIakHvSaXDdLtaPaL02LhGr4Fk6hzXF7tELeR733YZyyye1fnjNzrSlHgqcHU8BKqvE5Mi4B7iHIEdqjTelpoWyaqXqX8l6LzOvROAkTF4lrLXLD1oMHwDL9hnjR0P7g0BB2DqagKkoEYD4TmXeAXT9PbevbirWnOEzmIgSv65SlsNTRFYhmzWl93twXEBNclHTCTnZpf6diWoo8FsXZR49pe9v8J1paalh2LlbNF4ZUxMxNpSvSTRHxvkYo0TMpd0NqUSSLduLIWcE1jhCWnmHhsbohDZjFfMhVS8IFvCiu7rxfuWgwMPqD9FcBR79eqJBy2tjDMqA9S1k9k50AkbOQ6USVfEuqOtocqXonTvC3Jml90KYSs0gX4SSTFHofpMtbWIdkuKqZbitQjsPSBpTx27dhFZd8zT4erdE1ltHnq83pjEj9hQYqatmdzQGYnOyh9YDt8i1IJpk4DX83DLzw3QhaFPgZFq98SOj4ILytmBMIqOtD464aF8PKGq6g7dVqYOtyF2FwyY0xgA7LjGaFzaCDjnGEcPIMRc2tcorsuRPKUI0zcde1gYPsn4WKaKUp87hJd1YtorzCXPfvivfGGL5v1XaSzApc9BbZpbxcpTOi4Pgvx7hNafUcaCr6kcjp4JVYSktnnGCwEplgGEF8uCELsEBUi9LNhgsnwgoRh55TaJfcaFfGfYLokXYEgiyOwYhhdEY3kfjHZWAyFS4owCR6nMJGOGMHrQi1fBefdp28PQGwgELix5Vf8j6P' +}); + +describe('Server Status', () => { + it('The server is running', async () => { + let serverStatus; + try { + const response = await axios.get('http://localhost:80/'); + serverStatus = response.status; + } catch (error) { + console.error(error); + } + + expect(serverStatus).toBe(200); + }) + + it('server is ignoring body on GET requests', async () => { + let serverStatus; + try { + const response = await axios.request({ + url: 'http://localhost:80/', + method: 'GET', + data: randomData, + }); + serverStatus = response.status; + } catch (error) { + console.error(error); + } + + expect(serverStatus).toBe(200); + }) + +}) + + + + diff --git a/src/tests/integration.test.ts b/src/tests/integration.test.ts new file mode 100644 index 0000000..067d3ea --- /dev/null +++ b/src/tests/integration.test.ts @@ -0,0 +1,325 @@ +import axios, { AxiosError } from 'axios'; +import qs from 'qs'; +import fs from "fs"; +import path from "path"; + +async function callServer(timestamp = new Date().getTime(), query: string, expectStatus: number = 200, method: string = "HEAD") { + const url = new URL("http://localhost:80/write?"); + url.search = "?" + query; + const params = new URLSearchParams(url.search); + params.set("timestamp", timestamp.toString()); + url.search = params.toString(); + + + let response; + if (expectStatus == 200) { + if (method == "GET") { + response = await axios.get(url.toString()); + } else { + response = await axios.head(url.toString()); + } + expect(response.status).toBe(expectStatus); + } else { + try { + await axios.head(url.toString()); + } catch (error) { + const axiosError = error as AxiosError; + if (axiosError.response) { + expect(axiosError.response.status).toBe(expectStatus); + } else { + console.error(axiosError); + } + } + } +} + + +function getData(filePath: string) { + const data = fs.readFileSync(filePath); + return JSON.parse(data.toString()); +} + +function isInRange(actual: string | number, expected: number, range: number) { + return Math.abs(Number(actual) - expected) <= range; +} + +async function verifiedRequest(url: string, token: string) { + const response = await axios({ + method: 'get', + url: url, + headers: { + 'Authorization': `Bearer ${token}`, + } + }); + return response; +} + + + +describe('HEAD /write', () => { + // eslint-disable-next-line jest/expect-expect + it('with all parameters correctly set it should succeed', async () => { + await callServer(undefined, "user=xx&lat=45.000&lon=90.000×tamp=R3Pl4C3&hdop=50.0&altitude=5000.000&speed=150.000&heading=180.0&key=test", 200); + }); + + // eslint-disable-next-line jest/expect-expect + it('without key it sends 403', async () => { + await callServer(undefined, "user=xx&lat=45.000&lon=90.000×tamp=R3Pl4C3&hdop=50.0&altitude=5000.000&speed=150.000&heading=180.0", 403); + }); + + // eslint-disable-next-line jest/expect-expect + it('with user length not equal to 2 it sends 422', async () => { + await callServer(undefined, "user=x&lat=45.000&lon=90.000×tamp=R3Pl4C3&hdop=50.0&altitude=5000.000&speed=150.000&heading=180.0&key=test", 422); + }); + + // eslint-disable-next-line jest/expect-expect + it('with lat not between -90 and 90 it sends 422', async () => { + await callServer(undefined, "user=xx&lat=91.000&lon=90.000×tamp=R3Pl4C3&hdop=50.0&altitude=5000.000&speed=150.000&heading=180.0&key=test", 422); + }); + + // eslint-disable-next-line jest/expect-expect + it('with lon not between -180 and 180 it sends 422', async () => { + await callServer(undefined, "user=xx&lat=45.000&lon=181.000×tamp=R3Pl4C3&hdop=50.0&altitude=5000.000&speed=150.000&heading=180.0&key=test", 422); + }); + + // eslint-disable-next-line jest/expect-expect + it('with timestamp to old sends 422', async () => { + const timestamp = new Date().getTime() - 24 * 60 * 60 * 1000 * 2; // two days ago + await callServer(timestamp, "user=xx&lat=45.000&lon=90.000×tamp=R3Pl4C3&hdop=50.0&altitude=5000.000&speed=150.000&heading=180.0&key=test", 422); + }) + + // eslint-disable-next-line jest/expect-expect + it('with hdop not between 0 and 100 it sends 422', async () => { + await callServer(undefined, "user=xx&lat=45.000&lon=90.000×tamp=R3Pl4C3&hdop=101.0&altitude=5000.000&speed=150.000&heading=180.0&key=test", 422); + }); + + // eslint-disable-next-line jest/expect-expect + it('with altitude not between 0 and 10000 it sends 422', async () => { + await callServer(undefined, "user=xx&lat=45.000&lon=90.000×tamp=R3Pl4C3&hdop=50.0&altitude=10001.000&speed=150.000&heading=180.0&key=test", 422); + }); + + // eslint-disable-next-line jest/expect-expect + it('with speed not between 0 and 300 it sends 422', async () => { + await callServer(undefined, "user=xx&lat=45.000&lon=90.000×tamp=R3Pl4C3&hdop=50.0&altitude=5000.000&speed=301.000&heading=180.0&key=test", 422); + }); + + // eslint-disable-next-line jest/expect-expect + it('with heading not between 0 and 360 it sends 422', async () => { + await callServer(undefined, "user=xx&lat=45.000&lon=90.000×tamp=R3Pl4C3&hdop=50.0&altitude=5000.000&speed=150.000&heading=361.0&key=test", 422); + }); +}); + + +describe("GET /write", () => { + const date = new Date(); + const formattedDate = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`; + const dirPath = path.resolve(__dirname, '../../dist/data/'); + const filePath = path.resolve(dirPath, `data-${formattedDate}.json`); + + it('there should a file of the current date', async () => { + await callServer(undefined, "user=xx&lat=52.51451&lon=13.35105×tamp=R3Pl4C3&hdop=20.0&altitude=5000.000&speed=150.000&heading=180.0&key=test", 200, "GET"); + + fs.access(filePath, fs.constants.F_OK, (err) => { + expect(err).toBeFalsy(); + }); + }); + + it('the file contains valid JSON', async () => { + fs.readFile(filePath, 'utf8', (err, data) => { + expect(err).toBeFalsy(); + try { + JSON.parse(data); + } catch (e) { + expect(e).toBeFalsy(); + } + }); + }); + + it('after second call and the JSON entries length is 2', () => { + return new Promise(done => { + setTimeout(async () => { + await await callServer(undefined, "user=xx&lat=52.51627&lon=13.37770×tamp=R3Pl4C3&hdop=50&altitude=4000.000&speed=150.000&heading=180.0&key=test", 200, "GET"); + const jsonData = getData(filePath); + + expect(jsonData.entries.length).toBe(2); + + done(); + }, 3500); + }) + }); + + it('the time is correct', () => { + const jsonData = getData(filePath); + const entry = jsonData.entries.at(-1) + + expect(entry.time.created).toBeGreaterThan(date.getTime()); + expect(entry.time.diff).toBeGreaterThan(3.5); + expect(entry.time.diff).toBeLessThan(4.6); + + + const germanDayPattern = "(Montag|Dienstag|Mittwoch|Donnerstag|Freitag|Samstag|Sonntag)"; + const dayOfMonthPattern = "(0?[1-9]|[12][0-9]|3[01])"; + const germanMonthPattern = "(Januar|Februar|März|April|Mai|Juni|Juli|August|September|Oktober|November|Dezember)"; + const yearPattern = "(\\d{4})"; + const timePattern = "([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]"; + const pattern = new RegExp(`^${germanDayPattern}, ${dayOfMonthPattern}. ${germanMonthPattern} ${yearPattern} um ${timePattern}$`); + const string = entry.time.createdString; + expect(pattern.test(string)).toBeTruthy(); + + }); + + it('the distance is correct', () => { + const jsonData = getData(filePath); + const entry = jsonData.entries.at(-1) + + expect(entry.distance.horizontal).toBeCloseTo(1813.926); + expect(entry.distance.vertical).toBe(-1000); + expect(entry.distance.total).toBeCloseTo(2071.311); + }); + + it('the angle is correct', () => { + const jsonData = getData(filePath); + const entry = jsonData.entries.at(-1) + + expect(entry.angle).toBeCloseTo(83.795775); + }); + + it('the speed is correct', () => { + const jsonData = getData(filePath); + const entry = jsonData.entries.at(-1) + + expect(isInRange(entry.speed.horizontal, 515, 10)).toBe(true); + expect(isInRange(entry.speed.vertical, -284, 10)).toBe(true); + expect(isInRange(entry.speed.total, 588, 15)).toBe(true); + }); + + it('check ignore', async () => { + let jsonData = getData(filePath); + let entry = jsonData.entries[1]; + const lastEntry = jsonData.entries[0]; + + expect(entry.ignore).toBe(false); // current one to be false allways + expect(lastEntry.ignore).toBe(true); // last one to high hdop to be true + + await callServer(undefined, "user=xx&lat=52.51627&lon=13.37770×tamp=R3Pl4C3&hdop=50&altitude=4000.000&speed=150.000&heading=180.0&key=test", 200, "GET"); + jsonData = getData(filePath); + entry = jsonData.entries[1]; // same data point, but not last now therefore ignore true + expect(entry.ignore).toBe(true); + }); +}); + + +describe('API calls', () => { + test(`1000 api calls`, async () => { + for (let i = 0; i < 1000; i++) { + const url = `http://localhost:80/write?user=xx&lat=${(52 + Math.random()).toFixed(3)}&lon=${(13 + Math.random()).toFixed(3)}×tamp=${new Date().getTime()}&hdop=${(25 * Math.random()).toFixed(3)}&altitude=${i}&speed=88.888&heading=${(360 * Math.random()).toFixed(3)}&key=test`; + const response = await axios.get(url); + expect(response.status).toBe(200); + } + }, 20000); // adjust this to to fit your setup + + test(`length of json should not exceed 1000`, async () => { + const date = new Date(); + const formattedDate = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`; + const dirPath = path.resolve(__dirname, '../../dist/data/'); + const filePath = path.resolve(dirPath, `data-${formattedDate}.json`); + const jsonData = getData(filePath); + expect(jsonData.entries.length).toBeLessThanOrEqual(1000); + }); +}); + + +describe('read and login', () => { + let token = ""; + const testData = { + user: "TEST", + password: "test", + csrfToken: "" + } + + it('form available / get Token', async () => { + let response = {data:""}; + try { + response = await axios.get('http://localhost:80/login'); + } catch (error) { + console.error(error); + } + + const regex = /name="csrfToken" value="([^"]*)"/; + const match = response.data.match(regex); + testData.csrfToken = match ? match[1] : '-'; + }) + + test(`redirect without logged in`, async () => { + try { + await axios.get("http://localhost:80/read/"); + } catch (error) { + const axiosError = error as AxiosError; + if (axiosError.response) { + expect(axiosError.response.status).toBe(401); + } else { + console.error(axiosError); + } + } + }); + + it('test user can login', async () => { + const response = await axios.post('http://localhost:80/login', qs.stringify(testData)); + + expect(response.status).toBe(200); + expect(response.headers['content-type']).toEqual(expect.stringContaining('application/json')); + expect(response).toHaveProperty('data.token'); + expect(response.data.token).not.toBeNull(); + token = response.data.token; + }) + + test('wrong token get error', async () => { + try { + await verifiedRequest("http://localhost:80/read?index=0", "justWrongValue"); + } catch (error) { + const axiosError = error as AxiosError; + if (axiosError.response) { + expect(axiosError.response.status).toBe(403); + } else { + console.error(axiosError); + } + } + }); + + test('verified request returns json', async () => { + const response = await verifiedRequest("http://localhost:80/read?index=0", token); + expect(response.status).toBe(200); + expect(response.headers['content-type']).toEqual(expect.stringContaining('application/json')); + }); + + test(`index parameter to long`, async () => { + try { + await verifiedRequest("http://localhost:80/read?index=1234", token); + } catch (error) { + const axiosError = error as AxiosError; + if (axiosError.response) { + expect(axiosError.response.status).toBe(400); + } else { + console.error(axiosError); + } + } + }); + + test(`index parameter to be a number`, async () => { + try { + await verifiedRequest("http://localhost:80/read?index=a9", token); + } catch (error) { + const axiosError = error as AxiosError; + if (axiosError.response) { + expect(axiosError.response.status).toBe(400); + } else { + console.error(axiosError); + } + } + }); + test(`index parameter reduces length of json`, async () => { + const response = await verifiedRequest("http://localhost:80/read?index=999", token); + expect(response.status).toBe(200); + expect(response.data.entries.length).toBe(1); + }); +}); \ No newline at end of file diff --git a/src/tests/login.test.ts b/src/tests/login.test.ts new file mode 100644 index 0000000..e177826 --- /dev/null +++ b/src/tests/login.test.ts @@ -0,0 +1,94 @@ +import axios, { AxiosError } from 'axios'; +import qs from 'qs'; + +const userDataLarge = qs.stringify({ + user: "user", + password: "pass", + kilobyte: 'BPSwVu5vcvhWB17HcfIdyQK83mHJZKChv7zDihBJoifWK9EJFzK7VYf3kUgIqkc0io8DnSdewzc9U0GpzodQUFz0KLMaogsJruEbNSKvxnzUxS5UqSR64lLOmGumoPcn2InC0Ebpqfdiw90HFVZVlE3AY6Lhgbx8ILHi55RvpuGefDjBsePgow8Jh9sc8uVMCDglLmHQ0zk3PumMj0KlOszbMmX9fG0pPUsvLLc40biPBv9t97K3BFjYd3fGriRAQ3bFhGHBz2wzGbNQfHjKFDHuSvXOw8KReM7Wwd4Cl02QQ3RnDJVwH6cayh4BqFRXlP3i6uXw0l9qxdTv0q1CtV9rJho6zwo04gkGLvsS3AoYJQtHnOtUDdHPExu7l3nMKnPoRUwl7K2ePfHRuppFGqa43Q49bI04VjEhrB9k5S2uZJoxZdm63rIUrydmkZWdvBLVVZUIXwwIRnwLmoa26htKOz9FPKwWIPOM0NZj4jAoPhKqLDJwziNZn5UupzxBXoUM3BIyEk3K8GXs7eBduH9GCK2z2HPF0fJNtGiHASe7jCOC2mhSC5zGf9k0Yu1Ey63oQQZUtT7L57lp7UzPE2p6wzKDlbJZOn0Ho5OUfq3hE2C8fQRO1M6jDvRTiUIKhhxSHYd75Pvh4SG9lD8w5OHASusLDxmzKBUuG4GrGrQYpd0awJkqnKp5lk7psLD22YTtjTuDgI500tQLXSslxI1kIuB8RnN1LsxHyRQMVtXmNFOKKZV2U2frWpImIz2wSHCYrwRGygwDtiFfwtVwTapjhQqUMyb1vrWWi3EL1Y50fDCjDDHlvLI4N2tr2DULFf3a9m2SYWSoE6CYP4og5YyqjhqFQFm9urREInyZi9L0iQoMYxEqxTjGiVJfKmaSChSd0kQz6z2OdsxFbkMWJ2CAHOL1XNK8iFFSp93fIspaNMIonRVDCj4ZIP1LaPHDmIYcYTNU4k3Uz6VBHSIc1VjiG3sc2MZpKw9An0tJVlWbtVSk2RGYWIANAYyr5pQS' +}); +const userDataWithoutToken = qs.stringify({ + user: "user", + password: "pass" +}); + +let csrfToken = "-"; +const userDataWithToken = { + user: "user", + password: "pass", + csrfToken: "" +}; + +describe('Login', () => { + it('form available', async () => { + let serverStatus = {}; + let response = { data: "", status: "" }; + try { + response = await axios.get('http://localhost:80/login'); + serverStatus = response.status; + } catch (error) { + console.error(error); + } + + expect(serverStatus).toBe(200); + expect(response.data).toContain(' { + try { + await axios.post('http://localhost:80/login', userDataLarge); + } catch (error) { + const axiosError = error as AxiosError; + if (axiosError.response) { + expect(axiosError.response.status).toBe(413); + } else { + console.error(axiosError); + } + } + }) + + it('invalid csrf shows correct error', async () => { + try { + await axios.post('http://localhost:80/login', userDataWithoutToken); + } catch (error) { + const axiosError = error as AxiosError; + if (axiosError.response) { + expect(axiosError.response.status).toBe(403); + if (axiosError.response.data) { + expect(JSON.stringify(axiosError.response.data)).toContain('Invalid CSRF'); + } else { + throw Error("fail"); + } + } else { + console.error(axiosError); + } + } + }) + + + it('test invalid credentials to return error', async () => { + try { + userDataWithToken.csrfToken = csrfToken + await axios.post('http://localhost:80/login', qs.stringify(userDataWithToken)); + } catch (error) { + const axiosError = error as AxiosError; + if (axiosError.response) { + expect(axiosError.response.status).toBe(403); + if (axiosError.response.data) { + expect(JSON.stringify(axiosError.response.data)).toContain('Invalid credentials'); + } else { + throw Error("fail"); + } + } else { + console.error(axiosError); + } + } + }) +}) + + + + diff --git a/src/models/entry.test.ts b/src/tests/unit.test.ts similarity index 93% rename from src/models/entry.test.ts rename to src/tests/unit.test.ts index bd2c0b4..013a1b4 100644 --- a/src/models/entry.test.ts +++ b/src/tests/unit.test.ts @@ -1,7 +1,7 @@ -import { checkNumber, checkTime } from "./entry"; +import { checkNumber, checkTime } from "../models/entry"; -describe("checkNumber", () => { +describe("entry checkNumber", () => { it("should throw error if value is not provided", () => { expect(() => checkNumber(0, 100)("")).toThrow(new Error('is required')); }); @@ -19,7 +19,7 @@ describe("checkNumber", () => { }); }); -describe("checkTime", () => { +describe("entry checkTime", () => { it("should throw error if value is not a number", () => { expect(() => checkTime("abc")).toThrow(new Error('Timestamp should be a number')); }); diff --git a/tsconfig.json b/tsconfig.json index 613bc08..5f168cd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ "target": "ES6", "sourceMap": true, "baseUrl": "./src", + "esModuleInterop": true, "paths": { "@src/*": ["./*"], } diff --git a/types.d.ts b/types.d.ts index b3d3397..3cde1ce 100644 --- a/types.d.ts +++ b/types.d.ts @@ -1,14 +1,18 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ - -type NumericRange = - ARR['length'] extends END ? ACC | START | END : - NumericRange; +namespace RateLimit { + interface obj { + [key: string]: { + limitReachedOnError: boolean, + time: number + } + } +} namespace Response { interface Message { message: string; - data?: string|JSON; + data?: string | JSON; } interface Error extends Response.Message { @@ -17,7 +21,18 @@ namespace Response { status?: number } } +namespace File { + interface Obj { + path: string, + content?: Models.IEntries; + } +} + namespace Models { + interface IEntries { + entries: Models.IEntry[] + } + interface IEntry { /** * height above ground in meters, as received by gps @@ -27,25 +42,17 @@ namespace Models { /** * Direction in degrees between two coordinate pairs: 0°-360° */ - angle: NumericRange<0, 360>, + angle?: number, /** * object containing horizontal vertical and total distance, in meters */ - distance: { - horizontal: number, - vertical: number, - total: number - }, + distance: Models.IDistance, /** * object containing horizontal vertical and total speed, in km/h */ - speeed: { - horizontal: number, - vertical: number, - total: number - }, + speed: Models.ISpeed, /** * index, position of the entry point in the chain @@ -55,13 +62,13 @@ namespace Models { /** * Heading or Bearing as recieved from gps */ - heading: NumericRange<0, 360>, + heading: number, /** * lat */ lat: number, - + /** * lon @@ -82,17 +89,41 @@ namespace Models { /** * time object containing UNIX timestamps with milliseconds, gps creation time (as recieved via gps), server time (when the server recieved and computed it), differce to last entry (time between waypoints), upload time differnce */ - time: { - created: number, - recieved: number, - uploadDuration: number, - diff: number - createdString: string - }, + time: Models.time, /** * user as recieved */ user: string } + + interface ITime { + created: number, + recieved: number, + uploadDuration: number, + diff?: number + createdString: string + } + + interface ISpeed { + gps: number; + horizontal?: number, + vertical?: number, + total?: number + } + interface IDistance { + horizontal: number, + vertical: number, + total: number + } +} + +interface CSRFToken { + token: string; + expiry: number; } + +interface HttpError extends Error { + status?: number; + statusCode?: number; +} \ No newline at end of file diff --git a/views/login-form.ejs b/views/login-form.ejs new file mode 100644 index 0000000..34bf551 --- /dev/null +++ b/views/login-form.ejs @@ -0,0 +1,31 @@ + + + + + + Login Form - Lorex + + + + + + + + + + \ No newline at end of file