diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..3dcd3d268 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,68 @@ +name: Unit Tests & Coverage + +on: + push: + branches: [main, pre-stage, dev] + pull_request: + +jobs: + test-api: + runs-on: ubuntu-latest + defaults: + run: + working-directory: api + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + cache-dependency-path: api/package-lock.json + - run: npm ci + - run: npm install @rollup/rollup-linux-x64-gnu --no-save + - run: npm run test:coverage + - uses: actions/upload-artifact@v4 + if: always() + with: + name: api-coverage-report + path: api/coverage/ + + test-ui: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ui + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + cache-dependency-path: ui/package-lock.json + - run: npm ci --legacy-peer-deps + - run: npm run test:coverage + - uses: actions/upload-artifact@v4 + if: always() + with: + name: ui-coverage-report + path: ui/coverage/ + + test-upload-api: + runs-on: ubuntu-latest + defaults: + run: + working-directory: upload-api + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + cache-dependency-path: upload-api/package-lock.json + - run: npm install --legacy-peer-deps + - run: npm run test:coverage + - uses: actions/upload-artifact@v4 + if: always() + with: + name: upload-api-coverage-report + path: upload-api/coverage/ diff --git a/.gitignore b/.gitignore index 07d1ff8d3..e1b544e6f 100644 --- a/.gitignore +++ b/.gitignore @@ -367,3 +367,6 @@ app.json *MigrationData* *.zip app.json + +# Test coverage (global) +coverage/ diff --git a/api/.gitignore b/api/.gitignore index 736178335..1b83d423e 100644 --- a/api/.gitignore +++ b/api/.gitignore @@ -367,4 +367,5 @@ database/ /migration-data **/copy* **copy.ts -manifest.json \ No newline at end of file +manifest.json +coverage/ \ No newline at end of file diff --git a/api/package-lock.json b/api/package-lock.json index 12fddb887..94908c28c 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -49,17 +49,21 @@ "@types/jsonwebtoken": "^9.0.5", "@types/lodash": "^4.17.0", "@types/node": "^20.10.4", + "@types/supertest": "^6.0.3", "@types/uuid": "^9.0.8", "@types/wordpress__block-library": "^2.6.3", "@types/wordpress__block-serialization-spec-parser": "^3.1.3", "@types/wordpress__blocks": "^12.5.18", "@typescript-eslint/eslint-plugin": "^6.15.0", "@typescript-eslint/parser": "^6.15.0", + "@vitest/coverage-v8": "^4.0.18", "eslint": "^8.56.0", "eslint-config-prettier": "^8.3.0", "prettier": "^2.4.1", + "supertest": "^7.2.2", "tsx": "^4.7.1", - "typescript": "^5.4.3" + "typescript": "^5.4.3", + "vitest": "^4.0.18" } }, "node_modules/@apollo/client": { @@ -292,6 +296,16 @@ "node": ">=6.9.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@colors/colors": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", @@ -1445,50 +1459,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@eslint/js": { "version": "8.57.1", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", @@ -1596,28 +1566,6 @@ "node": ">=10.10.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -1969,25 +1917,6 @@ } } }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz", - "integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", @@ -2033,6 +1962,19 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2187,6 +2129,16 @@ "@otplib/plugin-thirty-two": "^12.0.1" } }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", + "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, "node_modules/@rollup/plugin-commonjs": { "version": "28.0.9", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.9.tgz", @@ -2300,18 +2252,331 @@ } } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", - "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", "cpu": [ "arm64" ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "license": "MIT", "optional": true, "os": [ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@samverschueren/stream-to-observable": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz", @@ -2378,6 +2643,13 @@ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, "node_modules/@tannin/compile": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@tannin/compile/-/compile-1.1.0.tgz", @@ -2410,9 +2682,10 @@ "dev": true }, "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-3.0.1.tgz", + "integrity": "sha512-VyMVKRrpHTT8PnotUeV8L/mDaMwD5DaAKCFLP73zAqAtvF0FCqky+Ki7BYbFCYQmqFyTe9316Ed5zS70QUR9eg==", + "license": "MIT", "engines": { "node": ">= 10" } @@ -2426,6 +2699,17 @@ "@types/node": "*" } }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -2434,6 +2718,13 @@ "@types/node": "*" } }, + "node_modules/@types/cookiejar": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", + "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/cors": { "version": "2.8.19", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", @@ -2442,6 +2733,13 @@ "@types/node": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -2544,6 +2842,13 @@ "integrity": "sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==", "dev": true }, + "node_modules/@types/methods": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", + "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -2648,6 +2953,30 @@ "@types/node": "*" } }, + "node_modules/@types/superagent": { + "version": "8.1.9", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.9.tgz", + "integrity": "sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cookiejar": "^2.1.5", + "@types/methods": "^1.1.4", + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/supertest": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.3.tgz", + "integrity": "sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/methods": "^1.1.4", + "@types/superagent": "^8.1.0" + } + }, "node_modules/@types/tough-cookie": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", @@ -2838,21 +3167,6 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@typescript-eslint/utils": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", @@ -2878,45 +3192,160 @@ "eslint": "^7.0.0 || ^8.0.0" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true + }, + "node_modules/@use-gesture/core": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz", + "integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==", + "dev": true + }, + "node_modules/@use-gesture/react": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz", + "integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==", + "dev": true, + "dependencies": { + "@use-gesture/core": "10.3.1" + }, + "peerDependencies": { + "react": ">= 16.8.0" + } + }, + "node_modules/@vitest/coverage-v8": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.18.tgz", + "integrity": "sha512-7i+N2i0+ME+2JFZhfuz7Tg/FqKtilHjGyGvoHYQ6iLV0zahbsJ9sljC9OcFcPDbhYKCet+sG8SsVqlyGvPflZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.0.18", + "ast-v8-to-istanbul": "^0.3.10", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.1", + "obug": "^2.1.1", + "std-env": "^3.10.0", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.0.18", + "vitest": "4.0.18" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", + "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", + "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz", + "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.18", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", + "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" + "@vitest/pretty-format": "4.0.18", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://opencollective.com/vitest" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true - }, - "node_modules/@use-gesture/core": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz", - "integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==", - "dev": true + "node_modules/@vitest/spy": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz", + "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } }, - "node_modules/@use-gesture/react": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz", - "integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==", + "node_modules/@vitest/utils": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz", + "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==", "dev": true, + "license": "MIT", "dependencies": { - "@use-gesture/core": "10.3.1" + "@vitest/pretty-format": "4.0.18", + "tinyrainbow": "^3.0.3" }, - "peerDependencies": { - "react": ">= 16.8.0" + "funding": { + "url": "https://opencollective.com/vitest" } }, "node_modules/@wordpress/a11y": { @@ -3476,9 +3905,10 @@ } }, "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -3670,6 +4100,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT" + }, "node_modules/assert": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", @@ -3690,6 +4127,45 @@ "node": ">=0.8" } }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-v8-to-istanbul": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.11.tgz", + "integrity": "sha512-Qya9fkoofMjCBNVdWINMjB5KZvkYfaO9/anwkWnjxibpWUxo5iHl2sOdP7/uAqaRuUYuoo8rDwnbaaKVFxoUvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -3779,9 +4255,13 @@ } }, "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==" + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } }, "node_modules/base64-js": { "version": "1.5.1", @@ -3930,11 +4410,15 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/braces": { @@ -4090,6 +4574,16 @@ "cdl": "bin/cdl.js" } }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -4487,18 +4981,22 @@ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/compute-scroll-into-view": { "version": "1.0.20", "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz", "integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==", "dev": true }, - "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 - }, "node_modules/concat-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", @@ -4599,6 +5097,13 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==" }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true, + "license": "MIT" + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -5000,6 +5505,17 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, "node_modules/diff": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", @@ -5420,6 +5936,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -5679,32 +6202,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/eslint/node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -5717,24 +6214,6 @@ "node": ">=10.13.0" } }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -5805,6 +6284,16 @@ "node": ">= 0.6" } }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/express": { "version": "4.22.1", "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", @@ -6029,12 +6518,6 @@ "node": ">=8.6.0" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, "node_modules/fast-levenshtein": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz", @@ -6043,6 +6526,13 @@ "fastest-levenshtein": "^1.0.7" } }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", @@ -6149,17 +6639,6 @@ "minimatch": "^5.0.1" } }, - "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.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -6237,49 +6716,6 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/flat-cache/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/flat-cache/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/flat-cache/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/flat-cache/node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -6355,6 +6791,24 @@ "node": ">= 6" } }, + "node_modules/formidable": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", + "dezalgo": "^1.0.4", + "once": "^1.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -6457,11 +6911,19 @@ "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==" }, - "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 + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, "node_modules/function-bind": { "version": "1.1.2", @@ -6611,6 +7073,23 @@ "safe-buffer": "^5.1.1" } }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -6909,6 +7388,13 @@ "node": ">=18" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, "node_modules/html-to-json-parser": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-to-json-parser/-/html-to-json-parser-2.0.1.tgz", @@ -7088,17 +7574,6 @@ "node": ">=8" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -8152,35 +8627,87 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "engines": { + "node": ">=18" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", "dependencies": { - "is-docker": "^2.0.0" + "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" - }, - "node_modules/isexe": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", - "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" - }, "node_modules/jake": { "version": "10.9.4", "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", @@ -9121,6 +9648,34 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/magicast": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", + "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/marked": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", @@ -9248,23 +9803,25 @@ } }, "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } @@ -9387,6 +9944,25 @@ "node": ">=8.0.0" } }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -9476,9 +10052,9 @@ } }, "node_modules/npm": { - "version": "10.9.4", - "resolved": "https://registry.npmjs.org/npm/-/npm-10.9.4.tgz", - "integrity": "sha512-OnUG836FwboQIbqtefDNlyR0gTHzIfwRfE3DuiNewBvnMnWEpB0VEXwBlFVgqpNzIgYo/MHh3d2Hel/pszapAA==", + "version": "10.9.5", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.9.5.tgz", + "integrity": "sha512-tFABtwt8S5KDs6DKs4p8uQ+u+8Hpx4ReD6bmkrPzPI0hsYkRWIkY/esz6ZtHyHvqVOltTB9DM/812Lx++SIXRw==", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", @@ -9549,6 +10125,7 @@ "which", "write-file-atomic" ], + "license": "Artistic-2.0", "workspaces": [ "docs", "smoke-tests", @@ -9558,24 +10135,24 @@ ], "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^8.0.1", + "@npmcli/arborist": "^8.0.2", "@npmcli/config": "^9.0.0", "@npmcli/fs": "^4.0.0", "@npmcli/map-workspaces": "^4.0.2", "@npmcli/package-json": "^6.2.0", - "@npmcli/promise-spawn": "^8.0.2", + "@npmcli/promise-spawn": "^8.0.3", "@npmcli/redact": "^3.2.2", "@npmcli/run-script": "^9.1.0", "@sigstore/tuf": "^3.1.1", "abbrev": "^3.0.1", "archy": "~1.0.0", "cacache": "^19.0.1", - "chalk": "^5.4.1", - "ci-info": "^4.2.0", + "chalk": "^5.6.2", + "ci-info": "^4.4.0", "cli-columns": "^4.0.0", "fastest-levenshtein": "^1.0.16", "fs-minipass": "^3.0.3", - "glob": "^10.4.5", + "glob": "^10.5.0", "graceful-fs": "^4.2.11", "hosted-git-info": "^8.1.0", "ini": "^5.0.0", @@ -9583,38 +10160,38 @@ "is-cidr": "^5.1.1", "json-parse-even-better-errors": "^4.0.0", "libnpmaccess": "^9.0.0", - "libnpmdiff": "^7.0.1", - "libnpmexec": "^9.0.1", - "libnpmfund": "^6.0.1", + "libnpmdiff": "^7.0.2", + "libnpmexec": "^9.0.2", + "libnpmfund": "^6.0.2", "libnpmhook": "^11.0.0", "libnpmorg": "^7.0.0", - "libnpmpack": "^8.0.1", - "libnpmpublish": "^10.0.1", + "libnpmpack": "^8.0.2", + "libnpmpublish": "^10.0.2", "libnpmsearch": "^8.0.0", "libnpmteam": "^7.0.0", "libnpmversion": "^7.0.0", "make-fetch-happen": "^14.0.3", - "minimatch": "^9.0.5", - "minipass": "^7.1.1", + "minimatch": "^9.0.9", + "minipass": "^7.1.3", "minipass-pipeline": "^1.2.4", "ms": "^2.1.2", - "node-gyp": "^11.2.0", + "node-gyp": "^11.5.0", "nopt": "^8.1.0", - "normalize-package-data": "^7.0.0", + "normalize-package-data": "^7.0.1", "npm-audit-report": "^6.0.0", - "npm-install-checks": "^7.1.1", + "npm-install-checks": "^7.1.2", "npm-package-arg": "^12.0.2", "npm-pick-manifest": "^10.0.0", "npm-profile": "^11.0.1", "npm-registry-fetch": "^18.0.2", "npm-user-validate": "^3.0.0", - "p-map": "^7.0.3", + "p-map": "^7.0.4", "pacote": "^19.0.1", "parse-conflict-json": "^4.0.0", "proc-log": "^5.0.0", "qrcode-terminal": "^0.12.0", "read": "^4.1.0", - "semver": "^7.7.2", + "semver": "^7.7.4", "spdx-expression-parse": "^4.0.0", "ssri": "^12.0.0", "supports-color": "^9.4.0", @@ -9622,7 +10199,7 @@ "text-table": "~0.2.0", "tiny-relative-date": "^1.3.0", "treeverse": "^3.0.0", - "validate-npm-package-name": "^6.0.1", + "validate-npm-package-name": "^6.0.2", "which": "^5.0.0", "write-file-atomic": "^6.0.0" }, @@ -9690,7 +10267,7 @@ } }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", + "version": "6.2.2", "inBundle": true, "license": "MIT", "engines": { @@ -9722,11 +10299,11 @@ } }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", + "version": "7.2.0", "inBundle": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -9767,7 +10344,7 @@ } }, "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "8.0.1", + "version": "8.0.2", "inBundle": true, "license": "ISC", "dependencies": { @@ -9801,6 +10378,7 @@ "proggy": "^3.0.0", "promise-all-reject-late": "^1.0.0", "promise-call-limit": "^3.0.1", + "promise-retry": "^2.0.1", "read-package-json-fast": "^4.0.0", "semver": "^7.3.7", "ssri": "^12.0.0", @@ -9969,7 +10547,7 @@ } }, "node_modules/npm/node_modules/@npmcli/promise-spawn": { - "version": "8.0.2", + "version": "8.0.3", "inBundle": true, "license": "ISC", "dependencies": { @@ -10060,7 +10638,7 @@ } }, "node_modules/npm/node_modules/agent-base": { - "version": "7.1.3", + "version": "7.1.4", "inBundle": true, "license": "MIT", "engines": { @@ -10076,7 +10654,7 @@ } }, "node_modules/npm/node_modules/ansi-styles": { - "version": "6.2.1", + "version": "6.2.3", "inBundle": true, "license": "MIT", "engines": { @@ -10087,7 +10665,7 @@ } }, "node_modules/npm/node_modules/aproba": { - "version": "2.0.0", + "version": "2.1.0", "inBundle": true, "license": "ISC" }, @@ -10165,30 +10743,15 @@ "node": ">=18" } }, - "node_modules/npm/node_modules/cacache/node_modules/mkdirp": { - "version": "3.0.1", - "inBundle": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/npm/node_modules/cacache/node_modules/tar": { - "version": "7.4.3", + "version": "7.5.9", "inBundle": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", + "minizlib": "^3.1.0", "yallist": "^5.0.0" }, "engines": { @@ -10204,7 +10767,7 @@ } }, "node_modules/npm/node_modules/chalk": { - "version": "5.4.1", + "version": "5.6.2", "inBundle": true, "license": "MIT", "engines": { @@ -10223,7 +10786,7 @@ } }, "node_modules/npm/node_modules/ci-info": { - "version": "4.2.0", + "version": "4.4.0", "funding": [ { "type": "github", @@ -10327,7 +10890,7 @@ } }, "node_modules/npm/node_modules/debug": { - "version": "4.4.1", + "version": "4.4.3", "inBundle": true, "license": "MIT", "dependencies": { @@ -10343,7 +10906,7 @@ } }, "node_modules/npm/node_modules/diff": { - "version": "5.2.0", + "version": "5.2.2", "inBundle": true, "license": "BSD-3-Clause", "engines": { @@ -10383,7 +10946,7 @@ "license": "MIT" }, "node_modules/npm/node_modules/exponential-backoff": { - "version": "3.1.2", + "version": "3.1.3", "inBundle": true, "license": "Apache-2.0" }, @@ -10395,6 +10958,22 @@ "node": ">= 4.9.1" } }, + "node_modules/npm/node_modules/fdir": { + "version": "6.5.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/npm/node_modules/foreground-child": { "version": "3.3.1", "inBundle": true, @@ -10422,7 +11001,7 @@ } }, "node_modules/npm/node_modules/glob": { - "version": "10.4.5", + "version": "10.5.0", "inBundle": true, "license": "ISC", "dependencies": { @@ -10542,13 +11121,9 @@ } }, "node_modules/npm/node_modules/ip-address": { - "version": "9.0.5", + "version": "10.1.0", "inBundle": true, "license": "MIT", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, "engines": { "node": ">= 12" } @@ -10604,7 +11179,6 @@ }, "node_modules/npm/node_modules/jsbn": { "version": "1.1.0", - "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/json-parse-even-better-errors": { @@ -10654,11 +11228,11 @@ } }, "node_modules/npm/node_modules/libnpmdiff": { - "version": "7.0.1", + "version": "7.0.2", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^8.0.1", + "@npmcli/arborist": "^8.0.2", "@npmcli/installed-package-contents": "^3.0.0", "binary-extensions": "^2.3.0", "diff": "^5.1.0", @@ -10672,11 +11246,11 @@ } }, "node_modules/npm/node_modules/libnpmexec": { - "version": "9.0.1", + "version": "9.0.2", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^8.0.1", + "@npmcli/arborist": "^8.0.2", "@npmcli/run-script": "^9.0.1", "ci-info": "^4.0.0", "npm-package-arg": "^12.0.0", @@ -10692,11 +11266,11 @@ } }, "node_modules/npm/node_modules/libnpmfund": { - "version": "6.0.1", + "version": "6.0.2", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^8.0.1" + "@npmcli/arborist": "^8.0.2" }, "engines": { "node": "^18.17.0 || >=20.5.0" @@ -10727,11 +11301,11 @@ } }, "node_modules/npm/node_modules/libnpmpack": { - "version": "8.0.1", + "version": "8.0.2", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^8.0.1", + "@npmcli/arborist": "^8.0.2", "@npmcli/run-script": "^9.0.1", "npm-package-arg": "^12.0.0", "pacote": "^19.0.0" @@ -10741,7 +11315,7 @@ } }, "node_modules/npm/node_modules/libnpmpublish": { - "version": "10.0.1", + "version": "10.0.2", "inBundle": true, "license": "ISC", "dependencies": { @@ -10822,20 +11396,12 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/make-fetch-happen/node_modules/negotiator": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/npm/node_modules/minimatch": { - "version": "9.0.5", + "version": "9.0.9", "inBundle": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -10845,9 +11411,9 @@ } }, "node_modules/npm/node_modules/minipass": { - "version": "7.1.2", + "version": "7.1.3", "inBundle": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } @@ -10946,7 +11512,7 @@ } }, "node_modules/npm/node_modules/minizlib": { - "version": "3.0.2", + "version": "3.1.0", "inBundle": true, "license": "MIT", "dependencies": { @@ -10980,8 +11546,16 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/npm/node_modules/negotiator": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/npm/node_modules/node-gyp": { - "version": "11.2.0", + "version": "11.5.0", "inBundle": true, "license": "MIT", "dependencies": { @@ -11011,30 +11585,15 @@ "node": ">=18" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/mkdirp": { - "version": "3.0.1", - "inBundle": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/npm/node_modules/node-gyp/node_modules/tar": { - "version": "7.4.3", + "version": "7.5.9", "inBundle": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", + "minizlib": "^3.1.0", "yallist": "^5.0.0" }, "engines": { @@ -11064,7 +11623,7 @@ } }, "node_modules/npm/node_modules/normalize-package-data": { - "version": "7.0.0", + "version": "7.0.1", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { @@ -11096,7 +11655,7 @@ } }, "node_modules/npm/node_modules/npm-install-checks": { - "version": "7.1.1", + "version": "7.1.2", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { @@ -11192,7 +11751,7 @@ } }, "node_modules/npm/node_modules/p-map": { - "version": "7.0.3", + "version": "7.0.4", "inBundle": true, "license": "MIT", "engines": { @@ -11273,8 +11832,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/npm/node_modules/picomatch": { + "version": "4.0.3", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/npm/node_modules/postcss-selector-parser": { - "version": "7.1.0", + "version": "7.1.1", "inBundle": true, "license": "MIT", "dependencies": { @@ -11393,7 +11963,7 @@ "optional": true }, "node_modules/npm/node_modules/semver": { - "version": "7.7.2", + "version": "7.7.4", "inBundle": true, "license": "ISC", "bin": { @@ -11507,11 +12077,11 @@ } }, "node_modules/npm/node_modules/socks": { - "version": "2.8.5", + "version": "2.8.7", "inBundle": true, "license": "MIT", "dependencies": { - "ip-address": "^9.0.5", + "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" }, "engines": { @@ -11565,13 +12135,12 @@ } }, "node_modules/npm/node_modules/spdx-license-ids": { - "version": "3.0.21", + "version": "3.0.23", "inBundle": true, "license": "CC0-1.0" }, "node_modules/npm/node_modules/sprintf-js": { "version": "1.1.3", - "inBundle": true, "license": "BSD-3-Clause" }, "node_modules/npm/node_modules/ssri": { @@ -11726,12 +12295,12 @@ "license": "MIT" }, "node_modules/npm/node_modules/tinyglobby": { - "version": "0.2.14", + "version": "0.2.15", "inBundle": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -11740,30 +12309,6 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/npm/node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.6", - "inBundle": true, - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/npm/node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/npm/node_modules/treeverse": { "version": "3.0.0", "inBundle": true, @@ -11773,13 +12318,13 @@ } }, "node_modules/npm/node_modules/tuf-js": { - "version": "3.0.1", + "version": "3.1.0", "inBundle": true, "license": "MIT", "dependencies": { "@tufjs/models": "3.0.1", - "debug": "^4.3.6", - "make-fetch-happen": "^14.0.1" + "debug": "^4.4.1", + "make-fetch-happen": "^14.0.3" }, "engines": { "node": "^18.17.0 || >=20.5.0" @@ -11843,7 +12388,7 @@ } }, "node_modules/npm/node_modules/validate-npm-package-name": { - "version": "6.0.1", + "version": "6.0.2", "inBundle": true, "license": "ISC", "engines": { @@ -11870,11 +12415,11 @@ } }, "node_modules/npm/node_modules/which/node_modules/isexe": { - "version": "3.1.1", + "version": "3.1.5", "inBundle": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/npm/node_modules/wrap-ansi": { @@ -11925,7 +12470,7 @@ } }, "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.1.0", + "version": "6.2.2", "inBundle": true, "license": "MIT", "engines": { @@ -11957,11 +12502,11 @@ } }, "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", + "version": "7.2.0", "inBundle": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -12080,6 +12625,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, "node_modules/omit-deep-lodash": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/omit-deep-lodash/-/omit-deep-lodash-1.1.7.tgz", @@ -12457,20 +13013,11 @@ } }, "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/path-key": { @@ -12488,15 +13035,16 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", - "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -12506,6 +13054,7 @@ "version": "11.2.6", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -12519,6 +13068,13 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/php-serialize": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/php-serialize/-/php-serialize-5.1.3.tgz", @@ -12618,6 +13174,35 @@ "node": ">= 0.4" } }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -13235,40 +13820,11 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/glob": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.2.tgz", - "integrity": "sha512-035InabNu/c1lW0tzPhAgapKctblppqsKKG9ZaNzbr+gXwWMjXoiyGSyB9sArzrjG7jY+zntRq5ZSUYemrnWVQ==", - "dependencies": { - "minimatch": "^10.1.2", - "minipass": "^7.1.2", - "path-scurry": "^2.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz", - "integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.1" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/rollup": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", - "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "license": "MIT", "dependencies": { "@types/estree": "1.0.8" }, @@ -13280,31 +13836,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.57.1", - "@rollup/rollup-android-arm64": "4.57.1", - "@rollup/rollup-darwin-arm64": "4.57.1", - "@rollup/rollup-darwin-x64": "4.57.1", - "@rollup/rollup-freebsd-arm64": "4.57.1", - "@rollup/rollup-freebsd-x64": "4.57.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", - "@rollup/rollup-linux-arm-musleabihf": "4.57.1", - "@rollup/rollup-linux-arm64-gnu": "4.57.1", - "@rollup/rollup-linux-arm64-musl": "4.57.1", - "@rollup/rollup-linux-loong64-gnu": "4.57.1", - "@rollup/rollup-linux-loong64-musl": "4.57.1", - "@rollup/rollup-linux-ppc64-gnu": "4.57.1", - "@rollup/rollup-linux-ppc64-musl": "4.57.1", - "@rollup/rollup-linux-riscv64-gnu": "4.57.1", - "@rollup/rollup-linux-riscv64-musl": "4.57.1", - "@rollup/rollup-linux-s390x-gnu": "4.57.1", - "@rollup/rollup-linux-x64-gnu": "4.57.1", - "@rollup/rollup-linux-x64-musl": "4.57.1", - "@rollup/rollup-openbsd-x64": "4.57.1", - "@rollup/rollup-openharmony-arm64": "4.57.1", - "@rollup/rollup-win32-arm64-msvc": "4.57.1", - "@rollup/rollup-win32-ia32-msvc": "4.57.1", - "@rollup/rollup-win32-x64-gnu": "4.57.1", - "@rollup/rollup-win32-x64-msvc": "4.57.1", + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" } }, @@ -13725,6 +14281,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -14003,6 +14566,16 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/speedometer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/speedometer/-/speedometer-1.0.0.tgz", @@ -14036,6 +14609,13 @@ "node": "*" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -14044,6 +14624,13 @@ "node": ">= 0.8" } }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, "node_modules/steno": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/steno/-/steno-4.0.2.tgz", @@ -14187,6 +14774,82 @@ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", "dev": true }, + "node_modules/superagent": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.3.0.tgz", + "integrity": "sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "component-emitter": "^1.3.1", + "cookiejar": "^2.1.4", + "debug": "^4.3.7", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.5", + "formidable": "^3.5.4", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.14.1" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/superagent/node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/superagent/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/supertest": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.2.2.tgz", + "integrity": "sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cookie-signature": "^1.2.2", + "methods": "^1.1.2", + "superagent": "^10.3.0" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/supertest/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, "node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -14235,9 +14898,10 @@ } }, "node_modules/tar": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", - "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.10.tgz", + "integrity": "sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw==", + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", @@ -14293,6 +14957,23 @@ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -14308,6 +14989,16 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tmp": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", @@ -14685,15 +15376,6 @@ "tslib": "^2.0.3" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/url-parse": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", @@ -14795,6 +15477,214 @@ "node": ">= 0.8" } }, + "node_modules/vitest": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz", + "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.0.18", + "@vitest/mocker": "4.0.18", + "@vitest/pretty-format": "4.0.18", + "@vitest/runner": "4.0.18", + "@vitest/snapshot": "4.0.18", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.18", + "@vitest/browser-preview": "4.0.18", + "@vitest/browser-webdriverio": "4.0.18", + "@vitest/ui": "4.0.18", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/@vitest/mocker": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz", + "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.18", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/vitest/node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/void-elements": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", @@ -14973,6 +15863,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/widest-line": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", diff --git a/api/package.json b/api/package.json index fa47e995d..cbbda018a 100644 --- a/api/package.json +++ b/api/package.json @@ -11,7 +11,13 @@ "windev": "SET NODE_ENV=production&& tsx watch ./src/server.ts", "winstart": "SET NODE_ENV=production&& node dist/server.js", "lint:fix": "eslint --ext .ts --ignore-pattern './node_modules/' --ignore-pattern './dist/'", - "precommit": "npm run prettify && npm run lint:fix" + "precommit": "npm run prettify && npm run lint:fix", + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage", + "test:unit": "vitest run tests/unit", + "test:integration": "vitest run tests/integration", + "coverage:ui": "npx serve coverage -l 3939" }, "type": "module", "repository": { @@ -35,6 +41,7 @@ "chokidar": "^3.6.0", "cors": "^2.8.5", "dayjs": "^1.11.18", + "diff": "^5.2.2", "dotenv": "^16.3.1", "express": "^4.22.0", "express-validator": "^7.3.1", @@ -53,8 +60,7 @@ "php-serialize": "^5.1.3", "socket.io": "^4.7.5", "uuid": "^9.0.1", - "winston": "^3.11.0", - "diff": "^5.2.2" + "winston": "^3.11.0" }, "devDependencies": { "@types/cors": "^2.8.17", @@ -65,21 +71,31 @@ "@types/jsonwebtoken": "^9.0.5", "@types/lodash": "^4.17.0", "@types/node": "^20.10.4", + "@types/supertest": "^6.0.3", "@types/uuid": "^9.0.8", "@types/wordpress__block-library": "^2.6.3", "@types/wordpress__block-serialization-spec-parser": "^3.1.3", "@types/wordpress__blocks": "^12.5.18", "@typescript-eslint/eslint-plugin": "^6.15.0", "@typescript-eslint/parser": "^6.15.0", + "@vitest/coverage-v8": "^4.0.18", "eslint": "^8.56.0", "eslint-config-prettier": "^8.3.0", "prettier": "^2.4.1", + "supertest": "^7.2.2", "tsx": "^4.7.1", - "typescript": "^5.4.3" + "typescript": "^5.4.3", + "vitest": "^4.0.18" }, "overrides": { "qs": ">=6.14.2", - "tmp": ">=0.2.4" + "tmp": ">=0.2.4", + "minimatch": ">=10.2.3", + "ajv": ">=8.18.0", + "glob": ">=11.1.0", + "rollup": ">=4.59.0", + "tar": ">=7.5.8", + "@tootallnate/once": ">=3.0.1" }, "keywords": [] } diff --git a/api/tests/fixtures/auth.fixture.ts b/api/tests/fixtures/auth.fixture.ts new file mode 100644 index 000000000..0c953cd26 --- /dev/null +++ b/api/tests/fixtures/auth.fixture.ts @@ -0,0 +1,26 @@ +export const createMockJwtPayload = (overrides: Record = {}) => ({ + region: 'NA', + user_id: 'user-123', + iat: Math.floor(Date.now() / 1000), + exp: Math.floor(Date.now() / 1000) + 3600, + ...overrides, +}); + +export const createMockToken = () => 'mock.jwt.token'; + +export const createMockLoginBody = (overrides: Record = {}) => ({ + email: 'test@example.com', + password: 'password123', + region: 'NA', + ...overrides, +}); + +export const createMockAuthUser = (overrides: Record = {}) => ({ + user_id: 'user-123', + email: 'test@example.com', + region: 'NA', + authtoken: 'cs-auth-token-123', + created_at: new Date().toISOString(), + updated_at: new Date().toISOString(), + ...overrides, +}); diff --git a/api/tests/fixtures/contentMapper.fixture.ts b/api/tests/fixtures/contentMapper.fixture.ts new file mode 100644 index 000000000..293fb4cf3 --- /dev/null +++ b/api/tests/fixtures/contentMapper.fixture.ts @@ -0,0 +1,46 @@ +export const createMockContentType = (overrides: Record = {}) => ({ + id: 'ct-123', + projectId: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', + otherCmsTitle: 'Blog Post', + otherCmsUid: 'blog_post', + isUpdated: false, + updateAt: new Date(), + contentstackTitle: '', + contentstackUid: '', + status: 1, + fieldMapping: [], + type: 'content_type', + ...overrides, +}); + +export const createMockFieldMapper = (overrides: Record = {}) => ({ + id: 'fm-123', + projectId: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', + contentTypeId: 'ct-123', + uid: 'field-uid-1', + otherCmsField: 'title', + otherCmsType: 'text', + contentstackField: 'title', + contentstackFieldUid: 'title', + contentstackFieldType: 'text', + isDeleted: false, + backupFieldType: 'text', + backupFieldUid: 'title', + refrnceTo: { uid: '', title: '' }, + advanced: { + validationRegex: '', + mandatory: false, + multiple: false, + unique: false, + nonLocalizable: false, + embedObject: false, + embedObjects: null, + minChars: '', + maxChars: 0, + default_value: '', + description: '', + validationErrorMessage: '', + options: [], + }, + ...overrides, +}); diff --git a/api/tests/fixtures/project.fixture.ts b/api/tests/fixtures/project.fixture.ts new file mode 100644 index 000000000..c5031fcd5 --- /dev/null +++ b/api/tests/fixtures/project.fixture.ts @@ -0,0 +1,50 @@ +export const createMockProject = (overrides: Record = {}) => ({ + id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', + region: 'NA', + org_id: 'org-123', + owner: 'user-123', + created_by: 'user-123', + updated_by: 'user-123', + former_owner_ids: [], + name: 'Test Project', + description: 'A test project', + status: 0, + current_step: 1, + destination_stack_id: '', + test_stacks: [], + current_test_stack_id: '', + legacy_cms: { + cms: '', + affix: '', + affix_confirmation: false, + file_format: '', + file_format_confirmation: false, + file: { id: '', name: '', size: 0, type: '', path: '' }, + awsDetails: { awsRegion: '', bucketName: '', bucketKey: '' }, + file_path: '', + is_fileValid: false, + is_localPath: false, + }, + content_mapper: [], + execution_log: [], + created_at: new Date().toISOString(), + updated_at: new Date().toISOString(), + isDeleted: false, + isNewStack: false, + newStackId: '', + stackDetails: [], + mapperKeys: {}, + extract_path: '', + isMigrationStarted: false, + isMigrationCompleted: false, + migration_execution: false, + ...overrides, +}); + +export const createMockProjectList = (count: number = 3) => + Array.from({ length: count }, (_, i) => + createMockProject({ + id: `proj-${i + 1}`, + name: `Project ${i + 1}`, + }) + ); diff --git a/api/tests/fixtures/user.fixture.ts b/api/tests/fixtures/user.fixture.ts new file mode 100644 index 000000000..cbbe37af4 --- /dev/null +++ b/api/tests/fixtures/user.fixture.ts @@ -0,0 +1,23 @@ +export const createMockUser = (overrides: Record = {}) => ({ + uid: 'user-123', + email: 'test@example.com', + first_name: 'Test', + last_name: 'User', + ...overrides, +}); + +export const createMockOrg = (overrides: Record = {}) => ({ + uid: 'org-123', + name: 'Test Organization', + org_roles: [{ admin: true }], + ...overrides, +}); + +export const createMockStack = (overrides: Record = {}) => ({ + api_key: 'stack-api-key-123', + name: 'Test Stack', + description: 'A test stack', + master_locale: 'en-us', + org_uid: 'org-123', + ...overrides, +}); diff --git a/api/tests/setup.ts b/api/tests/setup.ts new file mode 100644 index 000000000..c91770ea5 --- /dev/null +++ b/api/tests/setup.ts @@ -0,0 +1,20 @@ +import { vi, beforeAll, afterAll, afterEach } from 'vitest'; + +beforeAll(() => { + vi.stubEnv('NODE_ENV', 'production'); + vi.stubEnv('APP_TOKEN_KEY', 'test-secret-key'); + vi.stubEnv('PORT', '5001'); + vi.stubEnv('FILE_UPLOAD_KEY', 'test-upload-key'); + vi.stubEnv('MONGODB_URI', 'mongodb://localhost:27017/test-migration'); + vi.stubEnv('LOG_LEVEL', 'error'); + vi.stubEnv('DRUPAL_ASSETS_BASE_URL', 'http://localhost:8080'); + vi.stubEnv('DRUPAL_ASSETS_PUBLIC_PATH', '/sites/default/files'); +}); + +afterEach(() => { + vi.restoreAllMocks(); +}); + +afterAll(() => { + vi.unstubAllEnvs(); +}); diff --git a/api/tests/unit/config/index.config.test.ts b/api/tests/unit/config/index.config.test.ts new file mode 100644 index 000000000..63a1c6e9c --- /dev/null +++ b/api/tests/unit/config/index.config.test.ts @@ -0,0 +1,33 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +describe('config/index', () => { + beforeEach(() => { + vi.resetModules(); + }); + + it('should export config with expected keys', async () => { + vi.stubEnv('NODE_ENV', 'production'); + vi.stubEnv('APP_TOKEN_KEY', 'my-secret'); + vi.stubEnv('PORT', '3000'); + vi.stubEnv('FILE_UPLOAD_KEY', 'upload-key'); + + const { config } = await import('../../../src/config/index.js'); + + expect(config).toBeDefined(); + expect(config.APP_TOKEN_EXP).toBe('2d'); + expect(config.CS_API).toBeDefined(); + expect(config.CS_URL).toBeDefined(); + }); + + it('should have APP_TOKEN_EXP set to 2d', async () => { + const { config } = await import('../../../src/config/index.js'); + expect(config.APP_TOKEN_EXP).toBe('2d'); + }); + + it('should have CS_API with region keys', async () => { + const { config } = await import('../../../src/config/index.js'); + expect(config.CS_API).toHaveProperty('NA'); + expect(config.CS_API).toHaveProperty('EU'); + expect(config.CS_API).toHaveProperty('AZURE_NA'); + }); +}); diff --git a/api/tests/unit/controllers/auth.controller.test.ts b/api/tests/unit/controllers/auth.controller.test.ts new file mode 100644 index 000000000..2849eb870 --- /dev/null +++ b/api/tests/unit/controllers/auth.controller.test.ts @@ -0,0 +1,84 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockLogin, mockRequestSms } = vi.hoisted(() => ({ + mockLogin: vi.fn(), + mockRequestSms: vi.fn(), +})); + +vi.mock('../../../src/services/auth.service.js', () => ({ + authService: { + login: mockLogin, + requestSms: mockRequestSms, + }, +})); + +import { authController } from '../../../src/controllers/auth.controller.js'; + +describe('auth.controller', () => { + let req: any; + let res: any; + + beforeEach(() => { + vi.clearAllMocks(); + req = { body: {} }; + res = { + status: vi.fn().mockReturnThis(), + json: vi.fn().mockReturnThis(), + }; + }); + + describe('login', () => { + it('should return service response status and data on success', async () => { + mockLogin.mockResolvedValue({ status: 200, data: { app_token: 'token' } }); + await authController.login(req, res); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({ app_token: 'token' }); + }); + + it('should return error status and message on service throw', async () => { + mockLogin.mockRejectedValue({ statusCode: 400, message: 'Invalid credentials' }); + await authController.login(req, res); + expect(res.status).toHaveBeenCalledWith(400); + expect(res.json).toHaveBeenCalledWith({ message: 'Invalid credentials' }); + }); + + it('should default to 500 when error has no statusCode', async () => { + mockLogin.mockRejectedValue({ message: 'Unknown error' }); + await authController.login(req, res); + expect(res.status).toHaveBeenCalledWith(500); + }); + + it('should default to "Login failed" when error has no message', async () => { + mockLogin.mockRejectedValue({}); + await authController.login(req, res); + expect(res.json).toHaveBeenCalledWith({ message: 'Login failed' }); + }); + + it('should default to 500 when response has no status', async () => { + mockLogin.mockResolvedValue({ data: { ok: true } }); + await authController.login(req, res); + expect(res.status).toHaveBeenCalledWith(500); + }); + }); + + describe('RequestSms', () => { + it('should return service response on success', async () => { + mockRequestSms.mockResolvedValue({ status: 200, data: { message: 'SMS sent' } }); + await authController.RequestSms(req, res); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({ message: 'SMS sent' }); + }); + + it('should return error on failure', async () => { + mockRequestSms.mockRejectedValue({ statusCode: 429, message: 'Too many requests' }); + await authController.RequestSms(req, res); + expect(res.status).toHaveBeenCalledWith(429); + }); + + it('should default to 500 when no statusCode', async () => { + mockRequestSms.mockRejectedValue({ message: 'Fail' }); + await authController.RequestSms(req, res); + expect(res.status).toHaveBeenCalledWith(500); + }); + }); +}); diff --git a/api/tests/unit/controllers/migration.controller.test.ts b/api/tests/unit/controllers/migration.controller.test.ts new file mode 100644 index 000000000..7bcdee018 --- /dev/null +++ b/api/tests/unit/controllers/migration.controller.test.ts @@ -0,0 +1,104 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockMigrationService } = vi.hoisted(() => ({ + mockMigrationService: { + createTestStack: vi.fn(), + deleteTestStack: vi.fn(), + startTestMigration: vi.fn(), + startMigration: vi.fn(), + getLogs: vi.fn(), + createSourceLocales: vi.fn(), + updateLocaleMapper: vi.fn(), + getAuditData: vi.fn(), + }, +})); + +vi.mock('../../../src/services/migration.service.js', () => ({ + migrationService: mockMigrationService, +})); + +import { migrationController } from '../../../src/controllers/migration.controller.js'; + +describe('migration.controller', () => { + let req: any; + let res: any; + + beforeEach(() => { + vi.clearAllMocks(); + req = { params: { projectId: 'proj-123' }, body: {} }; + res = { + status: vi.fn().mockReturnThis(), + json: vi.fn().mockReturnThis(), + }; + }); + + it('createTestStack should return awaited service response', async () => { + mockMigrationService.createTestStack.mockResolvedValue({ status: 200, data: { stack: {} } }); + + await migrationController.createTestStack(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + }); + + it('getAuditData should return awaited service response', async () => { + mockMigrationService.getAuditData.mockResolvedValue({ status: 200, data: [] }); + + await migrationController.getAuditData(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + }); + + it('deleteTestStack should return 200', async () => { + mockMigrationService.deleteTestStack.mockResolvedValue({ ok: true }); + + await migrationController.deleteTestStack(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + }); + + it('getLogs should return 200', async () => { + mockMigrationService.getLogs.mockResolvedValue({ logs: [] }); + + await migrationController.getLogs(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + }); + + it('saveLocales should return 200', async () => { + mockMigrationService.createSourceLocales.mockResolvedValue({ ok: true }); + + await migrationController.saveLocales(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + }); + + it('saveMappedLocales should return 200', async () => { + mockMigrationService.updateLocaleMapper.mockResolvedValue({ ok: true }); + + await migrationController.saveMappedLocales(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + }); + + describe('fire-and-forget methods', () => { + it('startTestMigration should return 200 immediately and call service', async () => { + const migrationPromise = Promise.resolve({ ok: true }); + mockMigrationService.startTestMigration.mockReturnValue(migrationPromise); + + await migrationController.startTestMigration(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(mockMigrationService.startTestMigration).toHaveBeenCalledWith(req); + }); + + it('startMigration should return 200 immediately and call service', async () => { + const migrationPromise = Promise.resolve({ ok: true }); + mockMigrationService.startMigration.mockReturnValue(migrationPromise); + + await migrationController.startMigration(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(mockMigrationService.startMigration).toHaveBeenCalledWith(req); + }); + }); +}); diff --git a/api/tests/unit/controllers/org.controller.test.ts b/api/tests/unit/controllers/org.controller.test.ts new file mode 100644 index 000000000..37b7d7b71 --- /dev/null +++ b/api/tests/unit/controllers/org.controller.test.ts @@ -0,0 +1,51 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockOrgService } = vi.hoisted(() => ({ + mockOrgService: { + getAllStacks: vi.fn(), + createStack: vi.fn(), + getLocales: vi.fn(), + getStackStatus: vi.fn(), + getStackLocale: vi.fn(), + getOrgDetails: vi.fn(), + }, +})); + +vi.mock('../../../src/services/org.service.js', () => ({ + orgService: mockOrgService, +})); + +import { orgController } from '../../../src/controllers/org.controller.js'; + +describe('org.controller', () => { + let req: any; + let res: any; + + beforeEach(() => { + vi.clearAllMocks(); + req = { params: { orgId: 'org-123' }, body: { token_payload: { region: 'NA', user_id: 'user-123' } } }; + res = { + status: vi.fn().mockReturnThis(), + json: vi.fn().mockReturnThis(), + }; + }); + + const testControllerMethod = (methodName: keyof typeof orgController, serviceName: keyof typeof mockOrgService) => { + it(`${methodName} should delegate to service and return resp.status/resp.data`, async () => { + mockOrgService[serviceName].mockResolvedValue({ status: 200, data: { result: 'ok' } }); + + await orgController[methodName](req, res); + + expect(mockOrgService[serviceName]).toHaveBeenCalledWith(req); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({ result: 'ok' }); + }); + }; + + testControllerMethod('getAllStacks', 'getAllStacks'); + testControllerMethod('createStack', 'createStack'); + testControllerMethod('getLocales', 'getLocales'); + testControllerMethod('getStackStatus', 'getStackStatus'); + testControllerMethod('getStackLocale', 'getStackLocale'); + testControllerMethod('getOrgDetails', 'getOrgDetails'); +}); diff --git a/api/tests/unit/controllers/projects.contentMapper.controller.test.ts b/api/tests/unit/controllers/projects.contentMapper.controller.test.ts new file mode 100644 index 000000000..f27ea340b --- /dev/null +++ b/api/tests/unit/controllers/projects.contentMapper.controller.test.ts @@ -0,0 +1,101 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockContentMapperService } = vi.hoisted(() => ({ + mockContentMapperService: { + putTestData: vi.fn(), + getContentTypes: vi.fn(), + getFieldMapping: vi.fn(), + getExistingContentTypes: vi.fn(), + getExistingGlobalFields: vi.fn(), + getExistingTaxonomies: vi.fn(), + updateContentType: vi.fn(), + resetToInitialMapping: vi.fn(), + removeContentMapper: vi.fn(), + getSingleContentTypes: vi.fn(), + getSingleGlobalField: vi.fn(), + updateContentMapper: vi.fn(), + }, +})); + +vi.mock('../../../src/services/contentMapper.service.js', () => ({ + contentMapperService: mockContentMapperService, +})); + +import { contentMapperController } from '../../../src/controllers/projects.contentMapper.controller.js'; + +describe('projects.contentMapper.controller', () => { + let req: any; + let res: any; + + beforeEach(() => { + vi.clearAllMocks(); + req = { params: { projectId: 'proj-123' }, body: {} }; + res = { + status: vi.fn().mockReturnThis(), + json: vi.fn().mockReturnThis(), + }; + }); + + const testDelegation = ( + controllerMethod: string, + serviceMethod: string, + expectedStatus?: number + ) => { + it(`${controllerMethod} should delegate to service`, async () => { + const serviceResp = { status: 200, data: { ok: true } }; + (mockContentMapperService as any)[serviceMethod].mockResolvedValue(serviceResp); + + await (contentMapperController as any)[controllerMethod](req, res); + + expect((mockContentMapperService as any)[serviceMethod]).toHaveBeenCalledWith(req); + }); + }; + + testDelegation('putTestData', 'putTestData'); + testDelegation('getContentTypes', 'getContentTypes'); + testDelegation('getFieldMapping', 'getFieldMapping'); + testDelegation('putContentTypeFields', 'updateContentType'); + testDelegation('resetContentType', 'resetToInitialMapping'); + testDelegation('removeContentMapper', 'removeContentMapper'); + testDelegation('updateContentMapper', 'updateContentMapper'); + + it('getExistingContentTypes should return 201', async () => { + mockContentMapperService.getExistingContentTypes.mockResolvedValue({ contentTypes: [] }); + + await contentMapperController.getExistingContentTypes(req, res); + + expect(res.status).toHaveBeenCalledWith(201); + }); + + it('getExistingGlobalFields should return 201', async () => { + mockContentMapperService.getExistingGlobalFields.mockResolvedValue({ globalFields: [] }); + + await contentMapperController.getExistingGlobalFields(req, res); + + expect(res.status).toHaveBeenCalledWith(201); + }); + + it('getExistingTaxonomies should return status from response or default 200', async () => { + mockContentMapperService.getExistingTaxonomies.mockResolvedValue({ status: 200, taxonomies: [] }); + + await contentMapperController.getExistingTaxonomies(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + }); + + it('getSingleContentTypes should return 201', async () => { + mockContentMapperService.getSingleContentTypes.mockResolvedValue({ title: 'Blog' }); + + await contentMapperController.getSingleContentTypes(req, res); + + expect(res.status).toHaveBeenCalledWith(201); + }); + + it('getSingleGlobalField should return 201', async () => { + mockContentMapperService.getSingleGlobalField.mockResolvedValue({ title: 'SEO' }); + + await contentMapperController.getSingleGlobalField(req, res); + + expect(res.status).toHaveBeenCalledWith(201); + }); +}); diff --git a/api/tests/unit/controllers/projects.controller.test.ts b/api/tests/unit/controllers/projects.controller.test.ts new file mode 100644 index 000000000..42fc0136a --- /dev/null +++ b/api/tests/unit/controllers/projects.controller.test.ts @@ -0,0 +1,121 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockProjectService } = vi.hoisted(() => ({ + mockProjectService: { + getAllProjects: vi.fn(), + getProject: vi.fn(), + createProject: vi.fn(), + updateProject: vi.fn(), + updateLegacyCMS: vi.fn(), + updateAffix: vi.fn(), + affixConfirmation: vi.fn(), + updateFileFormat: vi.fn(), + fileformatConfirmation: vi.fn(), + updateDestinationStack: vi.fn(), + updateCurrentStep: vi.fn(), + deleteProject: vi.fn(), + revertProject: vi.fn(), + updateStackDetails: vi.fn(), + updateMigrationExecution: vi.fn(), + getMigratedStacks: vi.fn(), + }, +})); + +vi.mock('../../../src/services/projects.service.js', () => ({ + projectService: mockProjectService, +})); + +import { projectController } from '../../../src/controllers/projects.controller.js'; + +describe('projects.controller', () => { + let req: any; + let res: any; + + beforeEach(() => { + vi.clearAllMocks(); + req = { + params: { orgId: 'org-123', projectId: 'proj-123' }, + body: { token_payload: { region: 'NA', user_id: 'user-123' } }, + }; + res = { + status: vi.fn().mockReturnThis(), + json: vi.fn().mockReturnThis(), + }; + }); + + it('getAllProjects should return 200 with projects array', async () => { + const projects = [{ id: '1' }, { id: '2' }]; + mockProjectService.getAllProjects.mockResolvedValue(projects); + + await projectController.getAllProjects(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith(projects); + }); + + it('getProject should return 200 with single project', async () => { + const project = { id: 'proj-123', name: 'Test' }; + mockProjectService.getProject.mockResolvedValue(project); + + await projectController.getProject(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith(project); + }); + + it('createProject should return 201 with created project', async () => { + const project = { id: 'new-proj', name: 'New Project' }; + mockProjectService.createProject.mockResolvedValue(project); + + await projectController.createProject(req, res); + + expect(res.status).toHaveBeenCalledWith(201); + expect(res.json).toHaveBeenCalledWith(project); + }); + + it('updateProject should return 200 with updated project', async () => { + const project = { id: 'proj-123', name: 'Updated' }; + mockProjectService.updateProject.mockResolvedValue(project); + + await projectController.updateProject(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith(project); + }); + + const testServiceDelegation = ( + controllerMethod: keyof typeof projectController, + serviceMethod: keyof typeof mockProjectService + ) => { + it(`${controllerMethod} should delegate to service and return response`, async () => { + mockProjectService[serviceMethod].mockResolvedValue({ status: 200, data: { ok: true } }); + + await projectController[controllerMethod](req, res); + + expect(mockProjectService[serviceMethod]).toHaveBeenCalledWith(req); + expect(res.status).toHaveBeenCalledWith(200); + }); + }; + + testServiceDelegation('updateLegacyCMS', 'updateLegacyCMS'); + testServiceDelegation('updateAffix', 'updateAffix'); + testServiceDelegation('affixConfirmation', 'affixConfirmation'); + testServiceDelegation('updateFileFormat', 'updateFileFormat'); + testServiceDelegation('fileformatConfirmation', 'fileformatConfirmation'); + testServiceDelegation('updateDestinationStack', 'updateDestinationStack'); + testServiceDelegation('deleteProject', 'deleteProject'); + testServiceDelegation('revertProject', 'revertProject'); + testServiceDelegation('updateStackDetails', 'updateStackDetails'); + testServiceDelegation('updateMigrationExecution', 'updateMigrationExecution'); + testServiceDelegation('getMigratedStacks', 'getMigratedStacks'); + + it('updateCurrentStep should return 200', async () => { + const project = { id: 'proj-123', current_step: 2 }; + mockProjectService.updateCurrentStep.mockResolvedValue(project); + + await projectController.updateCurrentStep(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith(project); + }); +}); diff --git a/api/tests/unit/controllers/user.controller.test.ts b/api/tests/unit/controllers/user.controller.test.ts new file mode 100644 index 000000000..154325b0e --- /dev/null +++ b/api/tests/unit/controllers/user.controller.test.ts @@ -0,0 +1,39 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockGetUserProfile } = vi.hoisted(() => ({ mockGetUserProfile: vi.fn() })); + +vi.mock('../../../src/services/user.service.js', () => ({ + userService: { + getUserProfile: mockGetUserProfile, + }, +})); + +import { userController } from '../../../src/controllers/user.controller.js'; + +describe('user.controller', () => { + let req: any; + let res: any; + + beforeEach(() => { + vi.clearAllMocks(); + req = { body: { token_payload: { region: 'NA', user_id: 'user-123' } } }; + res = { + status: vi.fn().mockReturnThis(), + json: vi.fn().mockReturnThis(), + }; + }); + + describe('getUserProfile', () => { + it('should return resp.status and resp.data from service', async () => { + mockGetUserProfile.mockResolvedValue({ + status: 200, + data: { user: { email: 'test@example.com' } }, + }); + + await userController.getUserProfile(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({ user: { email: 'test@example.com' } }); + }); + }); +}); diff --git a/api/tests/unit/helpers/index.test.ts b/api/tests/unit/helpers/index.test.ts new file mode 100644 index 000000000..ecd51a418 --- /dev/null +++ b/api/tests/unit/helpers/index.test.ts @@ -0,0 +1,118 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockConnect, mockEnd, mockDestroy, mockCreateConnection } = vi.hoisted(() => { + const mockConnect = vi.fn(); + const mockEnd = vi.fn(); + const mockDestroy = vi.fn(); + const mockCreateConnection = vi.fn(() => ({ + connect: mockConnect, + end: mockEnd, + destroy: mockDestroy, + })); + return { mockConnect, mockEnd, mockDestroy, mockCreateConnection }; +}); + +vi.mock('mysql2', () => ({ + default: { + createConnection: mockCreateConnection, + }, +})); + +vi.mock('../../../src/utils/custom-logger.utils.js', () => ({ + default: vi.fn().mockResolvedValue(undefined), +})); + +import { createDbConnection, getDbConnection } from '../../../src/helper/index.js'; + +describe('helper/index', () => { + const dbConfig = { + host: 'localhost', + user: 'root', + password: 'password', + database: 'testdb', + port: 3306, + }; + + beforeEach(() => { + vi.clearAllMocks(); + vi.useFakeTimers(); + }); + + describe('createDbConnection', () => { + it('should create a MySQL connection successfully', async () => { + mockConnect.mockImplementation((cb: any) => cb(null)); + + const connectionPromise = createDbConnection(dbConfig, 'proj-1', 'stack-1'); + vi.runAllTimers(); + const connection = await connectionPromise; + + expect(connection).toBeTruthy(); + expect(mockCreateConnection).toHaveBeenCalledWith( + expect.objectContaining({ + host: 'localhost', + user: 'root', + password: 'password', + database: 'testdb', + port: 3306, + }) + ); + }); + + it('should reject on connection error', async () => { + const dbError = new Error('Access denied'); + mockConnect.mockImplementation((cb: any) => cb(dbError)); + mockEnd.mockImplementation((cb: any) => cb(null)); + + const connectionPromise = createDbConnection(dbConfig); + vi.runAllTimers(); + + await expect(connectionPromise).rejects.toThrow('Access denied'); + }); + + it('should reject on connection timeout', async () => { + mockConnect.mockImplementation(() => { + // Never calls callback, simulating hang + }); + + const connectionPromise = createDbConnection(dbConfig, '', '', 100); + vi.advanceTimersByTime(150); + + await expect(connectionPromise).rejects.toThrow('timed out'); + expect(mockDestroy).toHaveBeenCalled(); + }); + + it('should return null on synchronous createConnection error', async () => { + mockCreateConnection.mockImplementation(() => { + throw new Error('Invalid config'); + }); + + const result = await createDbConnection(dbConfig); + expect(result).toBeNull(); + }); + }); + + describe('getDbConnection', () => { + it('should return connection when createDbConnection succeeds', async () => { + mockConnect.mockImplementation((cb: any) => cb(null)); + mockCreateConnection.mockReturnValue({ + connect: mockConnect, + end: mockEnd, + destroy: mockDestroy, + }); + + const connectionPromise = getDbConnection(dbConfig, 'proj-1', 'stack-1'); + vi.runAllTimers(); + const connection = await connectionPromise; + + expect(connection).toBeTruthy(); + }); + + it('should throw when connection is null', async () => { + mockCreateConnection.mockImplementation(() => { + throw new Error('Cannot connect'); + }); + + await expect(getDbConnection(dbConfig)).rejects.toThrow(); + }); + }); +}); diff --git a/api/tests/unit/middlewares/auth.middleware.test.ts b/api/tests/unit/middlewares/auth.middleware.test.ts new file mode 100644 index 000000000..248e596a3 --- /dev/null +++ b/api/tests/unit/middlewares/auth.middleware.test.ts @@ -0,0 +1,63 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import jwt from 'jsonwebtoken'; + +vi.mock('../../../src/config/index.js', () => ({ + config: { APP_TOKEN_KEY: 'test-secret-key' }, +})); + +import { authenticateUser } from '../../../src/middlewares/auth.middleware.js'; + +describe('auth.middleware', () => { + let req: any; + let res: any; + let next: any; + + beforeEach(() => { + req = { + get: vi.fn(), + body: {}, + }; + res = { + status: vi.fn().mockReturnThis(), + json: vi.fn().mockReturnThis(), + }; + next = vi.fn(); + }); + + it('should return 401 when app_token header is missing', () => { + req.get.mockReturnValue(undefined); + + authenticateUser(req, res, next); + + expect(res.status).toHaveBeenCalledWith(401); + expect(res.json).toHaveBeenCalledWith( + expect.objectContaining({ message: 'Unauthorized - Token missing' }) + ); + expect(next).not.toHaveBeenCalled(); + }); + + it('should return 401 when JWT verification fails', () => { + req.get.mockReturnValue('invalid-token'); + + authenticateUser(req, res, next); + + expect(res.status).toHaveBeenCalledWith(401); + expect(res.json).toHaveBeenCalledWith( + expect.objectContaining({ message: 'Unauthorized - Invalid token' }) + ); + expect(next).not.toHaveBeenCalled(); + }); + + it('should call next and set token_payload on valid token', () => { + const payload = { region: 'NA', user_id: 'user-123' }; + const token = jwt.sign(payload, 'test-secret-key'); + req.get.mockReturnValue(token); + + authenticateUser(req, res, next); + + expect(next).toHaveBeenCalled(); + expect(req.body.token_payload).toBeDefined(); + expect(req.body.token_payload.region).toBe('NA'); + expect(req.body.token_payload.user_id).toBe('user-123'); + }); +}); diff --git a/api/tests/unit/middlewares/auth.uploadService.middleware.test.ts b/api/tests/unit/middlewares/auth.uploadService.middleware.test.ts new file mode 100644 index 000000000..636f248a6 --- /dev/null +++ b/api/tests/unit/middlewares/auth.uploadService.middleware.test.ts @@ -0,0 +1,52 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +vi.mock('../../../src/config/index.js', () => ({ + config: { FILE_UPLOAD_KEY: 'valid-upload-key' }, +})); + +import { authenticateUploadService } from '../../../src/middlewares/auth.uploadService.middleware.js'; + +describe('auth.uploadService.middleware', () => { + let req: any; + let res: any; + let next: any; + + beforeEach(() => { + req = { get: vi.fn() }; + res = { + status: vi.fn().mockReturnThis(), + json: vi.fn().mockReturnThis(), + }; + next = vi.fn(); + }); + + it('should return 401 when secret_key header is missing', () => { + req.get.mockReturnValue(undefined); + + authenticateUploadService(req, res, next); + + expect(res.status).toHaveBeenCalledWith(401); + expect(res.json).toHaveBeenCalledWith( + expect.objectContaining({ message: 'Unauthorized - Please provide a valid key' }) + ); + expect(next).not.toHaveBeenCalled(); + }); + + it('should return 401 when secret_key does not match', () => { + req.get.mockReturnValue('wrong-key'); + + authenticateUploadService(req, res, next); + + expect(res.status).toHaveBeenCalledWith(401); + expect(next).not.toHaveBeenCalled(); + }); + + it('should call next when secret_key matches', () => { + req.get.mockReturnValue('valid-upload-key'); + + authenticateUploadService(req, res, next); + + expect(next).toHaveBeenCalled(); + expect(res.status).not.toHaveBeenCalled(); + }); +}); diff --git a/api/tests/unit/middlewares/error.middleware.test.ts b/api/tests/unit/middlewares/error.middleware.test.ts new file mode 100644 index 000000000..7a26128f4 --- /dev/null +++ b/api/tests/unit/middlewares/error.middleware.test.ts @@ -0,0 +1,66 @@ +import { describe, it, expect, vi } from 'vitest'; + +vi.mock('../../../src/utils/logger.js', () => ({ + default: { error: vi.fn(), info: vi.fn(), warn: vi.fn() }, +})); + +import { errorMiddleware } from '../../../src/middlewares/error.middleware.js'; +import { AppError, BadRequestError, NotFoundError } from '../../../src/utils/custom-errors.utils.js'; + +describe('error.middleware', () => { + const req = {} as any; + const next = vi.fn(); + + const createRes = () => ({ + status: vi.fn().mockReturnThis(), + json: vi.fn().mockReturnThis(), + }); + + it('should return statusCode and message for AppError instances', () => { + const res = createRes(); + const error = new BadRequestError('Invalid input'); + + errorMiddleware(error, req, res as any, next); + + expect(res.status).toHaveBeenCalledWith(400); + expect(res.json).toHaveBeenCalledWith({ + error: { code: 400, message: 'Invalid input' }, + }); + }); + + it('should return 404 for NotFoundError', () => { + const res = createRes(); + const error = new NotFoundError('Resource not found'); + + errorMiddleware(error, req, res as any, next); + + expect(res.status).toHaveBeenCalledWith(404); + expect(res.json).toHaveBeenCalledWith({ + error: { code: 404, message: 'Resource not found' }, + }); + }); + + it('should return 500 for generic errors', () => { + const res = createRes(); + const error = new Error('Something broke'); + + errorMiddleware(error, req, res as any, next); + + expect(res.status).toHaveBeenCalledWith(500); + expect(res.json).toHaveBeenCalledWith({ + error: { code: 500, message: 'Internal Server Error' }, + }); + }); + + it('should handle AppError with custom statusCode', () => { + const res = createRes(); + const error = new AppError(503, 'Service unavailable'); + + errorMiddleware(error, req, res as any, next); + + expect(res.status).toHaveBeenCalledWith(503); + expect(res.json).toHaveBeenCalledWith({ + error: { code: 503, message: 'Service unavailable' }, + }); + }); +}); diff --git a/api/tests/unit/middlewares/req-headers.middleware.test.ts b/api/tests/unit/middlewares/req-headers.middleware.test.ts new file mode 100644 index 000000000..77d380deb --- /dev/null +++ b/api/tests/unit/middlewares/req-headers.middleware.test.ts @@ -0,0 +1,51 @@ +import { describe, it, expect, vi } from 'vitest'; +import { requestHeadersMiddleware } from '../../../src/middlewares/req-headers.middleware.js'; + +describe('req-headers.middleware', () => { + const createRes = () => ({ + header: vi.fn(), + status: vi.fn().mockReturnThis(), + json: vi.fn().mockReturnThis(), + }); + + it('should set CORS headers on all requests', () => { + const req = { method: 'GET' } as any; + const res = createRes(); + const next = vi.fn(); + + requestHeadersMiddleware(req, res as any, next); + + expect(res.header).toHaveBeenCalledWith('Access-Control-Allow-Origin', '*'); + expect(res.header).toHaveBeenCalledWith( + 'Access-Control-Allow-Headers', + 'Origin, Content-Type, Accept, app_token' + ); + }); + + it('should return 200 with empty JSON for OPTIONS requests', () => { + const req = { method: 'OPTIONS' } as any; + const res = createRes(); + const next = vi.fn(); + + requestHeadersMiddleware(req, res as any, next); + + expect(res.header).toHaveBeenCalledWith( + 'Access-Control-Allow-Methods', + 'GET, POST, PUT, DELETE' + ); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({}); + expect(next).not.toHaveBeenCalled(); + }); + + it('should call next for non-OPTIONS requests', () => { + const req = { method: 'POST' } as any; + const res = createRes(); + const next = vi.fn(); + + requestHeadersMiddleware(req, res as any, next); + + expect(next).toHaveBeenCalled(); + expect(res.status).not.toHaveBeenCalled(); + }); +}); diff --git a/api/tests/unit/middlewares/unmatched-routes.middleware.test.ts b/api/tests/unit/middlewares/unmatched-routes.middleware.test.ts new file mode 100644 index 000000000..f682aebd3 --- /dev/null +++ b/api/tests/unit/middlewares/unmatched-routes.middleware.test.ts @@ -0,0 +1,22 @@ +import { describe, it, expect, vi } from 'vitest'; +import { unmatchedRoutesMiddleware } from '../../../src/middlewares/unmatched-routes.middleware.js'; + +describe('unmatched-routes.middleware', () => { + it('should return 404 with route error message', () => { + const req = {} as any; + const res = { + status: vi.fn().mockReturnThis(), + json: vi.fn().mockReturnThis(), + }; + + unmatchedRoutesMiddleware(req, res as any); + + expect(res.status).toHaveBeenCalledWith(404); + expect(res.json).toHaveBeenCalledWith({ + error: { + code: 404, + message: 'Sorry, the requested resource is not available.', + }, + }); + }); +}); diff --git a/api/tests/unit/models/FieldMapper.model.test.ts b/api/tests/unit/models/FieldMapper.model.test.ts new file mode 100644 index 000000000..96efda500 --- /dev/null +++ b/api/tests/unit/models/FieldMapper.model.test.ts @@ -0,0 +1,43 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +vi.mock('lowdb/node', () => ({ + JSONFile: vi.fn().mockImplementation(function (this: unknown) { + return {}; + }), +})); + +vi.mock('../../../src/utils/lowdb-lodash.utils.js', () => ({ + default: vi.fn().mockImplementation(function ( + _adapter: unknown, + defaultData: { field_mapper: unknown[] } + ) { + return { + data: defaultData, + chain: {}, + }; + }), +})); + +describe('FieldMapper model', () => { + beforeEach(() => { + vi.resetModules(); + }); + + it('should export db with field_mapper array in default data', async () => { + const fieldMapperDb = (await import('../../../src/models/FieldMapper.js')).default; + + expect(fieldMapperDb).toBeDefined(); + expect(fieldMapperDb.data).toBeDefined(); + expect(fieldMapperDb.data).toHaveProperty('field_mapper'); + expect(Array.isArray(fieldMapperDb.data.field_mapper)).toBe(true); + expect(fieldMapperDb.data.field_mapper).toEqual([]); + }); + + it('should have correct default structure for FieldMapper', async () => { + const fieldMapperDb = (await import('../../../src/models/FieldMapper.js')).default; + + expect(fieldMapperDb.data).toMatchObject({ + field_mapper: [], + }); + }); +}); diff --git a/api/tests/unit/models/authentication.model.test.ts b/api/tests/unit/models/authentication.model.test.ts new file mode 100644 index 000000000..e7f6d4062 --- /dev/null +++ b/api/tests/unit/models/authentication.model.test.ts @@ -0,0 +1,43 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +vi.mock('lowdb/node', () => ({ + JSONFile: vi.fn().mockImplementation(function (this: unknown) { + return {}; + }), +})); + +vi.mock('../../../src/utils/lowdb-lodash.utils.js', () => ({ + default: vi.fn().mockImplementation(function ( + _adapter: unknown, + defaultData: { users: unknown[] } + ) { + return { + data: defaultData, + chain: {}, + }; + }), +})); + +describe('authentication model', () => { + beforeEach(() => { + vi.resetModules(); + }); + + it('should export db with users array in default data', async () => { + const authDb = (await import('../../../src/models/authentication.js')).default; + + expect(authDb).toBeDefined(); + expect(authDb.data).toBeDefined(); + expect(authDb.data).toHaveProperty('users'); + expect(Array.isArray(authDb.data.users)).toBe(true); + expect(authDb.data.users).toEqual([]); + }); + + it('should have correct default structure for AuthenticationDocument', async () => { + const authDb = (await import('../../../src/models/authentication.js')).default; + + expect(authDb.data).toMatchObject({ + users: [], + }); + }); +}); diff --git a/api/tests/unit/models/contentTypesMapper-lowdb.model.test.ts b/api/tests/unit/models/contentTypesMapper-lowdb.model.test.ts new file mode 100644 index 000000000..66ce8ce16 --- /dev/null +++ b/api/tests/unit/models/contentTypesMapper-lowdb.model.test.ts @@ -0,0 +1,43 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +vi.mock('lowdb/node', () => ({ + JSONFile: vi.fn().mockImplementation(function (this: unknown) { + return {}; + }), +})); + +vi.mock('../../../src/utils/lowdb-lodash.utils.js', () => ({ + default: vi.fn().mockImplementation(function ( + _adapter: unknown, + defaultData: { ContentTypesMappers: unknown[] } + ) { + return { + data: defaultData, + chain: {}, + }; + }), +})); + +describe('contentTypesMapper-lowdb model', () => { + beforeEach(() => { + vi.resetModules(); + }); + + it('should export db with ContentTypesMappers array in default data', async () => { + const contentTypesDb = (await import('../../../src/models/contentTypesMapper-lowdb.js')).default; + + expect(contentTypesDb).toBeDefined(); + expect(contentTypesDb.data).toBeDefined(); + expect(contentTypesDb.data).toHaveProperty('ContentTypesMappers'); + expect(Array.isArray(contentTypesDb.data.ContentTypesMappers)).toBe(true); + expect(contentTypesDb.data.ContentTypesMappers).toEqual([]); + }); + + it('should have correct default structure for ContentTypeMapperDocument', async () => { + const contentTypesDb = (await import('../../../src/models/contentTypesMapper-lowdb.js')).default; + + expect(contentTypesDb.data).toMatchObject({ + ContentTypesMappers: [], + }); + }); +}); diff --git a/api/tests/unit/models/project-lowdb.model.test.ts b/api/tests/unit/models/project-lowdb.model.test.ts new file mode 100644 index 000000000..6716269fb --- /dev/null +++ b/api/tests/unit/models/project-lowdb.model.test.ts @@ -0,0 +1,43 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +vi.mock('lowdb/node', () => ({ + JSONFile: vi.fn().mockImplementation(function (this: unknown) { + return {}; + }), +})); + +vi.mock('../../../src/utils/lowdb-lodash.utils.js', () => ({ + default: vi.fn().mockImplementation(function ( + _adapter: unknown, + defaultData: { projects: unknown[] } + ) { + return { + data: defaultData, + chain: {}, + }; + }), +})); + +describe('project-lowdb model', () => { + beforeEach(() => { + vi.resetModules(); + }); + + it('should export db with projects array in default data', async () => { + const projectDb = (await import('../../../src/models/project-lowdb.js')).default; + + expect(projectDb).toBeDefined(); + expect(projectDb.data).toBeDefined(); + expect(projectDb.data).toHaveProperty('projects'); + expect(Array.isArray(projectDb.data.projects)).toBe(true); + expect(projectDb.data.projects).toEqual([]); + }); + + it('should have correct default structure for ProjectDocument', async () => { + const projectDb = (await import('../../../src/models/project-lowdb.js')).default; + + expect(projectDb.data).toMatchObject({ + projects: [], + }); + }); +}); diff --git a/api/tests/unit/routes/auth.routes.test.ts b/api/tests/unit/routes/auth.routes.test.ts new file mode 100644 index 000000000..b8a37e5bb --- /dev/null +++ b/api/tests/unit/routes/auth.routes.test.ts @@ -0,0 +1,44 @@ +import { describe, it, expect, vi, beforeAll } from 'vitest'; + +vi.mock('../../../src/controllers/auth.controller.js', () => ({ + authController: { + login: vi.fn((_req: any, res: any) => res.status(200).json({ ok: true })), + RequestSms: vi.fn((_req: any, res: any) => res.status(200).json({ ok: true })), + }, +})); + +vi.mock('../../../src/validators/index.js', () => ({ + default: () => (_req: any, _res: any, next: any) => next(), +})); + +vi.mock('../../../src/utils/async-router.utils.js', () => ({ + asyncRouter: (fn: any) => fn, +})); + +describe('auth.routes', () => { + let router: any; + + beforeAll(async () => { + const mod = await import('../../../src/routes/auth.routes.js'); + router = mod.default; + }); + + it('should export an Express router', () => { + expect(router).toBeDefined(); + expect(typeof router).toBe('function'); + }); + + it('should register POST /user-session', () => { + const postRoutes = router.stack + .filter((layer: any) => layer.route?.methods?.post) + .map((layer: any) => layer.route.path); + expect(postRoutes).toContain('/user-session'); + }); + + it('should register POST /request-token-sms', () => { + const postRoutes = router.stack + .filter((layer: any) => layer.route?.methods?.post) + .map((layer: any) => layer.route.path); + expect(postRoutes).toContain('/request-token-sms'); + }); +}); diff --git a/api/tests/unit/routes/contentMapper.routes.test.ts b/api/tests/unit/routes/contentMapper.routes.test.ts new file mode 100644 index 000000000..5b9474f8b --- /dev/null +++ b/api/tests/unit/routes/contentMapper.routes.test.ts @@ -0,0 +1,104 @@ +import { describe, it, expect, vi, beforeAll } from 'vitest'; + +vi.mock('../../../src/controllers/projects.contentMapper.controller.js', () => ({ + contentMapperController: { + putTestData: vi.fn((_req: any, res: any) => res.status(200).json({})), + getContentTypes: vi.fn((_req: any, res: any) => res.status(200).json({})), + getFieldMapping: vi.fn((_req: any, res: any) => res.status(200).json({})), + getExistingContentTypes: vi.fn((_req: any, res: any) => res.status(200).json({})), + getExistingGlobalFields: vi.fn((_req: any, res: any) => res.status(200).json({})), + getExistingTaxonomies: vi.fn((_req: any, res: any) => res.status(200).json({})), + putContentTypeFields: vi.fn((_req: any, res: any) => res.status(200).json({})), + resetContentType: vi.fn((_req: any, res: any) => res.status(200).json({})), + removeContentMapper: vi.fn((_req: any, res: any) => res.status(200).json({})), + updateContentMapper: vi.fn((_req: any, res: any) => res.status(200).json({})), + }, +})); + +vi.mock('../../../src/utils/async-router.utils.js', () => ({ + asyncRouter: (fn: any) => fn, +})); + +describe('contentMapper.routes', () => { + let router: any; + + beforeAll(async () => { + const mod = await import('../../../src/routes/contentMapper.routes.js'); + router = mod.default; + }); + + it('should export an Express router', () => { + expect(router).toBeDefined(); + expect(typeof router).toBe('function'); + }); + + it('should register POST /createDummyData/:projectId', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.post) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/createDummyData/:projectId'); + }); + + it('should register GET /contentTypes/:projectId/:skip/:limit/:searchText?', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.get) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/contentTypes/:projectId/:skip/:limit/:searchText?'); + }); + + it('should register GET /fieldMapping/:projectId/:contentTypeId/:skip/:limit/:searchText?', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.get) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/fieldMapping/:projectId/:contentTypeId/:skip/:limit/:searchText?'); + }); + + it('should register GET /:projectId/contentTypes/:contentTypeUid?', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.get) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/:projectId/contentTypes/:contentTypeUid?'); + }); + + it('should register GET /:projectId/globalFields/:globalFieldUid?', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.get) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/:projectId/globalFields/:globalFieldUid?'); + }); + + it('should register GET /:projectId/taxonomies', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.get) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/:projectId/taxonomies'); + }); + + it('should register PUT /contentTypes/:orgId/:projectId/:contentTypeId', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.put) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/contentTypes/:orgId/:projectId/:contentTypeId'); + }); + + it('should register PUT /resetFields/:orgId/:projectId/:contentTypeId', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.put) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/resetFields/:orgId/:projectId/:contentTypeId'); + }); + + it('should register GET /:orgId/:projectId/content-mapper', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.get) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/:orgId/:projectId/content-mapper'); + }); + + it('should register PATCH /:orgId/:projectId/mapper_keys', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.patch) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/:orgId/:projectId/mapper_keys'); + }); +}); diff --git a/api/tests/unit/routes/migration.routes.test.ts b/api/tests/unit/routes/migration.routes.test.ts new file mode 100644 index 000000000..85ce2f391 --- /dev/null +++ b/api/tests/unit/routes/migration.routes.test.ts @@ -0,0 +1,92 @@ +import { describe, it, expect, vi, beforeAll } from 'vitest'; + +vi.mock('../../../src/controllers/migration.controller.js', () => ({ + migrationController: { + startTestMigration: vi.fn((_req: any, res: any) => res.status(200).json({})), + deleteTestStack: vi.fn((_req: any, res: any) => res.status(200).json({})), + createTestStack: vi.fn((_req: any, res: any) => res.status(200).json({})), + startMigration: vi.fn((_req: any, res: any) => res.status(200).json({})), + getLogs: vi.fn((_req: any, res: any) => res.status(200).json({})), + getAuditData: vi.fn((_req: any, res: any) => res.status(200).json({})), + saveLocales: vi.fn((_req: any, res: any) => res.status(200).json({})), + saveMappedLocales: vi.fn((_req: any, res: any) => res.status(200).json({})), + }, +})); + +vi.mock('../../../src/utils/async-router.utils.js', () => ({ + asyncRouter: (fn: any) => fn, +})); + +describe('migration.routes', () => { + let router: any; + + beforeAll(async () => { + const mod = await import('../../../src/routes/migration.routes.js'); + router = mod.default; + }); + + it('should export an Express router', () => { + expect(router).toBeDefined(); + expect(typeof router).toBe('function'); + }); + + it('should register POST /test-stack/:orgId/:projectId (startTestMigration)', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.post) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/test-stack/:orgId/:projectId'); + }); + + it('should register POST /test-stack/:projectId (deleteTestStack)', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.post) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/test-stack/:projectId'); + }); + + it('should register POST /create-test-stack/:orgId/:projectId', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.post) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/create-test-stack/:orgId/:projectId'); + }); + + it('should register POST /start/:orgId/:projectId', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.post) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/start/:orgId/:projectId'); + }); + + it('should register GET /get_migration_logs/...', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.get) + .map((layer: any) => layer.route.path); + expect(routes).toContain( + '/get_migration_logs/:orgId/:projectId/:stackId/:skip/:limit/:startIndex/:stopIndex/:searchText/:filter' + ); + }); + + it('should register GET /get_audit_data/...', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.get) + .map((layer: any) => layer.route.path); + expect(routes).toContain( + '/get_audit_data/:orgId/:projectId/:stackId/:moduleName/:skip/:limit/:startIndex/:stopIndex/:searchText/:filter' + ); + }); + + it('should register POST /localeMapper/:projectId', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.post) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/localeMapper/:projectId'); + }); + + it('should register POST /updateLocales/:projectId', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.post) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/updateLocales/:projectId'); + }); +}); diff --git a/api/tests/unit/routes/org.routes.test.ts b/api/tests/unit/routes/org.routes.test.ts new file mode 100644 index 000000000..30ef54187 --- /dev/null +++ b/api/tests/unit/routes/org.routes.test.ts @@ -0,0 +1,76 @@ +import { describe, it, expect, vi, beforeAll } from 'vitest'; + +vi.mock('../../../src/controllers/org.controller.js', () => ({ + orgController: { + getAllStacks: vi.fn((_req: any, res: any) => res.status(200).json([])), + createStack: vi.fn((_req: any, res: any) => res.status(201).json({})), + getLocales: vi.fn((_req: any, res: any) => res.status(200).json([])), + getStackStatus: vi.fn((_req: any, res: any) => res.status(200).json({})), + getStackLocale: vi.fn((_req: any, res: any) => res.status(200).json([])), + getOrgDetails: vi.fn((_req: any, res: any) => res.status(200).json({})), + }, +})); + +vi.mock('../../../src/validators/index.js', () => ({ + default: () => (_req: any, _res: any, next: any) => next(), +})); + +vi.mock('../../../src/utils/async-router.utils.js', () => ({ + asyncRouter: (fn: any) => fn, +})); + +describe('org.routes', () => { + let router: any; + + beforeAll(async () => { + const mod = await import('../../../src/routes/org.routes.js'); + router = mod.default; + }); + + it('should export an Express router', () => { + expect(router).toBeDefined(); + expect(typeof router).toBe('function'); + }); + + it('should register GET /stacks/:searchText?', () => { + const getRoutes = router.stack + .filter((layer: any) => layer.route?.methods?.get) + .map((layer: any) => layer.route.path); + expect(getRoutes).toContain('/stacks/:searchText?'); + }); + + it('should register POST /stacks', () => { + const postRoutes = router.stack + .filter((layer: any) => layer.route?.methods?.post) + .map((layer: any) => layer.route.path); + expect(postRoutes).toContain('/stacks'); + }); + + it('should register GET /locales', () => { + const getRoutes = router.stack + .filter((layer: any) => layer.route?.methods?.get) + .map((layer: any) => layer.route.path); + expect(getRoutes).toContain('/locales'); + }); + + it('should register POST /stack_status', () => { + const postRoutes = router.stack + .filter((layer: any) => layer.route?.methods?.post) + .map((layer: any) => layer.route.path); + expect(postRoutes).toContain('/stack_status'); + }); + + it('should register GET /get_stack_locales', () => { + const getRoutes = router.stack + .filter((layer: any) => layer.route?.methods?.get) + .map((layer: any) => layer.route.path); + expect(getRoutes).toContain('/get_stack_locales'); + }); + + it('should register GET /get_org_details', () => { + const getRoutes = router.stack + .filter((layer: any) => layer.route?.methods?.get) + .map((layer: any) => layer.route.path); + expect(getRoutes).toContain('/get_org_details'); + }); +}); diff --git a/api/tests/unit/routes/projects.routes.test.ts b/api/tests/unit/routes/projects.routes.test.ts new file mode 100644 index 000000000..94425b4fc --- /dev/null +++ b/api/tests/unit/routes/projects.routes.test.ts @@ -0,0 +1,156 @@ +import { describe, it, expect, vi, beforeAll } from 'vitest'; + +vi.mock('../../../src/controllers/projects.controller.js', () => ({ + projectController: { + getAllProjects: vi.fn((_req: any, res: any) => res.status(200).json([])), + getProject: vi.fn((_req: any, res: any) => res.status(200).json({})), + createProject: vi.fn((_req: any, res: any) => res.status(201).json({})), + updateProject: vi.fn((_req: any, res: any) => res.status(200).json({})), + updateLegacyCMS: vi.fn((_req: any, res: any) => res.status(200).json({})), + updateAffix: vi.fn((_req: any, res: any) => res.status(200).json({})), + affixConfirmation: vi.fn((_req: any, res: any) => res.status(200).json({})), + updateFileFormat: vi.fn((_req: any, res: any) => res.status(200).json({})), + fileformatConfirmation: vi.fn((_req: any, res: any) => res.status(200).json({})), + updateDestinationStack: vi.fn((_req: any, res: any) => res.status(200).json({})), + updateCurrentStep: vi.fn((_req: any, res: any) => res.status(200).json({})), + deleteProject: vi.fn((_req: any, res: any) => res.status(200).json({})), + revertProject: vi.fn((_req: any, res: any) => res.status(200).json({})), + updateStackDetails: vi.fn((_req: any, res: any) => res.status(200).json({})), + updateMigrationExecution: vi.fn((_req: any, res: any) => res.status(200).json({})), + getMigratedStacks: vi.fn((_req: any, res: any) => res.status(200).json({})), + }, +})); + +vi.mock('../../../src/validators/index.js', () => ({ + default: () => (_req: any, _res: any, next: any) => next(), +})); + +vi.mock('../../../src/utils/async-router.utils.js', () => ({ + asyncRouter: (fn: any) => fn, +})); + +describe('projects.routes', () => { + let router: any; + + beforeAll(async () => { + const mod = await import('../../../src/routes/projects.routes.js'); + router = mod.default; + }); + + it('should export an Express router', () => { + expect(router).toBeDefined(); + expect(typeof router).toBe('function'); + }); + + it('should register GET /', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.get && layer.route.path === '/') + .map((layer: any) => layer.route.path); + expect(routes).toContain('/'); + }); + + it('should register GET /:projectId', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.get) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/:projectId'); + }); + + it('should register POST /', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.post && layer.route.path === '/') + .map((layer: any) => layer.route.path); + expect(routes).toContain('/'); + }); + + it('should register PUT /:projectId', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.put && layer.route.path === '/:projectId') + .map((layer: any) => layer.route.path); + expect(routes).toContain('/:projectId'); + }); + + it('should register PUT /:projectId/legacy-cms', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.put) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/:projectId/legacy-cms'); + }); + + it('should register PUT /:projectId/affix', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.put) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/:projectId/affix'); + }); + + it('should register PUT /:projectId/affix_confirmation', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.put) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/:projectId/affix_confirmation'); + }); + + it('should register PUT /:projectId/file-format', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.put) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/:projectId/file-format'); + }); + + it('should register PUT /:projectId/fileformat_confirmation', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.put) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/:projectId/fileformat_confirmation'); + }); + + it('should register PUT /:projectId/destination-stack', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.put) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/:projectId/destination-stack'); + }); + + it('should register PUT /:projectId/current-step', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.put) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/:projectId/current-step'); + }); + + it('should register DELETE /:projectId', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.delete) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/:projectId'); + }); + + it('should register PATCH /:projectId', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.patch) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/:projectId'); + }); + + it('should register PATCH /:projectId/stack-details', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.patch) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/:projectId/stack-details'); + }); + + it('should register PUT /:projectId/migration-excution', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.put) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/:projectId/migration-excution'); + }); + + it('should register GET /:projectId/get-migrated-stacks', () => { + const routes = router.stack + .filter((layer: any) => layer.route?.methods?.get) + .map((layer: any) => layer.route.path); + expect(routes).toContain('/:projectId/get-migrated-stacks'); + }); +}); diff --git a/api/tests/unit/routes/user.routes.test.ts b/api/tests/unit/routes/user.routes.test.ts new file mode 100644 index 000000000..be61b2516 --- /dev/null +++ b/api/tests/unit/routes/user.routes.test.ts @@ -0,0 +1,32 @@ +import { describe, it, expect, vi, beforeAll } from 'vitest'; + +vi.mock('../../../src/controllers/user.controller.js', () => ({ + userController: { + getUserProfile: vi.fn((_req: any, res: any) => res.status(200).json({ ok: true })), + }, +})); + +vi.mock('../../../src/utils/async-router.utils.js', () => ({ + asyncRouter: (fn: any) => fn, +})); + +describe('user.routes', () => { + let router: any; + + beforeAll(async () => { + const mod = await import('../../../src/routes/user.routes.js'); + router = mod.default; + }); + + it('should export an Express router', () => { + expect(router).toBeDefined(); + expect(typeof router).toBe('function'); + }); + + it('should register GET /profile', () => { + const getRoutes = router.stack + .filter((layer: any) => layer.route?.methods?.get) + .map((layer: any) => layer.route.path); + expect(getRoutes).toContain('/profile'); + }); +}); diff --git a/api/tests/unit/services/auth.service.test.ts b/api/tests/unit/services/auth.service.test.ts new file mode 100644 index 000000000..a1654b287 --- /dev/null +++ b/api/tests/unit/services/auth.service.test.ts @@ -0,0 +1,228 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockHttps, mockGenerateToken, mockAuthModelRead, mockAuthModelUpdate, mockChainValue } = vi.hoisted(() => ({ + mockHttps: vi.fn(), + mockGenerateToken: vi.fn(), + mockAuthModelRead: vi.fn(), + mockAuthModelUpdate: vi.fn(), + mockChainValue: vi.fn(), +})); + +vi.mock('../../../src/utils/https.utils.js', () => ({ default: mockHttps })); +vi.mock('../../../src/utils/jwt.utils.js', () => ({ generateToken: mockGenerateToken })); +vi.mock('../../../src/utils/logger.js', () => ({ + default: { error: vi.fn(), info: vi.fn(), warn: vi.fn() }, +})); +vi.mock('../../../src/config/index.js', () => ({ + config: { + CS_API: { NA: 'https://api.contentstack.io/v3', EU: 'https://eu-api.contentstack.io/v3' }, + APP_TOKEN_KEY: 'test-secret', + APP_TOKEN_EXP: '2d', + }, +})); +vi.mock('../../../src/models/authentication.js', () => ({ + default: { + read: mockAuthModelRead, + update: mockAuthModelUpdate, + chain: { + get: vi.fn().mockReturnValue({ + findIndex: vi.fn().mockReturnValue({ value: mockChainValue }), + }), + }, + }, +})); + +import { authService } from '../../../src/services/auth.service.js'; + +describe('auth.service', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockAuthModelRead.mockResolvedValue(undefined); + mockAuthModelUpdate.mockImplementation((fn: any) => fn({ users: [] })); + mockChainValue.mockReturnValue(-1); + }); + + describe('login', () => { + const createReq = (body: any = {}) => ({ + body: { + email: 'test@example.com', + password: 'password123', + region: 'NA', + ...body, + }, + }); + + it('should return app_token on successful login with admin org', async () => { + mockHttps.mockResolvedValue({ + status: 200, + data: { + user: { + uid: 'user-123', + email: 'test@example.com', + authtoken: 'cs-token', + organizations: [ + { uid: 'org-1', name: 'Org 1', org_roles: [{ admin: true }], is_owner: false }, + ], + }, + }, + }); + mockGenerateToken.mockReturnValue('jwt-token'); + + const result = await authService.login(createReq() as any); + + expect(result.status).toBe(200); + expect(result.data.app_token).toBe('jwt-token'); + expect(result.data.message).toBe('Login Successful.'); + expect(mockGenerateToken).toHaveBeenCalledWith({ region: 'NA', user_id: 'user-123' }); + }); + + it('should return app_token for owner org', async () => { + mockHttps.mockResolvedValue({ + status: 200, + data: { + user: { + uid: 'user-123', + email: 'test@example.com', + authtoken: 'cs-token', + organizations: [ + { uid: 'org-1', name: 'Org 1', org_roles: [], is_owner: true }, + ], + }, + }, + }); + mockGenerateToken.mockReturnValue('jwt-token'); + + const result = await authService.login(createReq() as any); + + expect(result.status).toBe(200); + expect(result.data.app_token).toBe('jwt-token'); + }); + + it('should throw BadRequestError for non-admin/non-owner user', async () => { + mockHttps.mockResolvedValue({ + status: 200, + data: { + user: { + uid: 'user-123', + email: 'test@example.com', + authtoken: 'cs-token', + organizations: [ + { uid: 'org-1', name: 'Org 1', org_roles: [{ admin: false }], is_owner: false }, + ], + }, + }, + }); + + await expect(authService.login(createReq() as any)).rejects.toThrow( + "Sorry, You Don't have admin access in any of the Organisation" + ); + }); + + it('should return error data when CS API returns error', async () => { + mockHttps.mockRejectedValue({ + response: { data: { error_message: 'Invalid credentials' }, status: 401 }, + }); + + const result = await authService.login(createReq() as any); + + expect(result.status).toBe(401); + expect(result.data.error_message).toBe('Invalid credentials'); + }); + + it('should handle SUPPORT_DOC status response', async () => { + mockHttps.mockResolvedValue({ + status: 294, + data: { notice: 'Support doc needed' }, + }); + + const result = await authService.login(createReq() as any); + + expect(result.status).toBe(294); + expect(result.data.notice).toBe('Support doc needed'); + }); + + it('should handle 2FA token flow', async () => { + mockHttps.mockResolvedValue({ + status: 200, + data: { + user: { + uid: 'user-123', + email: 'test@example.com', + authtoken: 'cs-token', + organizations: [ + { uid: 'org-1', name: 'Org 1', org_roles: [{ admin: true }], is_owner: false }, + ], + }, + }, + }); + mockGenerateToken.mockReturnValue('jwt-2fa'); + + const result = await authService.login( + createReq({ tfa_token: '123456' }) as any + ); + + expect(result.status).toBe(200); + expect(result.data.app_token).toBe('jwt-2fa'); + expect(mockHttps).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + user: expect.objectContaining({ tfa_token: '123456' }), + }), + }) + ); + }); + + it('should return raw response when organizations is undefined', async () => { + mockHttps.mockResolvedValue({ + status: 200, + data: { user: { uid: 'u1' } }, + }); + + const result = await authService.login(createReq() as any); + + expect(result.status).toBe(200); + expect(result.data).toEqual({ user: { uid: 'u1' } }); + }); + }); + + describe('requestSms', () => { + const createReq = (body: any = {}) => ({ + body: { + email: 'test@example.com', + password: 'password123', + region: 'NA', + ...body, + }, + }); + + it('should return success response on successful SMS request', async () => { + mockHttps.mockResolvedValue({ + status: 200, + data: { message: 'SMS sent' }, + }); + + const result = await authService.requestSms(createReq() as any); + + expect(result.status).toBe(200); + expect(result.data.message).toBe('SMS sent'); + }); + + it('should return error data when CS API returns error', async () => { + mockHttps.mockRejectedValue({ + response: { data: { error_message: 'Rate limited' }, status: 429 }, + }); + + const result = await authService.requestSms(createReq() as any); + + expect(result.status).toBe(429); + }); + + it('should throw InternalServerError on unexpected exception', async () => { + mockHttps.mockImplementation(() => { + throw new Error('Unexpected'); + }); + + await expect(authService.requestSms(createReq() as any)).rejects.toThrow(); + }); + }); +}); diff --git a/api/tests/unit/services/contentMapper.service.test.ts b/api/tests/unit/services/contentMapper.service.test.ts new file mode 100644 index 000000000..d48502d5f --- /dev/null +++ b/api/tests/unit/services/contentMapper.service.test.ts @@ -0,0 +1,820 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { + mockHttps, + mockGetAuthToken, + mockGetProjectUtil, + mockFetchAllPaginatedData, + mockProjectRead, + mockProjectUpdate, + mockProjectWrite, + mockContentTypesMapperRead, + mockContentTypesMapperUpdate, + mockFieldMapperRead, + mockFieldMapperUpdate, + mockUuidv4, + mockFsPromises, +} = vi.hoisted(() => ({ + mockHttps: vi.fn(), + mockGetAuthToken: vi.fn(), + mockGetProjectUtil: vi.fn(), + mockFetchAllPaginatedData: vi.fn(), + mockProjectRead: vi.fn(), + mockProjectUpdate: vi.fn(), + mockProjectWrite: vi.fn(), + mockContentTypesMapperRead: vi.fn(), + mockContentTypesMapperUpdate: vi.fn(), + mockFieldMapperRead: vi.fn(), + mockFieldMapperUpdate: vi.fn(), + mockUuidv4: vi.fn(() => 'uuid-123'), + mockFsPromises: { lstat: vi.fn(), readFile: vi.fn(), realpath: vi.fn() }, +})); + +vi.mock('../../../src/utils/https.utils.js', () => ({ default: mockHttps })); +vi.mock('../../../src/utils/auth.utils.js', () => ({ default: mockGetAuthToken })); +vi.mock('../../../src/utils/get-project.utils.js', () => ({ default: mockGetProjectUtil })); +vi.mock('../../../src/utils/pagination.utils.js', () => ({ default: mockFetchAllPaginatedData })); +vi.mock('../../../src/utils/logger.js', () => ({ + default: { error: vi.fn(), info: vi.fn(), warn: vi.fn() }, +})); +vi.mock('../../../src/config/index.js', () => ({ + config: { CS_API: { NA: 'https://api.contentstack.io/v3' } }, +})); +vi.mock('uuid', () => ({ v4: mockUuidv4 })); + +vi.mock('../../../src/models/project-lowdb.js', () => { + const mockChainGet = vi.fn(); + return { + default: { + read: mockProjectRead, + update: mockProjectUpdate, + write: mockProjectWrite, + chain: { get: mockChainGet }, + data: { projects: [] }, + }, + }; +}); + +vi.mock('../../../src/models/contentTypesMapper-lowdb.js', () => { + const mockChainGet = vi.fn(); + return { + default: { + read: mockContentTypesMapperRead, + update: mockContentTypesMapperUpdate, + write: vi.fn(), + chain: { get: mockChainGet }, + data: { ContentTypesMappers: [] }, + }, + ContentTypesMapper: {}, + }; +}); + +vi.mock('../../../src/models/FieldMapper.js', () => { + const mockChainGet = vi.fn(); + return { + default: { + read: mockFieldMapperRead, + update: mockFieldMapperUpdate, + write: vi.fn(), + chain: { get: mockChainGet }, + data: { field_mapper: [] }, + }, + }; +}); + +vi.mock('fs', () => ({ + default: { promises: mockFsPromises }, + promises: mockFsPromises, +})); + +import { contentMapperService } from '../../../src/services/contentMapper.service.js'; +import ProjectModelLowdb from '../../../src/models/project-lowdb.js'; +import ContentTypesMapperModelLowdb from '../../../src/models/contentTypesMapper-lowdb.js'; +import FieldMapperModel from '../../../src/models/FieldMapper.js'; + +const createChain = (opts: { + find?: unknown; + findIndex?: number; + value?: unknown; +}) => { + const findValue = opts.find !== undefined ? opts.find : null; + const findIndexValue = opts.findIndex !== undefined ? opts.findIndex : -1; + return { + find: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(findValue) }), + findIndex: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(findIndexValue) }), + filter: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue([]) }), + }; +}; + +describe('contentMapper.service', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockGetAuthToken.mockResolvedValue('cs-auth-token'); + mockProjectRead.mockResolvedValue(undefined); + mockContentTypesMapperRead.mockResolvedValue(undefined); + mockFieldMapperRead.mockResolvedValue(undefined); + mockProjectUpdate.mockImplementation(async (fn: (d: any) => void) => { + const data = ProjectModelLowdb.data as any; + if (!data.projects) data.projects = []; + while (data.projects.length < 2) data.projects.push({}); + fn(data); + }); + mockContentTypesMapperUpdate.mockImplementation(async (fn: (d: any) => void) => { + const data = ContentTypesMapperModelLowdb.data as any; + if (!data.ContentTypesMappers) data.ContentTypesMappers = []; + while (data.ContentTypesMappers.length < 2) data.ContentTypesMappers.push({}); + fn(data); + }); + mockFieldMapperUpdate.mockImplementation(async (fn: (d: any) => void) => { + const data = FieldMapperModel.data as any; + if (!data.field_mapper) data.field_mapper = []; + fn(data); + }); + mockFetchAllPaginatedData.mockResolvedValue([]); + ProjectModelLowdb.data = { projects: [] }; + ContentTypesMapperModelLowdb.data = { ContentTypesMappers: [] }; + FieldMapperModel.data = { field_mapper: [] }; + (ProjectModelLowdb.chain.get as ReturnType).mockReturnValue(createChain({ find: null, findIndex: -1 })); + (ContentTypesMapperModelLowdb.chain.get as ReturnType).mockReturnValue(createChain({ find: null, findIndex: -1 })); + (FieldMapperModel.chain.get as ReturnType).mockReturnValue(createChain({ find: null, findIndex: -1 })); + }); + + describe('putTestData', () => { + it('should throw BadRequestError when contentTypes is not an array', async () => { + const req = { + params: { projectId: 'proj-1' }, + body: { contentTypes: 'invalid' }, + } as any; + + await expect(contentMapperService.putTestData(req)).rejects.toThrow('Invalid contentTypes: Expected an array.'); + }); + + it('should throw when project not found', async () => { + const req = { + params: { projectId: 'proj-999' }, + body: { + contentTypes: [ + { id: 'ct-1', otherCmsTitle: 'Blog', fieldMapping: [] }, + ], + }, + } as any; + + (ProjectModelLowdb.chain.get as ReturnType).mockReturnValue( + createChain({ findIndex: -1, find: null }) + ); + + await expect(contentMapperService.putTestData(req)).rejects.toThrow(); + }); + + it('should create content mappers successfully', async () => { + const project = { id: 'proj-1', content_mapper: [], destination_stack_id: 'stack-1' }; + ProjectModelLowdb.data.projects = [project]; + mockProjectWrite.mockResolvedValue(undefined); + + (ProjectModelLowdb.chain.get as ReturnType) + .mockReturnValueOnce(createChain({ findIndex: 0 })) + .mockReturnValueOnce(createChain({ find: project })); + + const req = { + params: { projectId: 'proj-1' }, + body: { + contentTypes: [ + { id: 'ct-1', otherCmsTitle: 'Blog', fieldMapping: [{ id: 'f1', otherCmsField: 'title' }] }, + ], + }, + } as any; + + const result = await contentMapperService.putTestData(req); + + expect(result.status).toBe(200); + expect(result.data).toBeDefined(); + }); + }); + + describe('getContentTypes', () => { + it('should throw when project not found', async () => { + const req = { + params: { projectId: 'proj-1', skip: 0, limit: 10 }, + } as any; + + (ProjectModelLowdb.chain.get as ReturnType).mockReturnValue( + createChain({ find: null }) + ); + + await expect(contentMapperService.getContentTypes(req)).rejects.toThrow('Sorry, the requested project does not exists.'); + }); + + it('should return content types when project has content mappers', async () => { + const project = { id: 'proj-1', content_mapper: ['ct-1'] }; + const contentMapper = { id: 'ct-1', projectId: 'proj-1', otherCmsTitle: 'Blog' }; + + (ProjectModelLowdb.chain.get as ReturnType).mockReturnValue(createChain({ find: project })); + (ContentTypesMapperModelLowdb.chain.get as ReturnType).mockReturnValue( + createChain({ find: contentMapper }) + ); + + const req = { + params: { projectId: 'proj-1', skip: 0, limit: 10 }, + } as any; + + const result = await contentMapperService.getContentTypes(req); + + expect(result.status).toBe(200); + expect(result.count).toBe(1); + expect(result.contentTypes).toHaveLength(1); + }); + + it('should filter by search when searchText provided', async () => { + const project = { id: 'proj-1', content_mapper: ['ct-1'] }; + const contentMapper = { id: 'ct-1', projectId: 'proj-1', otherCmsTitle: 'Blog' }; + + (ProjectModelLowdb.chain.get as ReturnType).mockReturnValue(createChain({ find: project })); + (ContentTypesMapperModelLowdb.chain.get as ReturnType).mockReturnValue( + createChain({ find: contentMapper }) + ); + + const req = { + params: { projectId: 'proj-1', skip: 0, limit: 10, searchText: 'blog' }, + } as any; + + const result = await contentMapperService.getContentTypes(req); + + expect(result.status).toBe(200); + expect(result.contentTypes).toBeDefined(); + }); + }); + + describe('getFieldMapping', () => { + it('should throw when content type not found', async () => { + (ContentTypesMapperModelLowdb.chain.get as ReturnType).mockReturnValue( + createChain({ find: null }) + ); + + const req = { + params: { projectId: 'proj-1', contentTypeId: 'ct-1', skip: 0, limit: 10 }, + } as any; + + await expect(contentMapperService.getFieldMapping(req)).rejects.toThrow('ContentType does not exist'); + }); + + it('should return field mapping when content type exists', async () => { + const contentType = { id: 'ct-1', projectId: 'proj-1', fieldMapping: ['f1'] }; + const fieldData = { id: 'f1', otherCmsField: 'title', contentstackField: 'title' }; + + (ContentTypesMapperModelLowdb.chain.get as ReturnType) + .mockReturnValue(createChain({ find: contentType })); + + (FieldMapperModel.chain.get as ReturnType) + .mockReturnValue(createChain({ find: fieldData })); + + const req = { + params: { projectId: 'proj-1', contentTypeId: 'ct-1', skip: 0, limit: 10 }, + } as any; + + const result = await contentMapperService.getFieldMapping(req); + + expect(result.status).toBe(200); + expect(result.fieldMapping).toBeDefined(); + }); + + it('should filter by search when searchText provided', async () => { + const contentType = { id: 'ct-1', projectId: 'proj-1', fieldMapping: ['f1'] }; + const fieldData = { id: 'f1', otherCmsField: 'Title', contentstackField: 'title' }; + + (ContentTypesMapperModelLowdb.chain.get as ReturnType) + .mockReturnValue(createChain({ find: contentType })); + (FieldMapperModel.chain.get as ReturnType) + .mockReturnValue(createChain({ find: fieldData })); + + const req = { + params: { projectId: 'proj-1', contentTypeId: 'ct-1', skip: 0, limit: 10, searchText: 'title' }, + } as any; + + const result = await contentMapperService.getFieldMapping(req); + expect(result.status).toBe(200); + }); + }); + + describe('getExistingContentTypes', () => { + it('should return content types successfully', async () => { + const project = { id: 'proj-1', destination_stack_id: 'stack-1' }; + (ProjectModelLowdb.chain.get as ReturnType).mockReturnValue( + createChain({ find: project }) + ); + mockFetchAllPaginatedData.mockResolvedValue([ + { uid: 'ct-1', title: 'Blog', schema: {} }, + ]); + + const req = { + params: { projectId: 'proj-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-1' } }, + } as any; + + const result = await contentMapperService.getExistingContentTypes(req); + + expect(result.contentTypes).toHaveLength(1); + expect(result.contentTypes[0].uid).toBe('ct-1'); + }); + + it('should fetch selected content type when contentTypeUid provided', async () => { + const project = { id: 'proj-1', destination_stack_id: 'stack-1' }; + (ProjectModelLowdb.chain.get as ReturnType).mockReturnValue( + createChain({ find: project }) + ); + mockFetchAllPaginatedData.mockResolvedValue([]); + mockHttps.mockResolvedValue({ + data: { content_type: { uid: 'ct-1', title: 'Blog', schema: {} } }, + }); + + const req = { + params: { projectId: 'proj-1', contentTypeUid: 'ct-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-1' } }, + } as any; + + const result = await contentMapperService.getExistingContentTypes(req); + + expect(result.contentTypes).toBeDefined(); + expect(result.selectedContentType).toBeDefined(); + }); + }); + + describe('getExistingGlobalFields', () => { + it('should return 400 when projectId is missing', async () => { + const req = { + params: {}, + body: { token_payload: { region: 'NA', user_id: 'user-1' } }, + } as any; + + const result = await contentMapperService.getExistingGlobalFields(req); + + expect(result.status).toBe(400); + expect(result.data).toContain('Project ID'); + }); + + it('should return 400 when token payload is missing', async () => { + const req = { + params: { projectId: 'proj-1' }, + body: {}, + } as any; + + const result = await contentMapperService.getExistingGlobalFields(req); + + expect(result.status).toBe(400); + expect(result.data).toContain('Token payload'); + }); + + it('should return 404 when project not found', async () => { + (ProjectModelLowdb.chain.get as ReturnType).mockReturnValue( + createChain({ find: null }) + ); + + const req = { + params: { projectId: 'proj-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-1' } }, + } as any; + + const result = await contentMapperService.getExistingGlobalFields(req); + + expect(result.status).toBe(404); + expect(result.data).toBe('Project not found'); + }); + + it('should return 400 when stackId is missing', async () => { + const project = { id: 'proj-1', destination_stack_id: null }; + (ProjectModelLowdb.chain.get as ReturnType).mockReturnValue( + createChain({ find: project }) + ); + + const req = { + params: { projectId: 'proj-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-1' } }, + } as any; + + const result = await contentMapperService.getExistingGlobalFields(req); + + expect(result.status).toBe(400); + expect(result.data).toContain('Destination stack ID'); + }); + + it('should return global fields successfully', async () => { + const project = { id: 'proj-1', destination_stack_id: 'stack-1' }; + (ProjectModelLowdb.chain.get as ReturnType).mockReturnValue( + createChain({ find: project }) + ); + mockFetchAllPaginatedData.mockResolvedValue([ + { uid: 'gf-1', title: 'SEO', schema: {} }, + ]); + + const req = { + params: { projectId: 'proj-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-1' } }, + } as any; + + const result = await contentMapperService.getExistingGlobalFields(req); + + expect(result.globalFields).toHaveLength(1); + expect(result.globalFields[0].uid).toBe('gf-1'); + }); + }); + + describe('updateContentType', () => { + it('should return 400 when status prevents update', async () => { + mockGetProjectUtil.mockResolvedValue(0); + ProjectModelLowdb.data.projects = [ + { status: 5, current_step: 2 }, + ]; + + const req = { + params: { orgId: 'org-1', projectId: 'proj-1', contentTypeId: 'ct-1' }, + body: { + token_payload: { region: 'NA', user_id: 'user-1' }, + contentTypeData: { otherCmsTitle: 'Blog' }, + }, + } as any; + + const result = await contentMapperService.updateContentType(req); + + expect(result.status).toBe(400); + expect(result.message).toContain('content mapping is restricted'); + }); + + it('should return 400 when contentTypeData is empty', async () => { + mockGetProjectUtil.mockResolvedValue(0); + ProjectModelLowdb.data.projects = [ + { status: 1, current_step: 3 }, + ]; + + const req = { + params: { orgId: 'org-1', projectId: 'proj-1', contentTypeId: 'ct-1' }, + body: { + token_payload: { region: 'NA', user_id: 'user-1' }, + contentTypeData: null, + }, + } as any; + + const result = await contentMapperService.updateContentType(req); + + expect(result.status).toBe(400); + expect(result.message).toContain('valid ContentType'); + }); + + it('should return 400 when field has invalid contentstackFieldType', async () => { + mockGetProjectUtil.mockResolvedValue(0); + ProjectModelLowdb.data.projects = [{ status: 1, current_step: 3 }]; + ContentTypesMapperModelLowdb.data.ContentTypesMappers = [{ id: 'ct-1', status: 1 }]; + + (ContentTypesMapperModelLowdb.chain.get as ReturnType) + .mockReturnValueOnce(createChain({ findIndex: 0 })) + .mockReturnValue(createChain({ find: { id: 'ct-1', projectId: 'proj-1', status: 1 } })); + + const req = { + params: { orgId: 'org-1', projectId: 'proj-1', contentTypeId: 'ct-1' }, + body: { + token_payload: { region: 'NA', user_id: 'user-1' }, + contentTypeData: { + otherCmsTitle: 'Blog', + fieldMapping: [{ id: 'f1', contentstackFieldType: '', contentstackFieldUid: '' }], + }, + }, + } as any; + + const result = await contentMapperService.updateContentType(req); + + expect(result.status).toBe(400); + }); + + it('should update content type successfully', async () => { + mockGetProjectUtil.mockResolvedValue(0); + ProjectModelLowdb.data.projects = [{ status: 1, current_step: 3 }]; + ContentTypesMapperModelLowdb.data.ContentTypesMappers = [{ id: 'ct-1', projectId: 'proj-1', status: 1 }]; + FieldMapperModel.data.field_mapper = [ + { id: 'f1', contentTypeId: 'ct-1', contentstackFieldType: 'text', contentstackFieldUid: 'f1' }, + ]; + + (ContentTypesMapperModelLowdb.chain.get as ReturnType) + .mockReturnValueOnce(createChain({ findIndex: 0 })) + .mockReturnValue(createChain({ find: { id: 'ct-1', projectId: 'proj-1', status: 1 } })); + + const req = { + params: { orgId: 'org-1', projectId: 'proj-1', contentTypeId: 'ct-1' }, + body: { + token_payload: { region: 'NA', user_id: 'user-1' }, + contentTypeData: { + otherCmsTitle: 'Blog', + contentstackTitle: 'Blog', + contentstackUid: 'ct-1', + fieldMapping: [ + { id: 'f1', contentTypeId: 'ct-1', contentstackFieldType: 'text', contentstackFieldUid: 'f1' }, + ], + }, + }, + } as any; + + const result = await contentMapperService.updateContentType(req); + + expect(result.status).toBe(200); + expect(result.data).toBeDefined(); + }); + }); + + describe('resetToInitialMapping', () => { + it('should throw when status prevents reset', async () => { + mockGetProjectUtil.mockResolvedValue(0); + ProjectModelLowdb.data.projects = [ + { status: 0, current_step: 2 }, + ]; + + const req = { + params: { orgId: 'org-1', projectId: 'proj-1', contentTypeId: 'ct-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-1' } }, + } as any; + + await expect(contentMapperService.resetToInitialMapping(req)).rejects.toThrow( + 'Reseting the content mapping is restricted' + ); + }); + + it('should reset successfully', async () => { + mockGetProjectUtil.mockResolvedValue(0); + ProjectModelLowdb.data.projects = [{ status: 1, current_step: 3 }]; + const contentTypeData = { + id: 'ct-1', + projectId: 'proj-1', + fieldMapping: ['f1'], + }; + const fieldData = { + id: 'f1', + otherCmsField: 'title', + backupFieldUid: 'buid', + backupFieldType: 'text', + advanced: { initial: {} }, + }; + + ContentTypesMapperModelLowdb.data.ContentTypesMappers = [contentTypeData]; + FieldMapperModel.data.field_mapper = [fieldData]; + + (ContentTypesMapperModelLowdb.chain.get as ReturnType) + .mockReturnValueOnce(createChain({ find: contentTypeData })) + .mockReturnValueOnce(createChain({ findIndex: 0 })); + (FieldMapperModel.chain.get as ReturnType).mockReturnValue(createChain({ find: fieldData })); + + const req = { + params: { orgId: 'org-1', projectId: 'proj-1', contentTypeId: 'ct-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-1' } }, + } as any; + + const result = await contentMapperService.resetToInitialMapping(req); + + expect(result.status).toBe(200); + expect(result.message).toContain('restored to its initial mapping'); + }); + }); + + describe('resetAllContentTypesMapping', () => { + it('should throw when content mapper is empty', async () => { + const project = { id: 'proj-1', content_mapper: [] }; + (ProjectModelLowdb.chain.get as ReturnType).mockReturnValue( + createChain({ find: project }) + ); + + await expect( + contentMapperService.resetAllContentTypesMapping('proj-1') + ).rejects.toThrow('content mapper id does not exists'); + }); + + it('should reset all content types successfully', async () => { + const project = { id: 'proj-1', content_mapper: ['ct-1'] }; + const contentType = { + id: 'ct-1', + projectId: 'proj-1', + fieldMapping: ['f1'], + }; + const fieldData = { id: 'f1', projectId: 'proj-1', backupFieldType: 'text' }; + + ContentTypesMapperModelLowdb.data.ContentTypesMappers = [{ ...contentType, contentstackTitle: 'Old' }]; + FieldMapperModel.data.field_mapper = [fieldData]; + + (ProjectModelLowdb.chain.get as ReturnType).mockReturnValue(createChain({ find: project })); + (ContentTypesMapperModelLowdb.chain.get as ReturnType) + .mockReturnValueOnce(createChain({ find: contentType })) + .mockReturnValueOnce(createChain({ findIndex: 0 })); + (FieldMapperModel.chain.get as ReturnType) + .mockReturnValueOnce(createChain({ find: fieldData })) + .mockReturnValueOnce(createChain({ findIndex: 0 })); + + const result = await contentMapperService.resetAllContentTypesMapping('proj-1'); + + expect(result).toEqual(project); + }); + }); + + describe('removeMapping', () => { + it('should throw when project not found', async () => { + (ProjectModelLowdb.chain.get as ReturnType).mockReturnValue( + createChain({ find: null }) + ); + + await expect(contentMapperService.removeMapping('proj-999')).rejects.toThrow( + 'Sorry, the requested project does not exists.' + ); + }); + + it('should remove mapping successfully', async () => { + const project = { id: 'proj-1', content_mapper: ['ct-1'] }; + const contentType = { id: 'ct-1', projectId: 'proj-1', fieldMapping: ['f1'] }; + + (ProjectModelLowdb.chain.get as ReturnType) + .mockReturnValueOnce(createChain({ find: project })) + .mockReturnValueOnce(createChain({ findIndex: 0 })); + (ContentTypesMapperModelLowdb.chain.get as ReturnType) + .mockReturnValueOnce(createChain({ find: contentType })) + .mockReturnValueOnce(createChain({ findIndex: 0 })); + (FieldMapperModel.chain.get as ReturnType).mockReturnValue( + createChain({ findIndex: 0 }) + ); + + const result = await contentMapperService.removeMapping('proj-1'); + + expect(result).toEqual(project); + }); + }); + + describe('removeContentMapper', () => { + it('should throw when project not found', async () => { + (ProjectModelLowdb.chain.get as ReturnType).mockReturnValue( + createChain({ find: null }) + ); + + const req = { params: { projectId: 'proj-999' } } as any; + + await expect(contentMapperService.removeContentMapper(req)).rejects.toThrow( + 'Sorry, the requested project does not exists.' + ); + }); + + it('should remove content mappers successfully', async () => { + const project = { id: 'proj-1', content_mapper: ['ct-1'] }; + const contentType = { id: 'ct-1', projectId: 'proj-1', fieldMapping: ['f1'] }; + + (ProjectModelLowdb.chain.get as ReturnType) + .mockReturnValueOnce(createChain({ find: project })) + .mockReturnValueOnce(createChain({ findIndex: 0 })); + (ContentTypesMapperModelLowdb.chain.get as ReturnType) + .mockReturnValueOnce(createChain({ find: contentType })) + .mockReturnValueOnce(createChain({ findIndex: 0 })); + (FieldMapperModel.chain.get as ReturnType).mockReturnValue( + createChain({ findIndex: 0 }) + ); + + const req = { params: { projectId: 'proj-1' } } as any; + + const result = await contentMapperService.removeContentMapper(req); + + expect(result).toEqual(project); + }); + }); + + describe('getSingleContentTypes', () => { + it('should return content type successfully', async () => { + const project = { id: 'proj-1', destination_stack_id: 'stack-1' }; + (ProjectModelLowdb.chain.get as ReturnType).mockReturnValue( + createChain({ find: project }) + ); + mockHttps.mockResolvedValue({ + data: { content_type: { title: 'Blog', uid: 'ct-1', schema: {} } }, + }); + + const req = { + params: { projectId: 'proj-1', contentTypeUid: 'ct-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-1' } }, + } as any; + + const result = await contentMapperService.getSingleContentTypes(req); + + expect(result.title).toBe('Blog'); + expect(result.uid).toBe('ct-1'); + }); + + it('should return error when https fails', async () => { + const project = { id: 'proj-1', destination_stack_id: 'stack-1' }; + (ProjectModelLowdb.chain.get as ReturnType).mockReturnValue( + createChain({ find: project }) + ); + mockHttps.mockRejectedValue({ response: { data: 'Error', status: 404 } }); + + const req = { + params: { projectId: 'proj-1', contentTypeUid: 'ct-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-1' } }, + } as any; + + const result = await contentMapperService.getSingleContentTypes(req); + + expect(result.status).toBe(404); + }); + }); + + describe('getSingleGlobalField', () => { + it('should return global field successfully', async () => { + const project = { id: 'proj-1', destination_stack_id: 'stack-1' }; + (ProjectModelLowdb.chain.get as ReturnType).mockReturnValue( + createChain({ find: project }) + ); + mockHttps.mockResolvedValue({ + data: { global_field: { title: 'SEO', uid: 'gf-1', schema: {} } }, + }); + + const req = { + params: { projectId: 'proj-1', globalFieldUid: 'gf-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-1' } }, + } as any; + + const result = await contentMapperService.getSingleGlobalField(req); + + expect(result.title).toBe('SEO'); + expect(result.uid).toBe('gf-1'); + }); + + it('should return error when https fails', async () => { + const project = { id: 'proj-1', destination_stack_id: 'stack-1' }; + (ProjectModelLowdb.chain.get as ReturnType).mockReturnValue( + createChain({ find: project }) + ); + mockHttps.mockRejectedValue({ response: { data: 'Error', status: 500 } }); + + const req = { + params: { projectId: 'proj-1', globalFieldUid: 'gf-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-1' } }, + } as any; + + const result = await contentMapperService.getSingleGlobalField(req); + + expect(result.status).toBe(500); + }); + }); + + describe('updateContentMapper', () => { + it('should update content mapper successfully', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = { id: 'proj-1', mapperKeys: undefined }; + ProjectModelLowdb.data.projects = [project]; + + const req = { + params: { orgId: 'org-1', projectId: 'proj-1' }, + body: { + token_payload: { region: 'NA', user_id: 'user-1' }, + content_mapper: { key: 'value' }, + }, + } as any; + + const result = await contentMapperService.updateContentMapper(req); + + expect(result.status).toBe(200); + expect(result.data.message).toContain('content mapping updated'); + }); + }); + + describe('getExistingTaxonomies', () => { + it('should return source and destination taxonomies when project has taxonomies', async () => { + const project = { + id: 'proj-1', + destination_stack_id: 'stack-1', + taxonomies: [{ uid: 'tax-1', name: 'Category', description: '' }], + }; + (ProjectModelLowdb.chain.get as ReturnType).mockReturnValue( + createChain({ find: project }) + ); + mockFetchAllPaginatedData.mockResolvedValue([ + { uid: 'cs-tax-1', name: 'Category', description: '' }, + ]); + + const req = { + params: { projectId: 'proj-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-1' } }, + } as any; + + const result = await contentMapperService.getExistingTaxonomies(req); + + expect(result.status).toBe(200); + expect(result.sourceTaxonomies).toHaveLength(1); + expect(result.sourceTaxonomies[0].uid).toBe('tax-1'); + expect(result.destinationTaxonomies).toHaveLength(1); + expect(result.destinationTaxonomies[0].uid).toBe('cs-tax-1'); + }); + + it('should return 404 when project not found', async () => { + (ProjectModelLowdb.chain.get as ReturnType).mockReturnValue( + createChain({ find: null }) + ); + + const req = { + params: { projectId: 'proj-999' }, + body: { token_payload: { region: 'NA', user_id: 'user-1' } }, + } as any; + + const result = await contentMapperService.getExistingTaxonomies(req); + + expect(result.status).toBe(404); + expect(result.data).toBe('Project not found'); + }); + }); +}); diff --git a/api/tests/unit/services/extension.service.test.ts b/api/tests/unit/services/extension.service.test.ts new file mode 100644 index 000000000..18ad83cf6 --- /dev/null +++ b/api/tests/unit/services/extension.service.test.ts @@ -0,0 +1,158 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { + mockFsPromises, + mockPathJoin, +} = vi.hoisted(() => ({ + mockFsPromises: { + access: vi.fn(), + mkdir: vi.fn(), + writeFile: vi.fn(), + readFile: vi.fn(), + }, + mockPathJoin: vi.fn((...args: string[]) => args.join('/')), +})); + +vi.mock('fs', () => ({ + default: { promises: mockFsPromises }, +})); +vi.mock('path', () => ({ default: { join: mockPathJoin } })); +vi.mock('../../../src/constants/index.js', () => ({ + MIGRATION_DATA_CONFIG: { + DATA: './cmsMigrationData', + EXTENSION_APPS_DIR_NAME: 'extensions', + EXTENSION_APPS_FILE_NAME: 'extensions.json', + CUSTOM_MAPPER_FILE_NAME: 'custmon-mapper.json', + }, + LIST_EXTENSION_UID: 'blt0000000000000000', +})); + +vi.stubGlobal('process', { + ...process, + cwd: vi.fn(() => '/test/cwd'), +}); + +import { extensionService } from '../../../src/services/extension.service.js'; + +describe('extension.service', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockFsPromises.access.mockResolvedValue(undefined); + mockFsPromises.mkdir.mockResolvedValue(undefined); + mockFsPromises.writeFile.mockResolvedValue(undefined); + mockFsPromises.readFile.mockResolvedValue(undefined); + mockPathJoin.mockImplementation((...args: string[]) => args.join('/')); + }); + + describe('createExtension', () => { + it('should create extension file when custom mapper has LIST_EXTENSION_UID', async () => { + const customMapperContent = JSON.stringify([ + { extensionUid: 'blt0000000000000000' }, + ]); + mockFsPromises.readFile.mockResolvedValue(customMapperContent); + + await extensionService.createExtension({ + destinationStackId: 'stack-123', + }); + + expect(mockFsPromises.readFile).toHaveBeenCalled(); + expect(mockFsPromises.writeFile).toHaveBeenCalledWith( + expect.stringContaining('extensions'), + expect.stringContaining('blt0000000000000000') + ); + }); + + it('should create extension file with unique extension UIDs only', async () => { + const customMapperContent = JSON.stringify([ + { extensionUid: 'blt0000000000000000' }, + { extensionUid: 'blt0000000000000000' }, + ]); + mockFsPromises.readFile.mockResolvedValue(customMapperContent); + + await extensionService.createExtension({ + destinationStackId: 'stack-456', + }); + + expect(mockFsPromises.writeFile).toHaveBeenCalled(); + const writeCall = mockFsPromises.writeFile.mock.calls[0]; + const writtenData = JSON.parse(writeCall[1]); + expect(Object.keys(writtenData)).toHaveLength(1); + expect(writtenData['blt0000000000000000']).toBeDefined(); + }); + + it('should create directory when it does not exist', async () => { + mockFsPromises.access.mockRejectedValue(new Error('ENOENT')); + mockFsPromises.readFile.mockResolvedValue( + JSON.stringify([{ extensionUid: 'blt0000000000000000' }]) + ); + + await extensionService.createExtension({ + destinationStackId: 'stack-789', + }); + + expect(mockFsPromises.mkdir).toHaveBeenCalledWith( + expect.any(String), + { recursive: true } + ); + }); + + it('should not write file when custom mapper is undefined (file not found)', async () => { + mockFsPromises.readFile.mockRejectedValue(new Error('ENOENT')); + + await extensionService.createExtension({ + destinationStackId: 'stack-999', + }); + + expect(mockFsPromises.writeFile).not.toHaveBeenCalled(); + }); + + it('should skip extensions not matching LIST_EXTENSION_UID', async () => { + const customMapperContent = JSON.stringify([ + { extensionUid: 'unknown-extension-uid' }, + ]); + mockFsPromises.readFile.mockResolvedValue(customMapperContent); + + await extensionService.createExtension({ + destinationStackId: 'stack-abc', + }); + + const writeCall = mockFsPromises.writeFile.mock.calls[0]; + if (writeCall) { + const writtenData = JSON.parse(writeCall[1]); + expect(Object.keys(writtenData)).toHaveLength(0); + } + }); + + it('should handle writeFile error gracefully', async () => { + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); + mockFsPromises.readFile.mockResolvedValue( + JSON.stringify([{ extensionUid: 'blt0000000000000000' }]) + ); + mockFsPromises.writeFile.mockRejectedValue(new Error('Write failed')); + + await extensionService.createExtension({ + destinationStackId: 'stack-err', + }); + + expect(consoleSpy).toHaveBeenCalled(); + consoleSpy.mockRestore(); + }); + + it('should handle mkdir error gracefully', async () => { + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); + mockFsPromises.access.mockRejectedValue(new Error('ENOENT')); + mockFsPromises.mkdir.mockRejectedValue(new Error('Mkdir failed')); + mockFsPromises.readFile.mockResolvedValue( + JSON.stringify([{ extensionUid: 'blt0000000000000000' }]) + ); + + await extensionService.createExtension({ + destinationStackId: 'stack-mkdir-err', + }); + + expect(consoleSpy).toHaveBeenCalled(); + expect(mockFsPromises.writeFile).not.toHaveBeenCalled(); + consoleSpy.mockRestore(); + }); + }); +}); diff --git a/api/tests/unit/services/globalField.service.test.ts b/api/tests/unit/services/globalField.service.test.ts new file mode 100644 index 000000000..c865e5dad --- /dev/null +++ b/api/tests/unit/services/globalField.service.test.ts @@ -0,0 +1,172 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { + mockHttps, + mockGetAuthToken, + mockFsExistsSync, + mockFsMkdirSync, + mockFsPromisesReadFile, + mockFsPromisesMkdir, + mockFsPromisesWriteFile, + mockPathJoin, + mockPathDirname, +} = vi.hoisted(() => ({ + mockHttps: vi.fn(), + mockGetAuthToken: vi.fn(), + mockFsExistsSync: vi.fn(), + mockFsMkdirSync: vi.fn(), + mockFsPromisesReadFile: vi.fn(), + mockFsPromisesMkdir: vi.fn(), + mockFsPromisesWriteFile: vi.fn(), + mockPathJoin: vi.fn((...args: string[]) => args.join('/')), + mockPathDirname: vi.fn((p: string) => p.split('/').slice(0, -1).join('/')), +})); + +vi.mock('../../../src/utils/https.utils.js', () => ({ default: mockHttps })); +vi.mock('../../../src/utils/auth.utils.js', () => ({ default: mockGetAuthToken })); +vi.mock('../../../src/utils/logger.js', () => ({ + default: { error: vi.fn(), info: vi.fn(), warn: vi.fn() }, +})); +vi.mock('../../../src/config/index.js', () => ({ + config: { CS_API: { NA: 'https://api.contentstack.io/v3', EU: 'https://eu-api.contentstack.com/v3' } }, +})); +vi.mock('fs', () => ({ + default: { + existsSync: mockFsExistsSync, + mkdirSync: mockFsMkdirSync, + promises: { + readFile: mockFsPromisesReadFile, + mkdir: mockFsPromisesMkdir, + writeFile: mockFsPromisesWriteFile, + }, + }, +})); +vi.mock('path', () => ({ + default: { join: mockPathJoin, dirname: mockPathDirname }, +})); + +import { globalFieldServie } from '../../../src/services/globalField.service.js'; + +describe('globalField.service', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockGetAuthToken.mockResolvedValue('cs-auth-token'); + mockHttps.mockResolvedValue({ + status: 200, + data: { + global_fields: [ + { uid: 'gf-1', title: 'SEO', schema: {} }, + { uid: 'gf-2', title: 'Meta', schema: {} }, + ], + }, + }); + mockFsExistsSync.mockReturnValue(false); + mockFsPromisesReadFile.mockResolvedValue('[]'); + mockFsPromisesMkdir.mockResolvedValue(undefined); + mockFsPromisesWriteFile.mockResolvedValue(undefined); + }); + + describe('createGlobalField', () => { + it('should fetch global fields from CS API and write to file', async () => { + await globalFieldServie.createGlobalField({ + region: 'NA', + user_id: 'user-123', + stackId: 'stack-abc', + current_test_stack_id: 'test-stack-1', + }); + + expect(mockGetAuthToken).toHaveBeenCalledWith('NA', 'user-123'); + expect(mockHttps).toHaveBeenCalledWith( + expect.objectContaining({ + method: 'GET', + url: expect.stringContaining('global_fields'), + headers: expect.objectContaining({ + api_key: 'stack-abc', + authtoken: 'cs-auth-token', + }), + }) + ); + expect(mockFsPromisesWriteFile).toHaveBeenCalled(); + const [, writtenData] = mockFsPromisesWriteFile.mock.calls[0]; + const parsed = JSON.parse(writtenData); + expect(parsed).toHaveLength(2); + expect(parsed[0].uid).toBe('gf-1'); + }); + + it('should merge new global fields with existing file data', async () => { + mockFsExistsSync.mockReturnValue(true); + mockFsPromisesReadFile.mockResolvedValue( + JSON.stringify([{ uid: 'gf-existing', title: 'Existing' }]) + ); + + await globalFieldServie.createGlobalField({ + region: 'NA', + user_id: 'user-123', + stackId: 'stack-abc', + current_test_stack_id: 'test-stack-2', + }); + + const [, writtenData] = mockFsPromisesWriteFile.mock.calls[0]; + const parsed = JSON.parse(writtenData); + expect(parsed).toHaveLength(3); + const uids = parsed.map((gf: { uid: string }) => gf.uid); + expect(uids).toContain('gf-1'); + expect(uids).toContain('gf-2'); + expect(uids).toContain('gf-existing'); + }); + + it('should create directory when it does not exist', async () => { + mockFsExistsSync.mockReturnValue(false); + + await globalFieldServie.createGlobalField({ + region: 'NA', + user_id: 'user-123', + stackId: 'stack-abc', + current_test_stack_id: 'test-stack-3', + }); + + expect(mockFsMkdirSync).toHaveBeenCalledWith(expect.any(String), { recursive: true }); + }); + + it('should return error object when writeFile fails', async () => { + mockFsPromisesWriteFile.mockRejectedValue(new Error('Write failed')); + + const result = await globalFieldServie.createGlobalField({ + region: 'NA', + user_id: 'user-123', + stackId: 'stack-abc', + current_test_stack_id: 'test-stack-4', + }); + + expect(result).toEqual({ + data: expect.any(Error), + status: 500, + }); + }); + + it('should handle invalid JSON in existing file', async () => { + mockFsExistsSync.mockReturnValue(true); + mockFsPromisesReadFile.mockResolvedValue('invalid json {'); + + await globalFieldServie.createGlobalField({ + region: 'NA', + user_id: 'user-123', + stackId: 'stack-abc', + current_test_stack_id: 'test-stack-5', + }); + + expect(mockFsPromisesWriteFile).toHaveBeenCalled(); + }); + + it('should use current_test_stack_id when provided', async () => { + await globalFieldServie.createGlobalField({ + region: 'EU', + user_id: 'user-456', + stackId: 'stack-eu', + current_test_stack_id: 'eu-test-stack', + }); + + expect(mockFsPromisesWriteFile).toHaveBeenCalled(); + }); + }); +}); diff --git a/api/tests/unit/services/marketplace.service.test.ts b/api/tests/unit/services/marketplace.service.test.ts new file mode 100644 index 000000000..6e873f1ae --- /dev/null +++ b/api/tests/unit/services/marketplace.service.test.ts @@ -0,0 +1,159 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { + mockGetAuthToken, + mockGetAppManifestAndAppConfig, + mockFsPromisesAccess, + mockFsPromisesMkdir, + mockFsPromisesWriteFile, + mockFsPromisesReadFile, + mockPathJoin, +} = vi.hoisted(() => ({ + mockGetAuthToken: vi.fn(), + mockGetAppManifestAndAppConfig: vi.fn(), + mockFsPromisesAccess: vi.fn(), + mockFsPromisesMkdir: vi.fn(), + mockFsPromisesWriteFile: vi.fn(), + mockFsPromisesReadFile: vi.fn(), + mockPathJoin: vi.fn((...args: string[]) => args.join('/')), +})); + +vi.mock('../../../src/utils/auth.utils.js', () => ({ default: mockGetAuthToken })); +vi.mock('../../../src/utils/market-app.utils.js', () => ({ + getAppManifestAndAppConfig: mockGetAppManifestAndAppConfig, +})); +vi.mock('fs', () => ({ + default: { + promises: { + access: mockFsPromisesAccess, + mkdir: mockFsPromisesMkdir, + writeFile: mockFsPromisesWriteFile, + readFile: mockFsPromisesReadFile, + }, + }, +})); +vi.mock('path', () => ({ default: { join: mockPathJoin } })); +vi.mock('../../../src/constants/index.js', () => ({ + MIGRATION_DATA_CONFIG: { + DATA: './cmsMigrationData', + EXTENSIONS_MAPPER_DIR_NAME: 'extension-mapper.json', + MARKETPLACE_APPS_DIR_NAME: 'marketplace_apps', + MARKETPLACE_APPS_FILE_NAME: 'marketplace_apps.json', + }, + KEYTOREMOVE: ['update', 'fetch', 'delete'], +})); + +vi.stubGlobal('process', { ...process, cwd: vi.fn(() => '/test/cwd') }); + +import { marketPlaceAppService } from '../../../src/services/marketplace.service.js'; + +describe('marketplace.service', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockGetAuthToken.mockResolvedValue('cs-auth-token'); + mockFsPromisesAccess.mockResolvedValue(undefined); + mockFsPromisesReadFile.mockResolvedValue( + JSON.stringify([ + { appUid: 'app-1', extensionUid: 'ext-1-field' }, + { appUid: 'app-1', extensionUid: 'ext-2-widget' }, + ]) + ); + mockGetAppManifestAndAppConfig.mockResolvedValue({ + uid: 'app-1', + name: 'Test App', + ui_location: { + locations: [ + { type: 'field', meta: [{ extension_uid: 'ext-1' }] }, + { type: 'widget', meta: [{ extension_uid: 'ext-2' }] }, + { type: 'cs.cm.stack.config', meta: [{}] }, + ], + }, + }); + }); + + describe('createAppManifest', () => { + it('should create app manifest and write to file', async () => { + await marketPlaceAppService.createAppManifest({ + destinationStackId: 'stack-123', + region: 'NA', + userId: 'user-1', + orgId: 'org-1', + }); + + expect(mockGetAuthToken).toHaveBeenCalledWith('NA', 'user-1'); + expect(mockFsPromisesReadFile).toHaveBeenCalled(); + expect(mockGetAppManifestAndAppConfig).toHaveBeenCalledWith( + expect.objectContaining({ + organizationUid: 'org-1', + authtoken: 'cs-auth-token', + region: 'NA', + manifestUid: 'app-1', + }) + ); + expect(mockFsPromisesWriteFile).toHaveBeenCalled(); + }); + + it('should create directory when it does not exist', async () => { + mockFsPromisesAccess.mockRejectedValue(new Error('ENOENT')); + + await marketPlaceAppService.createAppManifest({ + destinationStackId: 'stack-456', + region: 'NA', + userId: 'user-2', + orgId: 'org-2', + }); + + expect(mockFsPromisesMkdir).toHaveBeenCalledWith(expect.any(String), { recursive: true }); + }); + + it('should not process when extension mapper file is not found', async () => { + mockFsPromisesReadFile.mockRejectedValue(new Error('ENOENT')); + + await marketPlaceAppService.createAppManifest({ + destinationStackId: 'stack-789', + region: 'NA', + userId: 'user-3', + orgId: 'org-3', + }); + + expect(mockGetAppManifestAndAppConfig).not.toHaveBeenCalled(); + expect(mockFsPromisesWriteFile).not.toHaveBeenCalled(); + }); + + it('should remove KEYTOREMOVE keys from manifest data', async () => { + mockGetAppManifestAndAppConfig.mockResolvedValue({ + uid: 'app-1', + update: 'should-be-removed', + fetch: 'should-be-removed', + ui_location: { locations: [{ type: 'field', meta: [{}] }] }, + }); + + await marketPlaceAppService.createAppManifest({ + destinationStackId: 'stack-abc', + region: 'NA', + userId: 'user-4', + orgId: 'org-4', + }); + + const [, writtenData] = mockFsPromisesWriteFile.mock.calls[0]; + const parsed = JSON.parse(writtenData); + expect(parsed[0]).not.toHaveProperty('update'); + expect(parsed[0]).not.toHaveProperty('fetch'); + }); + + it('should handle writeFile error gracefully', async () => { + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); + mockFsPromisesWriteFile.mockRejectedValue(new Error('Write failed')); + + await marketPlaceAppService.createAppManifest({ + destinationStackId: 'stack-err', + region: 'NA', + userId: 'user-5', + orgId: 'org-5', + }); + + expect(consoleSpy).toHaveBeenCalled(); + consoleSpy.mockRestore(); + }); + }); +}); diff --git a/api/tests/unit/services/migration.service.test.ts b/api/tests/unit/services/migration.service.test.ts new file mode 100644 index 000000000..a5ebf2a72 --- /dev/null +++ b/api/tests/unit/services/migration.service.test.ts @@ -0,0 +1,1068 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { + mockHttps, + mockGetAuthToken, + mockProjectRead, + mockProjectUpdate, + mockChainGet, + mockProjects, + mockFsExistsSync, + mockFsReadDirSync, + mockFsPromisesReadFile, + mockFsPromisesAppendFile, + mockFsPromisesRealpath, +} = vi.hoisted(() => { + const projects = [ + { + id: 'proj-1', + org_id: 'org-123', + test_stacks: [] as any[], + stackDetails: { master_locale: 'en-us' }, + legacy_cms: { cms: 'wordpress' }, + current_test_stack_id: '', + destination_stack_id: '', + current_step: 1, + }, + ]; + return { + mockHttps: vi.fn(), + mockGetAuthToken: vi.fn(), + mockProjectRead: vi.fn(), + mockProjectUpdate: vi.fn(), + mockChainGet: vi.fn(), + mockProjects: projects, + mockFsExistsSync: vi.fn(), + mockFsReadDirSync: vi.fn(), + mockFsPromisesReadFile: vi.fn(), + mockFsPromisesAppendFile: vi.fn(), + mockFsPromisesRealpath: vi.fn(), + }; +}); + +vi.mock('../../../src/utils/https.utils.js', () => ({ default: mockHttps })); +vi.mock('../../../src/utils/auth.utils.js', () => ({ default: mockGetAuthToken })); +vi.mock('../../../src/utils/logger.js', () => ({ + default: { error: vi.fn(), info: vi.fn(), warn: vi.fn() }, +})); +vi.mock('../../../src/utils/custom-logger.utils.js', () => ({ + default: vi.fn().mockResolvedValue(undefined), +})); +vi.mock('../../../src/config/index.js', () => ({ + config: { + CS_API: { NA: 'https://api.contentstack.io/v3' }, + CS_URL: { NA: 'https://app.contentstack.com' }, + LOG_FILE_PATH: '/tmp/test.log', + }, +})); + +vi.mock('../../../src/models/project-lowdb.js', () => ({ + default: { + read: mockProjectRead, + update: mockProjectUpdate, + chain: { + get: (...args: unknown[]) => { + const chain = mockChainGet(...args); + return chain; + }, + }, + data: { projects: mockProjects }, + }, +})); + +vi.mock('../../../src/services/sitecore.service.js', () => ({ + siteCoreService: { + createEntry: vi.fn().mockResolvedValue(undefined), + createLocale: vi.fn().mockResolvedValue(undefined), + createEnvironment: vi.fn().mockResolvedValue(undefined), + createVersionFile: vi.fn().mockResolvedValue(undefined), + }, +})); +vi.mock('../../../src/services/drupal.service.js', () => ({ + drupalService: { + createQuery: vi.fn().mockResolvedValue(undefined), + generateContentTypeSchemas: vi.fn().mockResolvedValue(undefined), + createAssets: vi.fn().mockResolvedValue(undefined), + createRefrence: vi.fn().mockResolvedValue(undefined), + createTaxonomy: vi.fn().mockResolvedValue(undefined), + createEntry: vi.fn().mockResolvedValue(undefined), + createLocale: vi.fn().mockResolvedValue(undefined), + createVersionFile: vi.fn().mockResolvedValue(undefined), + }, +})); +vi.mock('../../../src/services/wordpress.service.js', () => ({ + wordpressService: { + getAllAssets: vi.fn().mockResolvedValue(undefined), + createTaxonomy: vi.fn().mockResolvedValue(undefined), + createEntry: vi.fn().mockResolvedValue(undefined), + createLocale: vi.fn().mockResolvedValue(undefined), + createVersionFile: vi.fn().mockResolvedValue(undefined), + }, +})); +vi.mock('../../../src/services/contentful.service.js', () => ({ + contentfulService: { + createLocale: vi.fn().mockResolvedValue(undefined), + createRefrence: vi.fn().mockResolvedValue(undefined), + createWebhooks: vi.fn().mockResolvedValue(undefined), + createEnvironment: vi.fn().mockResolvedValue(undefined), + createAssets: vi.fn().mockResolvedValue(undefined), + createEntry: vi.fn().mockResolvedValue(undefined), + createVersionFile: vi.fn().mockResolvedValue(undefined), + }, +})); +vi.mock('../../../src/services/aem.service.js', () => ({ + aemService: { + createAssets: vi.fn().mockResolvedValue(undefined), + createEntry: vi.fn().mockResolvedValue(undefined), + createLocale: vi.fn().mockResolvedValue(undefined), + createVersionFile: vi.fn().mockResolvedValue(undefined), + }, +})); +vi.mock('../../../src/services/marketplace.service.js', () => ({ + marketPlaceAppService: { createAppManifest: vi.fn().mockResolvedValue(undefined) }, +})); +vi.mock('../../../src/services/extension.service.js', () => ({ + extensionService: { createExtension: vi.fn().mockResolvedValue(undefined) }, +})); +vi.mock('../../../src/services/globalField.service.js', () => ({ + globalFieldServie: { createGlobalField: vi.fn().mockResolvedValue(undefined) }, +})); +vi.mock('../../../src/services/taxonomy.service.js', () => ({ + taxonomyService: { createTaxonomy: vi.fn().mockResolvedValue(undefined) }, +})); +vi.mock('../../../src/services/runCli.service.js', () => ({ + utilsCli: { runCli: vi.fn().mockResolvedValue(undefined) }, +})); +vi.mock('../../../src/utils/field-attacher.utils.js', () => ({ + fieldAttacher: vi.fn().mockResolvedValue([]), +})); +vi.mock('../../../src/utils/test-folder-creator.utils.js', () => ({ + testFolderCreator: vi.fn().mockResolvedValue(undefined), +})); +vi.mock('../../../src/server.js', () => ({ setLogFilePath: vi.fn() })); + +vi.mock('fs', () => ({ + default: { + existsSync: (...args: unknown[]) => mockFsExistsSync(...args), + readdirSync: (...args: unknown[]) => mockFsReadDirSync(...args), + promises: { + readFile: (...args: unknown[]) => mockFsPromisesReadFile(...args), + }, + }, +})); + +vi.mock('fs/promises', () => ({ + default: { + readFile: mockFsPromisesReadFile, + appendFile: mockFsPromisesAppendFile, + realpath: mockFsPromisesRealpath, + }, +})); + +vi.mock('../../../src/utils/sanitize-path.utils.js', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + getSafePath: vi.fn((p: string) => p), + }; +}); + +import { migrationService } from '../../../src/services/migration.service.js'; + +const createMockReq = (overrides: Record = {}) => + ({ + params: { orgId: 'org-123', projectId: 'proj-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-123' } }, + ...overrides, + }) as any; + +describe('migration.service', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockGetAuthToken.mockResolvedValue('cs-auth-token'); + mockProjectRead.mockResolvedValue(undefined); + mockProjectUpdate.mockImplementation((fn: (data: any) => void) => { + fn({ projects: [...mockProjects] }); + }); + mockChainGet.mockReturnValue({ + find: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(mockProjects[0]) }), + findIndex: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(0) }), + }); + mockFsPromisesRealpath.mockRejectedValue(new Error('File not found')); + }); + + describe('createTestStack', () => { + it('should create test stack and update project on success', async () => { + mockHttps.mockResolvedValue({ + status: 201, + data: { stack: { api_key: 'test-stack-1', name: 'MyStack-Test-1' } }, + }); + + const req = createMockReq({ + params: { orgId: 'org-123', projectId: 'proj-1' }, + body: { + token_payload: { region: 'NA', user_id: 'user-123' }, + name: 'MyStack', + }, + }); + + const result = await migrationService.createTestStack(req); + + expect(result.status).toBe(201); + expect(result.data.data.stack.api_key).toBe('test-stack-1'); + expect(result.data.url).toContain('test-stack-1'); + expect(mockProjectUpdate).toHaveBeenCalled(); + }); + + it('should return error when create stack API fails', async () => { + vi.spyOn( + await import('../../../src/utils/index.js'), + 'safePromise' + ).mockImplementation((p: Promise) => + p.then(() => [ + { response: { status: 400, data: { error: 'Bad request' } } }, + null, + ] as any) + ); + + mockHttps.mockResolvedValue({ status: 201, data: {} }); + + const req = createMockReq({ + params: { orgId: 'org-123', projectId: 'proj-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-123' }, name: 'Test' }, + }); + + const result = await migrationService.createTestStack(req); + + expect(result.status).toBe(400); + expect(result.data).toEqual({ error: 'Bad request' }); + }); + + it('should throw when getAuthtoken or ProjectModelLowdb fails', async () => { + mockGetAuthToken.mockRejectedValue(new Error('Auth failed')); + + const req = createMockReq({ + params: { orgId: 'org-123', projectId: 'proj-1' }, + body: { + token_payload: { region: 'NA', user_id: 'user-123' }, + name: 'MyStack', + }, + }); + + await expect(migrationService.createTestStack(req)).rejects.toThrow(); + }); + + it('should create Drupal test stack and generate queries when CMS is Drupal', async () => { + const drupalProject = { + ...mockProjects[0], + legacy_cms: { + cms: 'drupal', + mySQLDetails: { + host: 'localhost', + user: 'root', + password: '', + database: 'drupal', + port: 3306, + }, + }, + test_stacks: [], + }; + + mockChainGet.mockReturnValue({ + find: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(drupalProject) }), + findIndex: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(0) }), + }); + + mockHttps.mockResolvedValue({ + status: 201, + data: { stack: { api_key: 'drupal-test-stack', name: 'Drupal-Test-1' } }, + }); + + const req = createMockReq({ + params: { orgId: 'org-123', projectId: 'proj-1' }, + body: { + token_payload: { region: 'NA', user_id: 'user-123' }, + name: 'Drupal', + }, + }); + + const result = await migrationService.createTestStack(req); + + expect(result.status).toBe(201); + expect(mockProjectUpdate).toHaveBeenCalled(); + }); + }); + + describe('deleteTestStack', () => { + it('should delete test stack and remove from project on success', async () => { + mockHttps.mockResolvedValue({ status: 200, data: {} }); + + const req = createMockReq({ + params: { projectId: 'proj-1' }, + body: { + token_payload: { region: 'NA', user_id: 'user-123' }, + stack_key: 'test-stack-1', + }, + }); + + const result = await migrationService.deleteTestStack(req); + + expect(result.status).toBe(200); + expect(mockProjectUpdate).toHaveBeenCalled(); + }); + + it('should return error when delete API fails', async () => { + vi.spyOn( + await import('../../../src/utils/index.js'), + 'safePromise' + ).mockImplementation((p: Promise) => + p.then(() => [ + { response: { status: 404, data: { error: 'Not found' } } }, + null, + ] as any) + ); + + mockHttps.mockResolvedValue({ status: 200, data: {} }); + + const req = createMockReq({ + params: { projectId: 'proj-1' }, + body: { + token_payload: { region: 'NA', user_id: 'user-123' }, + stack_key: 'test-stack-1', + }, + }); + + const result = await migrationService.deleteTestStack(req); + + expect(result.status).toBe(404); + expect(result.data).toEqual({ error: 'Not found' }); + }); + + it('should still return success when index is -1 (stack not in project)', async () => { + mockHttps.mockResolvedValue({ status: 200, data: {} }); + mockChainGet.mockReturnValue({ + find: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(mockProjects[0]) }), + findIndex: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(-1) }), + }); + + const req = createMockReq({ + params: { projectId: 'proj-1' }, + body: { + token_payload: { region: 'NA', user_id: 'user-123' }, + stack_key: 'test-stack-1', + }, + }); + + const result = await migrationService.deleteTestStack(req); + + expect(result.status).toBe(200); + expect(mockProjectUpdate).not.toHaveBeenCalled(); + }); + + it('should throw when getAuthtoken fails', async () => { + mockGetAuthToken.mockRejectedValue(new Error('Token error')); + + const req = createMockReq({ + params: { projectId: 'proj-1' }, + body: { + token_payload: { region: 'NA', user_id: 'user-123' }, + stack_key: 'test-stack-1', + }, + }); + + await expect(migrationService.deleteTestStack(req)).rejects.toThrow(); + }); + }); + + describe('startTestMigration', () => { + it('should run without throwing when project has current_test_stack_id (WordPress)', async () => { + const projectWithTestStack = { + ...mockProjects[0], + current_test_stack_id: 'test-stack-1', + extract_path: '/tmp/extract', + legacy_cms: { cms: 'wordpress', file_path: '/tmp/wp' }, + stackDetails: { master_locale: 'en-us' }, + mapperKeys: {}, + }; + + mockChainGet.mockReturnValue({ + find: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(projectWithTestStack) }), + findIndex: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(0) }), + }); + + const req = createMockReq({ + params: { orgId: 'org-123', projectId: 'proj-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-123' } }, + }); + + await expect(migrationService.startTestMigration(req)).resolves.not.toThrow(); + }); + + it('should run for Sitecore CMS when project has current_test_stack_id', async () => { + const projectWithTestStack = { + ...mockProjects[0], + current_test_stack_id: 'test-stack-1', + extract_path: '/tmp/extract', + legacy_cms: { cms: 'sitecore v9', file_path: '/tmp/sc' }, + stackDetails: { master_locale: 'en-us' }, + mapperKeys: {}, + }; + + mockChainGet.mockReturnValue({ + find: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(projectWithTestStack) }), + findIndex: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(0) }), + }); + + const req = createMockReq({ + params: { orgId: 'org-123', projectId: 'proj-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-123' } }, + }); + + await expect(migrationService.startTestMigration(req)).resolves.not.toThrow(); + }); + + it('should run for Contentful CMS when project has current_test_stack_id', async () => { + const projectWithTestStack = { + ...mockProjects[0], + current_test_stack_id: 'test-stack-1', + extract_path: '/tmp/extract', + legacy_cms: { cms: 'contentful', file_path: '/tmp/cf/' }, + stackDetails: { master_locale: 'en-us' }, + mapperKeys: {}, + }; + + mockChainGet.mockReturnValue({ + find: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(projectWithTestStack) }), + findIndex: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(0) }), + }); + + const req = createMockReq({ + params: { orgId: 'org-123', projectId: 'proj-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-123' } }, + }); + + await expect(migrationService.startTestMigration(req)).resolves.not.toThrow(); + }); + + it('should run for AEM CMS when project has current_test_stack_id', async () => { + const projectWithTestStack = { + ...mockProjects[0], + current_test_stack_id: 'test-stack-1', + extract_path: '/tmp/extract', + legacy_cms: { cms: 'aem', file_path: '/tmp/aem' }, + stackDetails: { master_locale: 'en-us' }, + mapperKeys: {}, + }; + + mockChainGet.mockReturnValue({ + find: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(projectWithTestStack) }), + findIndex: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(0) }), + }); + + const req = createMockReq({ + params: { orgId: 'org-123', projectId: 'proj-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-123' } }, + }); + + await expect(migrationService.startTestMigration(req)).resolves.not.toThrow(); + }); + + it('should run for Drupal CMS when project has current_test_stack_id', async () => { + const projectWithTestStack = { + ...mockProjects[0], + current_test_stack_id: 'test-stack-1', + extract_path: '/tmp/extract', + legacy_cms: { + cms: 'drupal', + file_path: '/tmp/drupal', + mySQLDetails: { + host: 'localhost', + user: 'root', + password: '', + database: 'drupal', + port: 3306, + }, + }, + stackDetails: { master_locale: 'en-us' }, + mapperKeys: {}, + content_mapper: [], + }; + + mockChainGet.mockReturnValue({ + find: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(projectWithTestStack) }), + findIndex: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(0) }), + }); + + const req = createMockReq({ + params: { orgId: 'org-123', projectId: 'proj-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-123' } }, + }); + + await expect(migrationService.startTestMigration(req)).resolves.not.toThrow(); + }); + + it('should do nothing when project has no current_test_stack_id', async () => { + const projectNoTestStack = { + ...mockProjects[0], + current_test_stack_id: '', + extract_path: '/tmp/extract', + }; + + mockChainGet.mockReturnValue({ + find: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(projectNoTestStack) }), + findIndex: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(-1) }), + }); + + const req = createMockReq({ + params: { orgId: 'org-123', projectId: 'proj-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-123' } }, + }); + + await expect(migrationService.startTestMigration(req)).resolves.not.toThrow(); + }); + }); + + describe('startMigration', () => { + it('should run without throwing when project has destination_stack_id', async () => { + const projectWithDest = { + ...mockProjects[0], + destination_stack_id: 'dest-stack-1', + extract_path: '/tmp/extract', + legacy_cms: { cms: 'wordpress', file_path: '/tmp/wp' }, + stackDetails: { master_locale: 'en-us' }, + mapperKeys: {}, + }; + + mockChainGet.mockReturnValue({ + find: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(projectWithDest) }), + findIndex: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(0) }), + }); + + const req = createMockReq({ + params: { orgId: 'org-123', projectId: 'proj-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-123' } }, + }); + + await expect(migrationService.startMigration(req)).resolves.not.toThrow(); + expect(mockProjectUpdate).toHaveBeenCalled(); + }); + + it('should do nothing when project has no destination_stack_id', async () => { + const projectNoDest = { + ...mockProjects[0], + destination_stack_id: '', + extract_path: '/tmp/extract', + }; + + mockChainGet.mockReturnValue({ + find: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(projectNoDest) }), + findIndex: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(0) }), + }); + + const req = createMockReq({ + params: { orgId: 'org-123', projectId: 'proj-1' }, + body: { token_payload: { region: 'NA', user_id: 'user-123' } }, + }); + + await migrationService.startMigration(req); + + expect(mockProjectUpdate).toHaveBeenCalled(); + }); + }); + + describe('getLogs', () => { + it('should return logs when file exists with valid logs', async () => { + mockFsExistsSync.mockReturnValue(true); + const logLine1 = JSON.stringify({ level: 'info', message: 'test', id: 0 }); + const logLine2 = JSON.stringify({ level: 'error', message: 'test2', id: 1 }); + mockFsPromisesReadFile.mockResolvedValue(logLine1 + '\n' + logLine2 + '\n'); + + const req = createMockReq({ + params: { + projectId: 'proj-1', + stackId: 'stack-1', + limit: '10', + startIndex: '0', + searchText: 'null', + filter: 'all', + }, + }); + + const result = await migrationService.getLogs(req); + + expect(result.status).toBe(200); + expect(result.logs).toBeDefined(); + expect(result.total).toBeDefined(); + expect(result.filterOptions).toBeDefined(); + expect(Array.isArray(result.logs)).toBe(true); + }); + + it('should return empty logs when file has no valid log entries', async () => { + mockFsExistsSync.mockReturnValue(true); + mockFsPromisesReadFile.mockResolvedValue('invalid\nnotjson\n'); + + const req = createMockReq({ + params: { + projectId: 'proj-1', + stackId: 'stack-1', + limit: '10', + startIndex: '0', + }, + }); + + const result = await migrationService.getLogs(req); + + expect(result.status).toBe(200); + expect(result.logs).toEqual([]); + expect(result.total).toBe(0); + }); + + it('should throw BadRequestError when projectId contains ..', async () => { + const req = createMockReq({ + params: { + projectId: '..', + stackId: 'stack-1', + limit: '10', + startIndex: '0', + }, + }); + + await expect(migrationService.getLogs(req)).rejects.toThrow('Invalid projectId or stackId'); + }); + + it('should throw BadRequestError when stackId contains ..', async () => { + const req = createMockReq({ + params: { + projectId: 'proj-1', + stackId: '../..', + limit: '10', + startIndex: '0', + }, + }); + + await expect(migrationService.getLogs(req)).rejects.toThrow('Invalid projectId or stackId'); + }); + + it('should throw BadRequestError when projectId is missing', async () => { + const req = createMockReq({ + params: { + projectId: '', + stackId: 'stack-1', + limit: '10', + startIndex: '0', + }, + }); + + await expect(migrationService.getLogs(req)).rejects.toThrow('Invalid projectId or stackId'); + }); + + it('should throw BadRequestError when stackId is missing', async () => { + const req = createMockReq({ + params: { + projectId: 'proj-1', + stackId: '', + limit: '10', + startIndex: '0', + }, + }); + + await expect(migrationService.getLogs(req)).rejects.toThrow('Invalid projectId or stackId'); + }); + + it('should throw BadRequestError when log file does not exist', async () => { + mockFsExistsSync.mockReturnValue(false); + + const req = createMockReq({ + params: { + projectId: 'proj-1', + stackId: 'stack-1', + limit: '10', + startIndex: '0', + }, + }); + + await expect(migrationService.getLogs(req)).rejects.toThrow(); + }); + + it('should apply filter when filter is not "all"', async () => { + mockFsExistsSync.mockReturnValue(true); + const logLine1 = JSON.stringify({ level: 'info', message: 'info msg', id: 0 }); + const logLine2 = JSON.stringify({ level: 'error', message: 'error msg', id: 1 }); + mockFsPromisesReadFile.mockResolvedValue(logLine1 + '\n' + logLine2 + '\n'); + + const req = createMockReq({ + params: { + projectId: 'proj-1', + stackId: 'stack-1', + limit: '10', + startIndex: '0', + filter: 'error', + }, + }); + + const result = await migrationService.getLogs(req); + + expect(result.status).toBe(200); + expect(result.logs).toBeDefined(); + }); + + it('should apply searchText when provided', async () => { + mockFsExistsSync.mockReturnValue(true); + const logLine1 = JSON.stringify({ + level: 'info', + message: 'Starting audit process', + methodName: 'audit', + timestamp: '2024-01-01', + id: 0, + }); + mockFsPromisesReadFile.mockResolvedValue(logLine1 + '\n'); + + const req = createMockReq({ + params: { + projectId: 'proj-1', + stackId: 'stack-1', + limit: '10', + startIndex: '0', + searchText: 'audit', + }, + }); + + const result = await migrationService.getLogs(req); + + expect(result.status).toBe(200); + expect(result.logs).toBeDefined(); + }); + + it('should use default limit and startIndex when not provided', async () => { + mockFsExistsSync.mockReturnValue(true); + mockFsPromisesReadFile.mockResolvedValue( + JSON.stringify({ level: 'info', message: 'test', id: 0 }) + '\n' + ); + + const req = createMockReq({ + params: { + projectId: 'proj-1', + stackId: 'stack-1', + }, + }); + + const result = await migrationService.getLogs(req); + + expect(result.status).toBe(200); + expect(result.logs).toBeDefined(); + }); + }); + + describe('createSourceLocales', () => { + it('should update project source locales when project exists', async () => { + const req = createMockReq({ + params: { projectId: 'proj-1' }, + body: { + token_payload: { region: 'NA', user_id: 'user-123' }, + locale: [{ code: 'en-us', name: 'English' }], + }, + }); + + await expect(migrationService.createSourceLocales(req)).resolves.not.toThrow(); + expect(mockProjectUpdate).toHaveBeenCalled(); + }); + + it('should not throw when project index is -1', async () => { + mockChainGet.mockReturnValue({ + find: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(null) }), + findIndex: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(-1) }), + }); + + const req = createMockReq({ + params: { projectId: 'nonexistent' }, + body: { locale: [] }, + }); + + await expect(migrationService.createSourceLocales(req)).resolves.not.toThrow(); + expect(mockProjectUpdate).not.toHaveBeenCalled(); + }); + + it('should throw when ProjectModelLowdb.read fails', async () => { + mockProjectRead.mockRejectedValue(new Error('DB read failed')); + + const req = createMockReq({ + params: { projectId: 'proj-1' }, + body: { locale: [{ code: 'en-us', name: 'English' }] }, + }); + + await expect(migrationService.createSourceLocales(req)).rejects.toThrow(); + }); + }); + + describe('updateLocaleMapper', () => { + it('should update master_locale and locales when project exists', async () => { + const req = createMockReq({ + params: { projectId: 'proj-1' }, + body: { + master_locale: 'en-us', + locales: [{ code: 'fr', name: 'French' }], + }, + }); + + await expect(migrationService.updateLocaleMapper(req)).resolves.not.toThrow(); + expect(mockProjectUpdate).toHaveBeenCalled(); + }); + + it('should not throw when project index is -1', async () => { + mockChainGet.mockReturnValue({ + find: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(null) }), + findIndex: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(-1) }), + }); + + const req = createMockReq({ + params: { projectId: 'nonexistent' }, + body: { master_locale: 'en-us', locales: [] }, + }); + + await expect(migrationService.updateLocaleMapper(req)).resolves.not.toThrow(); + expect(mockProjectUpdate).not.toHaveBeenCalled(); + }); + + it('should throw when ProjectModelLowdb.read fails', async () => { + mockProjectRead.mockRejectedValue(new Error('DB read failed')); + + const req = createMockReq({ + params: { projectId: 'proj-1' }, + body: { master_locale: 'en-us', locales: [] }, + }); + + await expect(migrationService.updateLocaleMapper(req)).rejects.toThrow(); + }); + }); + + describe('getAuditData', () => { + it('should throw BadRequestError when projectId contains ..', async () => { + const req = createMockReq({ + params: { + projectId: '..bad', + stackId: 'stack-1', + moduleName: 'entries', + limit: '10', + startIndex: '0', + searchText: '', + filter: 'all', + }, + }); + + await expect(migrationService.getAuditData(req)).rejects.toThrow( + 'Invalid projectId, stackId, or moduleName' + ); + }); + + it('should throw BadRequestError when stackId contains ..', async () => { + const req = createMockReq({ + params: { + projectId: 'proj-1', + stackId: '..stack', + moduleName: 'entries', + limit: '10', + startIndex: '0', + searchText: '', + filter: 'all', + }, + }); + + await expect(migrationService.getAuditData(req)).rejects.toThrow( + 'Invalid projectId, stackId, or moduleName' + ); + }); + + it('should throw BadRequestError when moduleName contains ..', async () => { + const req = createMockReq({ + params: { + projectId: 'proj-1', + stackId: 'stack-1', + moduleName: '..entries', + limit: '10', + startIndex: '0', + searchText: '', + filter: 'all', + }, + }); + + await expect(migrationService.getAuditData(req)).rejects.toThrow( + 'Invalid projectId, stackId, or moduleName' + ); + }); + + it('should throw when stack folder not found in migration-data', async () => { + mockFsReadDirSync.mockReturnValue(['other-stack-folder']); + + const req = createMockReq({ + params: { + projectId: 'proj-1', + stackId: 'stack-1', + moduleName: 'entries', + limit: '10', + startIndex: '0', + searchText: '', + filter: 'all', + }, + }); + + await expect(migrationService.getAuditData(req)).rejects.toThrow( + 'Migration data not found for this stack' + ); + }); + + it('should throw when audit log path does not exist', async () => { + mockFsReadDirSync.mockReturnValue(['stack-1-abc']); + mockFsExistsSync.mockReturnValue(false); + + const req = createMockReq({ + params: { + projectId: 'proj-1', + stackId: 'stack-1', + moduleName: 'entries', + limit: '10', + startIndex: '0', + searchText: '', + filter: 'all', + }, + }); + + await expect(migrationService.getAuditData(req)).rejects.toThrow( + 'Audit log path not found' + ); + }); + + it('should return audit data when files exist', async () => { + mockFsReadDirSync.mockReturnValue(['stack-1-abc']); + mockFsExistsSync + .mockReturnValueOnce(true) + .mockReturnValueOnce(true); + + mockFsPromisesReadFile.mockResolvedValue( + JSON.stringify([{ uid: 'item-1', title: 'Test', data_type: 'entry' }]) + ); + + const req = createMockReq({ + params: { + projectId: 'proj-1', + stackId: 'stack-1', + moduleName: 'entries', + limit: '10', + startIndex: '0', + searchText: '', + filter: 'all', + }, + }); + + const result = await migrationService.getAuditData(req); + + expect(result.status).toBe(200); + expect(result.data).toBeDefined(); + expect(result.totalCount).toBeDefined(); + expect(Array.isArray(result.data)).toBe(true); + }); + + it('should throw when no audit data found for module', async () => { + mockFsReadDirSync.mockReturnValue(['stack-1-abc']); + mockFsExistsSync.mockReturnValue(false); + + const req = createMockReq({ + params: { + projectId: 'proj-1', + stackId: 'stack-1', + moduleName: 'nonexistent', + limit: '10', + startIndex: '0', + searchText: '', + filter: 'all', + }, + }); + + await expect(migrationService.getAuditData(req)).rejects.toThrow(); + }); + + it('should apply filter when filter is not "all"', async () => { + mockFsReadDirSync.mockReturnValue(['stack-1-abc']); + mockFsExistsSync.mockReturnValue(true); + mockFsPromisesReadFile.mockResolvedValue( + JSON.stringify([ + { uid: '1', data_type: 'entry', title: 'Entry 1' }, + { uid: '2', data_type: 'asset', title: 'Asset 1' }, + ]) + ); + + const req = createMockReq({ + params: { + projectId: 'proj-1', + stackId: 'stack-1', + moduleName: 'entries', + limit: '10', + startIndex: '0', + searchText: '', + filter: 'entry-asset', + }, + }); + + const result = await migrationService.getAuditData(req); + + expect(result.status).toBe(200); + expect(result.data).toBeDefined(); + }); + + it('should throw on invalid JSON in audit file', async () => { + mockFsReadDirSync.mockReturnValue(['stack-1-abc']); + mockFsExistsSync.mockReturnValue(true); + mockFsPromisesReadFile.mockResolvedValue('invalid json {'); + + const req = createMockReq({ + params: { + projectId: 'proj-1', + stackId: 'stack-1', + moduleName: 'entries', + limit: '10', + startIndex: '0', + searchText: '', + filter: 'all', + }, + }); + + await expect(migrationService.getAuditData(req)).rejects.toThrow( + 'Invalid JSON format in audit file' + ); + }); + + it('should apply searchText for Entries_Select_feild module', async () => { + mockFsReadDirSync.mockReturnValue(['stack-1-abc']); + mockFsExistsSync + .mockReturnValueOnce(true) + .mockReturnValueOnce(true); + + mockFsPromisesReadFile.mockResolvedValue( + JSON.stringify([ + { + uid: '1', + data_type: 'entry', + title: 'Hello World', + display_type: 'entry', + }, + ]) + ); + + const req = createMockReq({ + params: { + projectId: 'proj-1', + stackId: 'stack-1', + moduleName: 'Entries_Select_feild', + limit: '10', + startIndex: '0', + searchText: 'Hello', + filter: 'all', + }, + }); + + const result = await migrationService.getAuditData(req); + + expect(result.status).toBe(200); + expect(result.data).toBeDefined(); + }); + }); +}); diff --git a/api/tests/unit/services/org.service.test.ts b/api/tests/unit/services/org.service.test.ts new file mode 100644 index 000000000..f5c4267fb --- /dev/null +++ b/api/tests/unit/services/org.service.test.ts @@ -0,0 +1,339 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockHttps, mockGetAuthToken } = vi.hoisted(() => ({ + mockHttps: vi.fn(), + mockGetAuthToken: vi.fn(), +})); + +vi.mock('../../../src/utils/https.utils.js', () => ({ default: mockHttps })); +vi.mock('../../../src/utils/auth.utils.js', () => ({ default: mockGetAuthToken })); +vi.mock('../../../src/utils/logger.js', () => ({ + default: { error: vi.fn(), info: vi.fn(), warn: vi.fn() }, +})); +vi.mock('../../../src/config/index.js', () => ({ + config: { + CS_API: { NA: 'https://api.contentstack.io/v3' }, + }, +})); + +const { mockProjectRead, mockChainGet } = vi.hoisted(() => ({ + mockProjectRead: vi.fn(), + mockChainGet: vi.fn(), +})); + +vi.mock('../../../src/models/project-lowdb.js', () => ({ + default: { + read: mockProjectRead, + chain: { + get: (...args: unknown[]) => mockChainGet(...args), + }, + data: { projects: [] }, + }, +})); + +import { orgService } from '../../../src/services/org.service.js'; + +const createMockReq = (overrides: Record = {}) => + ({ + params: { orgId: 'org-123' }, + body: { token_payload: { region: 'NA', user_id: 'user-123', org_uid: 'org-123' } }, + ...overrides, + }) as any; + +describe('org.service', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockGetAuthToken.mockResolvedValue('cs-auth-token'); + mockProjectRead.mockResolvedValue(undefined); + }); + + describe('getAllStacks', () => { + it('should return stacks from CS API', async () => { + mockHttps.mockResolvedValue({ + status: 200, + data: { + stacks: [ + { api_key: 'stack-1', name: 'Stack 1', description: 'Desc 1' }, + { api_key: 'stack-2', name: 'Stack 2', description: 'Desc 2' }, + ], + }, + }); + mockChainGet.mockReturnValue({ + flatMap: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue([]) }), + }); + + const req = createMockReq({ params: { orgId: 'org-123' } }); + const result = await orgService.getAllStacks(req); + + expect(result.status).toBe(200); + expect(result.data.stacks).toHaveLength(2); + }); + + it('should filter stacks by searchText', async () => { + mockHttps.mockResolvedValue({ + status: 200, + data: { + stacks: [ + { api_key: 's1', name: 'Foo Stack', description: 'A' }, + { api_key: 's2', name: 'Bar Stack', description: 'B' }, + { api_key: 's3', name: 'Other', description: 'Foo bar' }, + ], + }, + }); + mockChainGet.mockReturnValue({ + flatMap: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue([]) }), + }); + + const req = createMockReq({ + params: { orgId: 'org-123', searchText: 'foo' }, + }); + const result = await orgService.getAllStacks(req); + + expect(result.data.stacks).toHaveLength(2); + }); + + it('should exclude test stacks from results', async () => { + mockHttps.mockResolvedValue({ + status: 200, + data: { + stacks: [ + { api_key: 'stack-1', name: 'Stack 1' }, + { api_key: 'stack-2', name: 'Stack 2' }, + ], + }, + }); + mockChainGet.mockReturnValue({ + flatMap: vi.fn().mockReturnValue({ + value: vi.fn().mockReturnValue([{ stackUid: 'stack-1' }]), + }), + }); + + const req = createMockReq(); + const result = await orgService.getAllStacks(req); + + expect(result.data.stacks).toHaveLength(1); + expect(result.data.stacks[0].api_key).toBe('stack-2'); + }); + + it('should return error when https returns error tuple', async () => { + const safePromiseModule = await import('../../../src/utils/index.js'); + const origSafePromise = safePromiseModule.safePromise; + vi.spyOn(safePromiseModule, 'safePromise').mockImplementation((p: Promise) => + p.then(() => [{ response: { status: 403, data: { error: 'Forbidden' } } }, null] as any) + ); + + mockHttps.mockResolvedValue({ status: 200, data: { stacks: [] } }); + mockChainGet.mockReturnValue({ + flatMap: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue([]) }), + }); + + const req = createMockReq(); + const result = await orgService.getAllStacks(req); + expect(result.status).toBe(403); + expect(result.data).toEqual({ error: 'Forbidden' }); + }); + }); + + describe('createStack', () => { + it('should create a stack via CS API', async () => { + mockHttps.mockResolvedValue({ + status: 201, + data: { stack: { api_key: 'new-stack', name: 'New Stack' } }, + }); + + const req = createMockReq({ + body: { + token_payload: { region: 'NA', user_id: 'user-123' }, + name: 'New Stack', + description: 'Test stack', + master_locale: 'en-us', + }, + }); + const result = await orgService.createStack(req); + + expect(result.status).toBe(201); + expect(result.data.stack.api_key).toBe('new-stack'); + }); + + it('should return error response when create stack API returns error', async () => { + const safePromiseModule = await import('../../../src/utils/index.js'); + vi.spyOn(safePromiseModule, 'safePromise').mockImplementation((p: Promise) => + p.then(() => [{ response: { status: 400, data: { error: 'Bad request' } } }, null] as any) + ); + + mockHttps.mockResolvedValue({ status: 201, data: {} }); + + const req = createMockReq({ + body: { + token_payload: { region: 'NA', user_id: 'user-123' }, + name: 'New Stack', + description: 'Test', + master_locale: 'en-us', + }, + }); + const result = await orgService.createStack(req); + + expect(result.status).toBe(400); + }); + }); + + describe('getLocales', () => { + it('should return locales from CS API', async () => { + mockHttps.mockResolvedValue({ + status: 200, + data: { locales: [{ code: 'en-us', name: 'English' }] }, + }); + + const req = createMockReq(); + const result = await orgService.getLocales(req); + + expect(result.status).toBe(200); + expect(result.data.locales).toHaveLength(1); + }); + + it('should return error when get locales API returns error', async () => { + const safePromiseModule = await import('../../../src/utils/index.js'); + vi.spyOn(safePromiseModule, 'safePromise').mockImplementation((p: Promise) => + p.then(() => [{ response: { status: 401, data: { error: 'Unauthorized' } } }, null] as any) + ); + + mockHttps.mockResolvedValue({ status: 200, data: {} }); + + const req = createMockReq(); + const result = await orgService.getLocales(req); + + expect(result.status).toBe(401); + }); + }); + + describe('getStackStatus', () => { + it('should return stack status with content type count', async () => { + mockHttps + .mockResolvedValueOnce({ + status: 200, + data: { stacks: [{ api_key: 'stack-1', name: 'Stack 1' }] }, + }) + .mockResolvedValueOnce({ + status: 200, + data: { count: 5 }, + }); + + const req = createMockReq({ + params: { orgId: 'org-123' }, + body: { + token_payload: { region: 'NA', user_id: 'user-123' }, + stack_api_key: 'stack-1', + }, + }); + const result = await orgService.getStackStatus(req); + + expect(result.status).toBe(200); + expect(result.data.contenttype_count).toBe(5); + }); + + it('should return error when stacks fetch fails', async () => { + const safePromiseModule = await import('../../../src/utils/index.js'); + vi.spyOn(safePromiseModule, 'safePromise').mockImplementation((p: Promise) => + p.then(() => [{ response: { status: 500, data: {} } }, null] as any) + ); + + mockHttps.mockResolvedValue({ status: 200, data: { stacks: [] } }); + + const req = createMockReq({ + params: { orgId: 'org-123' }, + body: { + token_payload: { region: 'NA', user_id: 'user-123' }, + stack_api_key: 'stack-1', + }, + }); + const result = await orgService.getStackStatus(req); + + expect(result.status).toBe(500); + expect(result.data.message).toBeDefined(); + }); + + it('should throw when stack not found', async () => { + mockHttps.mockResolvedValueOnce({ + status: 200, + data: { stacks: [{ api_key: 'other-stack' }] }, + }); + + const req = createMockReq({ + params: { orgId: 'org-123' }, + body: { + token_payload: { region: 'NA', user_id: 'user-123' }, + stack_api_key: 'stack-1', + }, + }); + await expect(orgService.getStackStatus(req)).rejects.toThrow(); + }); + }); + + describe('getStackLocale', () => { + it('should return stack locales', async () => { + mockHttps.mockResolvedValue({ + status: 200, + data: { locales: [{ code: 'en-us', name: 'English' }] }, + }); + + const req = createMockReq({ + body: { + token_payload: { region: 'NA', user_id: 'user-123' }, + stack_api_key: 'stack-1', + }, + }); + const result = await orgService.getStackLocale(req); + + expect(result.status).toBe(200); + expect(result.data.locales).toHaveLength(1); + }); + + it('should return error when get stack locale fails', async () => { + const safePromiseModule = await import('../../../src/utils/index.js'); + vi.spyOn(safePromiseModule, 'safePromise').mockImplementation((p: Promise) => + p.then(() => [{ response: { status: 404, data: {} } }, null] as any) + ); + + mockHttps.mockResolvedValue({ status: 200, data: {} }); + + const req = createMockReq({ + body: { + token_payload: { region: 'NA', user_id: 'user-123' }, + stack_api_key: 'stack-1', + }, + }); + const result = await orgService.getStackLocale(req); + + expect(result.status).toBe(404); + }); + }); + + describe('getOrgDetails', () => { + it('should return org details with plan', async () => { + mockHttps.mockResolvedValue({ + status: 200, + data: { organization: { uid: 'org-123', name: 'Test Org' } }, + }); + + const req = createMockReq({ params: { orgId: 'org-123' } }); + const result = await orgService.getOrgDetails(req); + + expect(result.status).toBe(200); + expect(result.data.organization.uid).toBe('org-123'); + }); + + it('should return error when get org details fails', async () => { + const safePromiseModule = await import('../../../src/utils/index.js'); + vi.spyOn(safePromiseModule, 'safePromise').mockImplementation((p: Promise) => + p.then(() => [{ response: { status: 404, data: {} } }, null] as any) + ); + + mockHttps.mockResolvedValue({ status: 200, data: {} }); + + const req = createMockReq({ params: { orgId: 'org-123' } }); + const result = await orgService.getOrgDetails(req); + + expect(result.status).toBe(404); + }); + }); +}); diff --git a/api/tests/unit/services/projects.service.test.ts b/api/tests/unit/services/projects.service.test.ts new file mode 100644 index 000000000..55ea2e419 --- /dev/null +++ b/api/tests/unit/services/projects.service.test.ts @@ -0,0 +1,701 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { createMockProject } from '../../fixtures/project.fixture.js'; + +const { + mockProjectRead, + mockProjectUpdate, + mockProjectWrite, + mockFindValue, + mockFilterValue, + mockGetProjectUtil, + mockHttps, + mockGetAuthToken, + mockFindIndexValue, +} = vi.hoisted(() => ({ + mockProjectRead: vi.fn(), + mockProjectUpdate: vi.fn(), + mockProjectWrite: vi.fn(), + mockFindValue: vi.fn(), + mockFilterValue: vi.fn(), + mockGetProjectUtil: vi.fn(), + mockHttps: vi.fn(), + mockGetAuthToken: vi.fn(), + mockFindIndexValue: vi.fn(), +})); + +vi.mock('../../../src/models/project-lowdb.js', () => ({ + default: { + read: mockProjectRead, + update: mockProjectUpdate, + write: mockProjectWrite, + chain: { + get: vi.fn().mockReturnValue({ + filter: vi.fn().mockReturnValue({ value: mockFilterValue }), + find: vi.fn().mockReturnValue({ value: mockFindValue }), + findIndex: vi.fn().mockReturnValue({ value: mockFindIndexValue }), + }), + }, + data: { projects: [] }, + }, +})); + +vi.mock('../../../src/utils/get-project.utils.js', () => ({ default: mockGetProjectUtil })); +vi.mock('../../../src/utils/https.utils.js', () => ({ default: mockHttps })); +vi.mock('../../../src/utils/auth.utils.js', () => ({ default: mockGetAuthToken })); +vi.mock('../../../src/utils/logger.js', () => ({ + default: { error: vi.fn(), info: vi.fn(), warn: vi.fn() }, +})); +vi.mock('../../../src/utils/custom-logger.utils.js', () => ({ + default: vi.fn().mockResolvedValue(undefined), +})); +vi.mock('../../../src/config/index.js', () => ({ + config: { + CS_API: { NA: 'https://api.contentstack.io/v3' }, + }, +})); +vi.mock('../../../src/models/contentTypesMapper-lowdb.js', () => ({ + default: { + read: vi.fn().mockResolvedValue(undefined), + update: vi.fn(), + write: vi.fn(), + chain: { + get: vi.fn().mockReturnValue({ + filter: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue([]) }), + find: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(null) }), + findIndex: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(-1) }), + }), + }, + data: { ContentTypesMappers: [] }, + }, +})); +vi.mock('../../../src/models/FieldMapper.js', () => ({ + default: { + read: vi.fn().mockResolvedValue(undefined), + update: vi.fn(), + write: vi.fn(), + chain: { + get: vi.fn().mockReturnValue({ + filter: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue([]) }), + find: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(null) }), + findIndex: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(-1) }), + }), + }, + data: { field_mapper: [] }, + }, +})); +vi.mock('../../../src/services/contentMapper.service.js', () => ({ + contentMapperService: { + removeMapping: vi.fn().mockResolvedValue(undefined), + resetAllContentTypesMapping: vi.fn().mockResolvedValue(undefined), + }, +})); + +import { projectService } from '../../../src/services/projects.service.js'; + +const makeReq = (params: any = {}, body: any = {}) => + ({ params, body } as any); + +const tokenPayload = { region: 'NA', user_id: 'user-123' }; + +describe('projects.service', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockProjectRead.mockResolvedValue(undefined); + mockGetAuthToken.mockResolvedValue('cs-auth-token'); + }); + + describe('getAllProjects', () => { + it('should return filtered projects', async () => { + const projects = [createMockProject(), createMockProject({ id: 'proj-2' })]; + mockFilterValue.mockReturnValue(projects); + const result = await projectService.getAllProjects( + makeReq({ orgId: 'org-123' }, { token_payload: tokenPayload }) + ); + expect(result).toEqual(projects); + }); + + it('should throw NotFoundError when projects is null', async () => { + mockFilterValue.mockReturnValue(null); + await expect( + projectService.getAllProjects(makeReq({ orgId: 'org-123' }, { token_payload: tokenPayload })) + ).rejects.toThrow(); + }); + + it('should return empty array when no projects match', async () => { + mockFilterValue.mockReturnValue([]); + const result = await projectService.getAllProjects( + makeReq({ orgId: 'org-123' }, { token_payload: tokenPayload }) + ); + expect(result).toEqual([]); + }); + + it('should throw BadRequestError when orgId is missing', async () => { + await expect( + projectService.getAllProjects(makeReq({}, { token_payload: tokenPayload })) + ).rejects.toThrow('Organization ID is required'); + }); + + it('should throw BadRequestError when token_payload is missing', async () => { + await expect( + projectService.getAllProjects(makeReq({ orgId: 'org-123' }, {})) + ).rejects.toThrow('Token payload is required'); + }); + }); + + describe('getProject', () => { + it('should return project by ID', async () => { + const project = createMockProject(); + mockGetProjectUtil.mockResolvedValue(project); + const result = await projectService.getProject( + makeReq({ orgId: 'org-123', projectId: project.id }, { token_payload: tokenPayload }) + ); + expect(result).toEqual(project); + }); + + it('should throw BadRequestError when params missing', async () => { + await expect( + projectService.getProject(makeReq({}, { token_payload: tokenPayload })) + ).rejects.toThrow('Organization ID and Project ID are required'); + }); + }); + + describe('createProject', () => { + it('should create project and return success', async () => { + mockProjectUpdate.mockImplementation((fn: any) => { + const data = { projects: [] }; + fn(data); + return data; + }); + const result = await projectService.createProject( + makeReq({ orgId: 'org-123' }, { token_payload: tokenPayload, name: 'New', description: 'Desc' }) + ); + expect(result.status).toBe('success'); + expect(result.project.name).toBe('New'); + }); + + it('should throw BadRequestError when name is missing', async () => { + await expect( + projectService.createProject(makeReq({ orgId: 'org-123' }, { token_payload: tokenPayload })) + ).rejects.toThrow('Project name is required'); + }); + }); + + describe('updateProject', () => { + it('should update project and return success', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject(); + mockProjectUpdate.mockImplementation(async (fn: any) => { + const data = { projects: [project] }; + fn(data); + }); + const result = await projectService.updateProject( + makeReq( + { orgId: 'org-123', projectId: project.id }, + { token_payload: tokenPayload, name: 'Updated', description: 'Updated desc' } + ) + ); + expect(result.status).toBe('success'); + }); + }); + + describe('updateLegacyCMS', () => { + it('should update legacy CMS successfully', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject({ status: 0, legacy_cms: {} }); + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { projects: [project] }; + mockProjectUpdate.mockImplementation(async (fn: any) => fn({ projects: [project] })); + + const result = await projectService.updateLegacyCMS( + makeReq( + { orgId: 'org-123', projectId: project.id }, + { token_payload: tokenPayload, legacy_cms: 'wordpress' } + ) + ); + expect(result.status).toBe(200); + }); + + it('should throw BadRequestError when project status is migration completed', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject({ status: 5 }); + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { projects: [project] }; + + await expect( + projectService.updateLegacyCMS( + makeReq( + { orgId: 'org-123', projectId: project.id }, + { token_payload: tokenPayload, legacy_cms: 'wordpress' } + ) + ) + ).rejects.toThrow(); + }); + + it('should throw BadRequestError when legacy_cms is missing', async () => { + await expect( + projectService.updateLegacyCMS( + makeReq({ orgId: 'org-123', projectId: 'p1' }, { token_payload: tokenPayload }) + ) + ).rejects.toThrow('Legacy CMS data is required'); + }); + }); + + describe('updateAffix', () => { + it('should update affix successfully', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject(); + mockProjectUpdate.mockImplementation(async (fn: any) => fn({ projects: [project] })); + + const result = await projectService.updateAffix( + makeReq( + { orgId: 'org-123', projectId: project.id }, + { token_payload: tokenPayload, affix: 'pre' } + ) + ); + expect(result.status).toBe(200); + }); + + it('should throw BadRequestError when affix is empty', async () => { + await expect( + projectService.updateAffix( + makeReq({ orgId: 'org-123', projectId: 'p1' }, { token_payload: tokenPayload, affix: '' }) + ) + ).rejects.toThrow('Affix is required'); + }); + }); + + describe('affixConfirmation', () => { + it('should update affix confirmation', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject(); + mockProjectUpdate.mockImplementation(async (fn: any) => fn({ projects: [project] })); + + const result = await projectService.affixConfirmation( + makeReq( + { orgId: 'org-123', projectId: project.id }, + { token_payload: tokenPayload, affix_confirmation: true } + ) + ); + expect(result.status).toBe(200); + }); + }); + + describe('updateFileFormat', () => { + it('should update file format successfully', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject({ status: 0, legacy_cms: {} }); + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { projects: [project] }; + mockProjectUpdate.mockImplementation(async (fn: any) => fn({ projects: [project] })); + + const result = await projectService.updateFileFormat( + makeReq( + { orgId: 'org-123', projectId: project.id }, + { token_payload: tokenPayload, file_format: 'json', file_path: '/path', is_localPath: true, is_fileValid: true } + ) + ); + expect(result.status).toBe(200); + }); + + it('should update file format with awsDetails', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject({ status: 0, legacy_cms: {} }); + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { projects: [project] }; + mockProjectUpdate.mockImplementation(async (fn: any) => fn({ projects: [project] })); + + const result = await projectService.updateFileFormat( + makeReq( + { orgId: 'org-123', projectId: project.id }, + { + token_payload: tokenPayload, + file_format: 'json', + file_path: '/path', + is_localPath: false, + is_fileValid: true, + awsDetails: { awsRegion: 'us-east-1', bucketName: 'bucket', bucketKey: 'key' }, + } + ) + ); + expect(result.status).toBe(200); + }); + + it('should throw when project status is migration completed', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject({ status: 5 }); + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { projects: [project] }; + + await expect( + projectService.updateFileFormat( + makeReq( + { orgId: 'org-123', projectId: project.id }, + { token_payload: tokenPayload, file_format: 'json' } + ) + ) + ).rejects.toThrow(); + }); + }); + + describe('fileformatConfirmation', () => { + it('should update fileformat confirmation', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject(); + mockProjectUpdate.mockImplementation(async (fn: any) => fn({ projects: [project] })); + + const result = await projectService.fileformatConfirmation( + makeReq( + { orgId: 'org-123', projectId: project.id }, + { token_payload: tokenPayload, fileformat_confirmation: true } + ) + ); + expect(result.status).toBe(200); + }); + + it('should skip update when fileformat_confirmation is undefined', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const result = await projectService.fileformatConfirmation( + makeReq( + { orgId: 'org-123', projectId: 'p1' }, + { token_payload: tokenPayload } + ) + ); + expect(result.status).toBe(200); + expect(mockProjectUpdate).not.toHaveBeenCalled(); + }); + }); + + describe('updateDestinationStack', () => { + it('should update destination stack when stack is found', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject({ status: 0, current_step: 2 }); + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { projects: [project] }; + mockHttps.mockResolvedValue({ + data: { stacks: [{ api_key: 'stack-key' }] }, + status: 200, + }); + mockProjectUpdate.mockImplementation(async (fn: any) => fn({ projects: [project] })); + + const result = await projectService.updateDestinationStack( + makeReq( + { orgId: 'org-123', projectId: project.id }, + { token_payload: tokenPayload, stack_api_key: 'stack-key' } + ) + ); + expect(result.status).toBe(200); + }); + + it('should throw when stack not found in org stacks', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject({ status: 0, current_step: 2 }); + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { projects: [project] }; + mockHttps.mockResolvedValue({ + data: { stacks: [{ api_key: 'other-stack' }] }, + status: 200, + }); + + await expect( + projectService.updateDestinationStack( + makeReq( + { orgId: 'org-123', projectId: project.id }, + { token_payload: tokenPayload, stack_api_key: 'stack-key' } + ) + ) + ).rejects.toThrow(); + }); + + it('should throw when project status blocks update', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject({ status: 5, current_step: 2 }); + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { projects: [project] }; + + await expect( + projectService.updateDestinationStack( + makeReq( + { orgId: 'org-123', projectId: project.id }, + { token_payload: tokenPayload, stack_api_key: 'stack-key' } + ) + ) + ).rejects.toThrow(); + }); + + it('should return error when CS API fails', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject({ status: 0, current_step: 2 }); + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { projects: [project] }; + mockHttps.mockRejectedValue({ response: { data: 'error', status: 500 } }); + + const result = await projectService.updateDestinationStack( + makeReq( + { orgId: 'org-123', projectId: project.id }, + { token_payload: tokenPayload, stack_api_key: 'stack-key' } + ) + ); + expect(result.status).toBe(500); + }); + }); + + describe('updateCurrentStep', () => { + it('should advance from LEGACY_CMS to DESTINATION_STACK', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject({ + status: 0, + current_step: 1, + legacy_cms: { cms: 'wordpress', file_format: 'json' }, + }); + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { projects: [project] }; + mockProjectUpdate.mockImplementation(async (fn: any) => fn({ projects: [project] })); + + const result = await projectService.updateCurrentStep( + makeReq({ orgId: 'org-123', projectId: project.id }, { token_payload: tokenPayload }) + ); + expect(result).toBeDefined(); + }); + + it('should advance from DESTINATION_STACK to CONTENT_MAPPING', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject({ + status: 0, + current_step: 2, + legacy_cms: { cms: 'wordpress', file_format: 'json' }, + destination_stack_id: 'stack-1', + }); + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { projects: [project] }; + mockProjectUpdate.mockImplementation(async (fn: any) => fn({ projects: [project] })); + + const result = await projectService.updateCurrentStep( + makeReq({ orgId: 'org-123', projectId: project.id }, { token_payload: tokenPayload }) + ); + expect(result).toBeDefined(); + }); + + it('should advance from CONTENT_MAPPING to TESTING', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject({ + status: 3, + current_step: 3, + legacy_cms: { cms: 'wordpress', file_format: 'json' }, + destination_stack_id: 'stack-1', + content_mapper: ['ct-1'], + }); + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { projects: [project] }; + mockProjectUpdate.mockImplementation(async (fn: any) => fn({ projects: [project] })); + + const result = await projectService.updateCurrentStep( + makeReq({ orgId: 'org-123', projectId: project.id }, { token_payload: tokenPayload }) + ); + expect(result).toBeDefined(); + }); + + it('should advance from TESTING to MIGRATION', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject({ + status: 4, + current_step: 4, + legacy_cms: { cms: 'wordpress', file_format: 'json' }, + destination_stack_id: 'stack-1', + content_mapper: ['ct-1'], + current_test_stack_id: 'test-stack-1', + migration_execution: true, + }); + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { projects: [project] }; + mockProjectUpdate.mockImplementation(async (fn: any) => fn({ projects: [project] })); + + const result = await projectService.updateCurrentStep( + makeReq({ orgId: 'org-123', projectId: project.id }, { token_payload: tokenPayload }) + ); + expect(result).toBeDefined(); + }); + + it('should complete MIGRATION step', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject({ + status: 4, + current_step: 5, + legacy_cms: { cms: 'wordpress', file_format: 'json' }, + destination_stack_id: 'stack-1', + content_mapper: ['ct-1'], + current_test_stack_id: 'test-stack-1', + isMigrationCompleted: true, + }); + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { projects: [project] }; + mockProjectUpdate.mockImplementation(async (fn: any) => fn({ projects: [project] })); + + const result = await projectService.updateCurrentStep( + makeReq({ orgId: 'org-123', projectId: project.id }, { token_payload: tokenPayload }) + ); + expect(result).toBeDefined(); + }); + + it('should throw when LEGACY_CMS step is incomplete', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject({ status: 0, current_step: 1, legacy_cms: {} }); + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { projects: [project] }; + + await expect( + projectService.updateCurrentStep( + makeReq({ orgId: 'org-123', projectId: project.id }, { token_payload: tokenPayload }) + ) + ).rejects.toThrow(); + }); + }); + + describe('deleteProject', () => { + it('should soft delete project when status is not completed', async () => { + const project = createMockProject({ status: 0 }); + mockGetProjectUtil.mockResolvedValue(0); + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { projects: [project] }; + mockProjectUpdate.mockImplementation(async (fn: any) => fn({ projects: [project] })); + + const result = await projectService.deleteProject( + makeReq({ orgId: 'org-123', projectId: project.id }, { token_payload: tokenPayload }) + ); + expect(result.status).toBe(200); + }); + + it('should hard delete project with content mappers when status is 5', async () => { + const project = createMockProject({ status: 5, content_mapper: ['ct-1'] }); + mockGetProjectUtil.mockResolvedValue(0); + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { projects: [project] }; + + const ctMock = await import('../../../src/models/contentTypesMapper-lowdb.js'); + (ctMock.default as any).chain.get.mockReturnValue({ + find: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue({ id: 'ct-1', fieldMapping: [] }) }), + findIndex: vi.fn().mockReturnValue({ value: vi.fn().mockReturnValue(0) }), + }); + mockProjectUpdate.mockImplementation(async (fn: any) => fn({ projects: [project] })); + + const result = await projectService.deleteProject( + makeReq({ orgId: 'org-123', projectId: project.id }, { token_payload: tokenPayload }) + ); + expect(result.status).toBe(200); + }); + }); + + describe('revertProject', () => { + it('should set isDeleted to false', async () => { + const project = createMockProject({ isDeleted: true }); + mockGetProjectUtil.mockResolvedValue(0); + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { projects: [project] }; + mockProjectUpdate.mockImplementation(async (fn: any) => fn({ projects: [project] })); + + const result = await projectService.revertProject( + makeReq({ orgId: 'org-123', projectId: project.id }, { token_payload: tokenPayload }) + ); + expect(result.status).toBe(200); + }); + + it('should throw NotFoundError when project not found', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { projects: [undefined] }; + + await expect( + projectService.revertProject( + makeReq({ orgId: 'org-123', projectId: 'p1' }, { token_payload: tokenPayload }) + ) + ).rejects.toThrow(); + }); + }); + + describe('updateStackDetails', () => { + it('should update stack details successfully', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject(); + mockProjectUpdate.mockImplementation(async (fn: any) => fn({ projects: [project] })); + + const result = await projectService.updateStackDetails( + makeReq( + { orgId: 'org-123', projectId: project.id }, + { token_payload: tokenPayload, stack_details: { uid: 's1', label: 'Stack' } } + ) + ); + expect(result.status).toBe(200); + }); + }); + + describe('updateContentMapper', () => { + it('should update content mapper keys', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject(); + mockProjectUpdate.mockImplementation(async (fn: any) => fn({ projects: [project] })); + + const result = await projectService.updateContentMapper( + makeReq( + { orgId: 'org-123', projectId: project.id }, + { token_payload: tokenPayload, content_mapper: { key: 'value' } } + ) + ); + expect(result.status).toBe(200); + }); + }); + + describe('updateMigrationExecution', () => { + it('should set migration_execution to true', async () => { + mockGetProjectUtil.mockResolvedValue(0); + const project = createMockProject(); + mockProjectUpdate.mockImplementation(async (fn: any) => fn({ projects: [project] })); + + const result = await projectService.updateMigrationExecution( + makeReq( + { orgId: 'org-123', projectId: project.id }, + { token_payload: tokenPayload } + ) + ); + expect(result.status).toBe(200); + }); + + it('should throw BadRequestError when params missing', async () => { + await expect( + projectService.updateMigrationExecution(makeReq({}, { token_payload: tokenPayload })) + ).rejects.toThrow('Organization ID and Project ID are required'); + }); + }); + + describe('getMigratedStacks', () => { + it('should return destination stacks of completed projects', async () => { + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { + projects: [ + { status: 5, current_step: 5, destination_stack_id: 'stack-1' }, + { status: 0, current_step: 1, destination_stack_id: '' }, + ], + }; + + const result = await projectService.getMigratedStacks( + makeReq({}, { token_payload: tokenPayload }) + ); + expect(result.status).toBe(200); + expect(result.destinationStacks).toEqual(['stack-1']); + }); + + it('should return empty array when no completed projects', async () => { + const mockModel = await import('../../../src/models/project-lowdb.js'); + (mockModel.default as any).data = { projects: [] }; + + const result = await projectService.getMigratedStacks( + makeReq({}, { token_payload: tokenPayload }) + ); + expect(result.destinationStacks).toEqual([]); + }); + + it('should throw BadRequestError when token_payload missing', async () => { + await expect( + projectService.getMigratedStacks(makeReq({}, {})) + ).rejects.toThrow('Token payload is required'); + }); + }); +}); diff --git a/api/tests/unit/services/taxonomy.service.test.ts b/api/tests/unit/services/taxonomy.service.test.ts new file mode 100644 index 000000000..40efa9d97 --- /dev/null +++ b/api/tests/unit/services/taxonomy.service.test.ts @@ -0,0 +1,204 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { + mockHttps, + mockGetAuthToken, + mockFsPromisesMkdir, + mockFsPromisesWriteFile, + mockPathJoin, +} = vi.hoisted(() => ({ + mockHttps: vi.fn(), + mockGetAuthToken: vi.fn(), + mockFsPromisesMkdir: vi.fn(), + mockFsPromisesWriteFile: vi.fn(), + mockPathJoin: vi.fn((...args: string[]) => args.join('/')), +})); + +vi.mock('../../../src/utils/https.utils.js', () => ({ default: mockHttps })); +vi.mock('../../../src/utils/auth.utils.js', () => ({ default: mockGetAuthToken })); +vi.mock('../../../src/utils/logger.js', () => ({ + default: { error: vi.fn(), info: vi.fn(), warn: vi.fn() }, +})); +vi.mock('../../../src/config/index.js', () => ({ + config: { CS_API: { NA: 'https://api.contentstack.io/v3', EU: 'https://eu-api.contentstack.com/v3' } }, +})); +vi.mock('fs', () => ({ + default: { + promises: { + mkdir: mockFsPromisesMkdir, + writeFile: mockFsPromisesWriteFile, + }, + }, +})); +vi.mock('path', () => ({ + default: { join: mockPathJoin }, +})); +vi.mock('../../../src/constants/index.js', () => ({ + MIGRATION_DATA_CONFIG: { + DATA: './cmsMigrationData', + TAXONOMIES_DIR_NAME: 'taxonomies', + TAXONOMIES_FILE_NAME: 'taxonomies.json', + }, + HTTP_TEXTS: { CS_ERROR: 'Contentstack API error' }, +})); + +import { taxonomyService } from '../../../src/services/taxonomy.service.js'; + +describe('taxonomy.service', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockGetAuthToken.mockResolvedValue('cs-auth-token'); + mockFsPromisesMkdir.mockResolvedValue(undefined); + mockFsPromisesWriteFile.mockResolvedValue(undefined); + mockHttps + .mockResolvedValueOnce({ + status: 200, + data: { + taxonomies: [ + { uid: 'tax-1', name: 'Category', description: 'Cat taxonomy' }, + ], + }, + }) + .mockResolvedValue({ + status: 200, + data: { + terms: [ + { uid: 'term-1', name: 'Root', parent_uid: null, children_count: 0 }, + ], + }, + }); + }); + + describe('createTaxonomy', () => { + it('should fetch taxonomies and create term files', async () => { + await taxonomyService.createTaxonomy({ + stackId: 'stack-123', + region: 'NA', + userId: 'user-1', + current_test_stack_id: 'test-stack-1', + orgId: 'org-1', + projectId: 'proj-1', + }); + + expect(mockGetAuthToken).toHaveBeenCalledWith('NA', 'user-1'); + expect(mockHttps).toHaveBeenCalledWith( + expect.objectContaining({ + method: 'GET', + url: expect.stringContaining('taxonomies'), + headers: expect.objectContaining({ + api_key: 'stack-123', + authtoken: 'cs-auth-token', + }), + }) + ); + expect(mockFsPromisesMkdir).toHaveBeenCalled(); + expect(mockFsPromisesWriteFile).toHaveBeenCalled(); + }); + + it('should return error object when taxonomies API fails', async () => { + mockHttps + .mockReset() + .mockRejectedValue({ response: { status: 500, data: { message: 'Error' } } }); + + const result = await taxonomyService.createTaxonomy({ + stackId: 'stack-456', + region: 'NA', + userId: 'user-2', + current_test_stack_id: 'test-stack-2', + orgId: 'org-2', + projectId: 'proj-2', + }); + + expect(result).toEqual({ + data: { message: 'Error' }, + status: 500, + }); + }); + + it('should create taxonomy JSON file with correct structure', async () => { + mockHttps + .mockReset() + .mockResolvedValueOnce({ + status: 200, + data: { taxonomies: [{ uid: 'tax-1', name: 'Tags', description: 'Tags taxonomy' }] }, + }) + .mockResolvedValue({ + status: 200, + data: { terms: [{ uid: 't1', name: 'Tag1', parent_uid: null, children_count: 0 }] }, + }); + + await taxonomyService.createTaxonomy({ + stackId: 'stack-789', + region: 'NA', + userId: 'user-3', + current_test_stack_id: 'test-stack-3', + orgId: 'org-3', + projectId: 'proj-3', + }); + + const writeCalls = mockFsPromisesWriteFile.mock.calls; + expect(writeCalls.length).toBeGreaterThan(0); + const taxonomiesFileCall = writeCalls.find((c) => c[0].includes('taxonomies.json')); + expect(taxonomiesFileCall).toBeDefined(); + if (taxonomiesFileCall) { + const parsed = JSON.parse(taxonomiesFileCall[1]); + expect(parsed['tax-1']).toEqual({ + uid: 'tax-1', + name: 'Tags', + description: 'Tags taxonomy', + }); + } + }); + + it('should recursively fetch descendant terms', async () => { + mockHttps + .mockReset() + .mockResolvedValueOnce({ + status: 200, + data: { + taxonomies: [{ uid: 'tax-2', name: 'Nested', description: '' }], + }, + }) + .mockResolvedValueOnce({ + status: 200, + data: { + terms: [ + { uid: 'root', name: 'Root', parent_uid: null, children_count: 1 }, + ], + }, + }) + .mockResolvedValue({ + status: 200, + data: { + terms: [{ uid: 'child', name: 'Child', parent_uid: 'root', children_count: 0 }], + }, + }); + + await taxonomyService.createTaxonomy({ + stackId: 'stack-nested', + region: 'NA', + userId: 'user-4', + current_test_stack_id: 'test-stack-4', + orgId: 'org-4', + projectId: 'proj-4', + }); + + expect(mockHttps.mock.calls.length).toBeGreaterThan(2); + }); + + it('should throw when getAuthtoken fails', async () => { + mockGetAuthToken.mockRejectedValue(new Error('Network failure')); + + await expect( + taxonomyService.createTaxonomy({ + stackId: 'stack-err', + region: 'NA', + userId: 'user-5', + current_test_stack_id: 'test-stack-5', + orgId: 'org-5', + projectId: 'proj-5', + }) + ).rejects.toThrow('Network failure'); + }); + }); +}); diff --git a/api/tests/unit/services/user.service.test.ts b/api/tests/unit/services/user.service.test.ts new file mode 100644 index 000000000..6903c04b5 --- /dev/null +++ b/api/tests/unit/services/user.service.test.ts @@ -0,0 +1,100 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockHttps, mockAuthModelRead, mockChainValue } = vi.hoisted(() => ({ + mockHttps: vi.fn(), + mockAuthModelRead: vi.fn(), + mockChainValue: vi.fn(), +})); + +vi.mock('../../../src/utils/https.utils.js', () => ({ default: mockHttps })); +vi.mock('../../../src/utils/logger.js', () => ({ + default: { error: vi.fn(), info: vi.fn(), warn: vi.fn() }, +})); +vi.mock('../../../src/config/index.js', () => ({ + config: { + CS_API: { NA: 'https://api.contentstack.io/v3' }, + }, +})); +vi.mock('../../../src/models/authentication.js', () => ({ + default: { + read: mockAuthModelRead, + chain: { + get: vi.fn().mockReturnValue({ + findIndex: vi.fn().mockReturnValue({ value: mockChainValue }), + }), + }, + data: { + users: [{ user_id: 'user-123', region: 'NA', authtoken: 'cs-token' }], + }, + }, +})); + +import { userService } from '../../../src/services/user.service.js'; + +describe('user.service', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockAuthModelRead.mockResolvedValue(undefined); + }); + + describe('getUserProfile', () => { + const createReq = () => ({ + body: { token_payload: { region: 'NA', user_id: 'user-123' } }, + }); + + it('should return user profile with orgs', async () => { + mockChainValue.mockReturnValue(0); + mockHttps.mockResolvedValue({ + status: 200, + data: { + user: { + email: 'test@example.com', + first_name: 'Test', + last_name: 'User', + organizations: [ + { uid: 'org-1', name: 'Org 1', org_roles: [{ admin: true }], is_owner: false }, + { uid: 'org-2', name: 'Org 2', org_roles: [], is_owner: true }, + ], + }, + }, + }); + + const result = await userService.getUserProfile(createReq() as any); + + expect(result.status).toBe(200); + expect(result.data.user.email).toBe('test@example.com'); + expect(result.data.user.orgs).toHaveLength(2); + }); + + it('should throw when user not found in AuthenticationModel', async () => { + mockChainValue.mockReturnValue(-1); + + await expect( + userService.getUserProfile(createReq() as any) + ).rejects.toThrow(); + }); + + it('should return error response when CS API fails', async () => { + mockChainValue.mockReturnValue(0); + mockHttps.mockRejectedValue({ + response: { data: { error: 'Token expired' }, status: 401 }, + }); + + const result = await userService.getUserProfile(createReq() as any); + + expect(result.status).toBe(401); + }); + + it('should throw when CS API returns no user', async () => { + mockChainValue.mockReturnValue(0); + mockHttps.mockResolvedValue({ + status: 200, + data: {}, + }); + + await expect( + userService.getUserProfile(createReq() as any) + ).rejects.toThrow(); + }); + }); +}); diff --git a/api/tests/unit/utils/async-router.utils.test.ts b/api/tests/unit/utils/async-router.utils.test.ts new file mode 100644 index 000000000..ab2f7eb68 --- /dev/null +++ b/api/tests/unit/utils/async-router.utils.test.ts @@ -0,0 +1,37 @@ +import { describe, it, expect, vi } from 'vitest'; +import { asyncRouter } from '../../../src/utils/async-router.utils.js'; + +describe('async-router.utils', () => { + const mockReq = {} as any; + const mockRes = {} as any; + const mockNext = vi.fn(); + + it('should call the wrapped function with req, res, next', async () => { + const handler = vi.fn().mockResolvedValue(undefined); + const wrapped = asyncRouter(handler); + + await wrapped(mockReq, mockRes, mockNext); + + expect(handler).toHaveBeenCalledWith(mockReq, mockRes, mockNext); + }); + + it('should catch rejected promises and pass error to next', async () => { + const error = new Error('Async failure'); + const handler = vi.fn().mockRejectedValue(error); + const wrapped = asyncRouter(handler); + + await wrapped(mockReq, mockRes, mockNext); + + expect(mockNext).toHaveBeenCalledWith(error); + }); + + it('should not call next on success if handler does not call it', async () => { + const handler = vi.fn().mockResolvedValue(undefined); + const next = vi.fn(); + const wrapped = asyncRouter(handler); + + await wrapped(mockReq, mockRes, next); + + expect(next).not.toHaveBeenCalled(); + }); +}); diff --git a/api/tests/unit/utils/auth.utils.test.ts b/api/tests/unit/utils/auth.utils.test.ts new file mode 100644 index 000000000..26a006ed9 --- /dev/null +++ b/api/tests/unit/utils/auth.utils.test.ts @@ -0,0 +1,63 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockRead, mockChain } = vi.hoisted(() => { + const mockChain = { + get: vi.fn().mockReturnThis(), + findIndex: vi.fn().mockReturnThis(), + value: vi.fn(), + }; + return { + mockRead: vi.fn(), + mockChain, + }; +}); + +vi.mock('../../../src/models/authentication.js', () => ({ + default: { + read: mockRead, + chain: mockChain, + data: { users: [] }, + }, +})); + +vi.mock('../../../src/utils/custom-errors.utils.js', async (importOriginal) => { + const actual = await importOriginal(); + return actual; +}); + +import getAuthToken from '../../../src/utils/auth.utils.js'; +import AuthenticationModel from '../../../src/models/authentication.js'; + +describe('auth.utils', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockRead.mockResolvedValue(undefined); + }); + + it('should return authtoken for a valid user', async () => { + mockChain.value.mockReturnValue(0); + (AuthenticationModel as any).data = { + users: [{ region: 'NA', user_id: 'user-123', authtoken: 'valid-token' }], + }; + + const token = await getAuthToken('NA', 'user-123'); + expect(token).toBe('valid-token'); + expect(mockRead).toHaveBeenCalled(); + }); + + it('should throw UnauthorizedError when user is not found', async () => { + mockChain.value.mockReturnValue(-1); + (AuthenticationModel as any).data = { users: [] }; + + await expect(getAuthToken('NA', 'unknown-user')).rejects.toThrow(); + }); + + it('should throw UnauthorizedError when authtoken is missing', async () => { + mockChain.value.mockReturnValue(0); + (AuthenticationModel as any).data = { + users: [{ region: 'NA', user_id: 'user-123', authtoken: '' }], + }; + + await expect(getAuthToken('NA', 'user-123')).rejects.toThrow(); + }); +}); diff --git a/api/tests/unit/utils/batch-processor.utils.test.ts b/api/tests/unit/utils/batch-processor.utils.test.ts new file mode 100644 index 000000000..65855d596 --- /dev/null +++ b/api/tests/unit/utils/batch-processor.utils.test.ts @@ -0,0 +1,100 @@ +import { describe, it, expect, vi } from 'vitest'; +import { BatchProcessor, processBatches } from '../../../src/utils/batch-processor.utils.js'; + +describe('batch-processor.utils', () => { + describe('BatchProcessor', () => { + it('should process all items in correct batch sizes', async () => { + const processor = new BatchProcessor({ + batchSize: 2, + concurrency: 2, + delayBetweenBatches: 0, + }); + + const items = [1, 2, 3, 4, 5]; + const results = await processor.processBatches( + items, + async (item) => item * 2 + ); + + expect(results).toEqual([2, 4, 6, 8, 10]); + }); + + it('should handle empty arrays', async () => { + const processor = new BatchProcessor({ + batchSize: 5, + concurrency: 2, + delayBetweenBatches: 0, + }); + + const results = await processor.processBatches([], async (item) => item); + expect(results).toEqual([]); + }); + + it('should respect concurrency limit', async () => { + let maxConcurrent = 0; + let currentConcurrent = 0; + + const processor = new BatchProcessor({ + batchSize: 10, + concurrency: 2, + delayBetweenBatches: 0, + }); + + const items = [1, 2, 3, 4]; + await processor.processBatches(items, async (item) => { + currentConcurrent++; + maxConcurrent = Math.max(maxConcurrent, currentConcurrent); + await new Promise((r) => setTimeout(r, 10)); + currentConcurrent--; + return item; + }); + + expect(maxConcurrent).toBeLessThanOrEqual(2); + }); + + it('should call onBatchComplete callback', async () => { + const processor = new BatchProcessor({ + batchSize: 2, + concurrency: 2, + delayBetweenBatches: 0, + }); + + const callback = vi.fn(); + await processor.processBatches( + [1, 2, 3, 4], + async (item) => item, + callback + ); + + expect(callback).toHaveBeenCalledTimes(2); + expect(callback).toHaveBeenCalledWith(1, 2, [1, 2]); + expect(callback).toHaveBeenCalledWith(2, 2, [3, 4]); + }); + + it('should apply delay between batches', async () => { + const processor = new BatchProcessor({ + batchSize: 1, + concurrency: 1, + delayBetweenBatches: 50, + }); + + const start = Date.now(); + await processor.processBatches([1, 2, 3], async (item) => item); + const elapsed = Date.now() - start; + + expect(elapsed).toBeGreaterThanOrEqual(80); + }); + }); + + describe('processBatches utility function', () => { + it('should process items using the utility function', async () => { + const results = await processBatches( + [1, 2, 3], + async (item) => item * 3, + { batchSize: 2, concurrency: 1, delayBetweenBatches: 0 } + ); + + expect(results).toEqual([3, 6, 9]); + }); + }); +}); diff --git a/api/tests/unit/utils/custom-errors.utils.test.ts b/api/tests/unit/utils/custom-errors.utils.test.ts new file mode 100644 index 000000000..2b4c45a36 --- /dev/null +++ b/api/tests/unit/utils/custom-errors.utils.test.ts @@ -0,0 +1,110 @@ +import { describe, it, expect } from 'vitest'; +import { + AppError, + NotFoundError, + BadRequestError, + DatabaseError, + ValidationError, + InternalServerError, + UnauthorizedError, + S3Error, + ExceptionFunction, +} from '../../../src/utils/custom-errors.utils.js'; + +describe('Custom Error Classes', () => { + describe('AppError', () => { + it('should create an error with statusCode and message', () => { + const error = new AppError(418, 'I am a teapot'); + expect(error.statusCode).toBe(418); + expect(error.message).toBe('I am a teapot'); + expect(error).toBeInstanceOf(Error); + expect(error).toBeInstanceOf(AppError); + }); + }); + + describe('NotFoundError', () => { + it('should default to 404 and "Not Found"', () => { + const error = new NotFoundError(); + expect(error.statusCode).toBe(404); + expect(error.message).toBe('Not Found'); + expect(error).toBeInstanceOf(AppError); + }); + + it('should accept a custom message', () => { + const error = new NotFoundError('Resource missing'); + expect(error.statusCode).toBe(404); + expect(error.message).toBe('Resource missing'); + }); + }); + + describe('BadRequestError', () => { + it('should default to 400 and "Bad Request"', () => { + const error = new BadRequestError(); + expect(error.statusCode).toBe(400); + expect(error.message).toBe('Bad Request'); + expect(error).toBeInstanceOf(AppError); + }); + + it('should accept a custom message', () => { + const error = new BadRequestError('Invalid input'); + expect(error.message).toBe('Invalid input'); + }); + }); + + describe('DatabaseError', () => { + it('should default to 500 and "DB error"', () => { + const error = new DatabaseError(); + expect(error.statusCode).toBe(500); + expect(error.message).toBe('DB error'); + expect(error).toBeInstanceOf(AppError); + }); + }); + + describe('ValidationError', () => { + it('should default to 422 and "User validation error"', () => { + const error = new ValidationError(); + expect(error.statusCode).toBe(422); + expect(error.message).toBe('User validation error'); + expect(error).toBeInstanceOf(AppError); + }); + }); + + describe('InternalServerError', () => { + it('should default to 500 with internal error message', () => { + const error = new InternalServerError(); + expect(error.statusCode).toBe(500); + expect(error.message).toBeTruthy(); + expect(error).toBeInstanceOf(AppError); + }); + }); + + describe('UnauthorizedError', () => { + it('should default to 401', () => { + const error = new UnauthorizedError(); + expect(error.statusCode).toBe(401); + expect(error).toBeInstanceOf(AppError); + }); + + it('should accept a custom message', () => { + const error = new UnauthorizedError('Token expired'); + expect(error.message).toBe('Token expired'); + }); + }); + + describe('S3Error', () => { + it('should default to 500', () => { + const error = new S3Error(); + expect(error.statusCode).toBe(500); + expect(error).toBeInstanceOf(AppError); + }); + }); + + describe('ExceptionFunction', () => { + it('should accept custom message and status', () => { + const error = new ExceptionFunction('Custom error', 503); + expect(error.statusCode).toBe(503); + expect(error.message).toBe('Custom error'); + expect(error).toBeInstanceOf(AppError); + }); + }); +}); diff --git a/api/tests/unit/utils/field-attacher.utils.test.ts b/api/tests/unit/utils/field-attacher.utils.test.ts new file mode 100644 index 000000000..c4fcc32b9 --- /dev/null +++ b/api/tests/unit/utils/field-attacher.utils.test.ts @@ -0,0 +1,224 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { + mockProjectRead, + mockContentTypesRead, + mockFieldMapperRead, + mockProjectChain, + mockContentTypesChain, + mockFieldMapperChain, + mockContenTypeMaker, +} = vi.hoisted(() => { + const mockProjectChain = { + get: vi.fn().mockReturnThis(), + find: vi.fn().mockReturnThis(), + value: vi.fn(), + }; + const mockContentTypesChain = { + get: vi.fn().mockReturnThis(), + find: vi.fn().mockReturnThis(), + value: vi.fn(), + }; + const mockFieldMapperChain = { + get: vi.fn().mockReturnThis(), + find: vi.fn().mockReturnThis(), + value: vi.fn(), + }; + return { + mockProjectRead: vi.fn(), + mockContentTypesRead: vi.fn(), + mockFieldMapperRead: vi.fn(), + mockProjectChain, + mockContentTypesChain, + mockFieldMapperChain, + mockContenTypeMaker: vi.fn(), + }; +}); + +vi.mock('../../../src/models/project-lowdb.js', () => ({ + default: { + read: mockProjectRead, + chain: mockProjectChain, + }, +})); + +vi.mock('../../../src/models/contentTypesMapper-lowdb.js', () => ({ + default: { + read: mockContentTypesRead, + chain: mockContentTypesChain, + }, +})); + +vi.mock('../../../src/models/FieldMapper.js', () => ({ + default: { + read: mockFieldMapperRead, + chain: mockFieldMapperChain, + }, +})); + +vi.mock('../../../src/utils/content-type-creator.utils.js', () => ({ + contenTypeMaker: (...args: any[]) => mockContenTypeMaker(...args), +})); + +import { fieldAttacher } from '../../../src/utils/field-attacher.utils.js'; + +describe('field-attacher.utils', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockProjectRead.mockResolvedValue(undefined); + mockContentTypesRead.mockResolvedValue(undefined); + mockFieldMapperRead.mockResolvedValue(undefined); + mockContenTypeMaker.mockResolvedValue(undefined); + }); + + it('should return empty array when project has no content_mapper', async () => { + mockProjectChain.value.mockReturnValue({ + id: 'proj-1', + org_id: 'org-1', + content_mapper: undefined, + }); + + const result = await fieldAttacher({ + projectId: 'proj-1', + orgId: 'org-1', + destinationStackId: 'stack-1', + region: 'NA', + user_id: 'user-1', + }); + + expect(result).toEqual([]); + expect(mockProjectRead).toHaveBeenCalled(); + expect(mockContentTypesRead).toHaveBeenCalled(); + expect(mockFieldMapperRead).toHaveBeenCalled(); + expect(mockContenTypeMaker).not.toHaveBeenCalled(); + }); + + it('should return empty array when project has empty content_mapper', async () => { + mockProjectChain.value.mockReturnValue({ + id: 'proj-1', + org_id: 'org-1', + content_mapper: [], + }); + + const result = await fieldAttacher({ + projectId: 'proj-1', + orgId: 'org-1', + destinationStackId: 'stack-1', + region: 'NA', + user_id: 'user-1', + }); + + expect(result).toEqual([]); + expect(mockContenTypeMaker).not.toHaveBeenCalled(); + }); + + it('should call contenTypeMaker for each content type and return contentTypes', async () => { + const contentType1 = { + id: 'ct-1', + fieldMapping: ['field-1'], + }; + const field1 = { id: 'field-1', display_name: 'Title' }; + + mockProjectChain.value.mockReturnValue({ + id: 'proj-1', + org_id: 'org-1', + content_mapper: ['ct-1'], + stackDetails: { isNewStack: false }, + mapperKeys: {}, + }); + + mockContentTypesChain.value.mockReturnValue(contentType1); + mockFieldMapperChain.value.mockReturnValue(field1); + + const result = await fieldAttacher({ + projectId: 'proj-1', + orgId: 'org-1', + destinationStackId: 'stack-1', + region: 'NA', + user_id: 'user-1', + }); + + expect(mockContenTypeMaker).toHaveBeenCalledWith( + expect.objectContaining({ + contentType: expect.objectContaining({ + id: 'ct-1', + fieldMapping: [field1], + }), + destinationStackId: 'stack-1', + projectId: 'proj-1', + newStack: false, + keyMapper: {}, + region: 'NA', + user_id: 'user-1', + }), + ); + expect(result).toHaveLength(1); + }); + + it('should handle content type with no fieldMapping', async () => { + const contentType = { id: 'ct-1', fieldMapping: undefined }; + + mockProjectChain.value.mockReturnValue({ + id: 'proj-1', + org_id: 'org-1', + content_mapper: ['ct-1'], + stackDetails: { isNewStack: true }, + mapperKeys: {}, + }); + + mockContentTypesChain.value.mockReturnValue(contentType); + + const result = await fieldAttacher({ + projectId: 'proj-1', + orgId: 'org-1', + destinationStackId: 'stack-1', + region: 'NA', + user_id: 'user-1', + }); + + expect(mockContenTypeMaker).toHaveBeenCalled(); + expect(result).toHaveLength(1); + }); + + it('should return empty array when project is not found', async () => { + mockProjectChain.value.mockReturnValue(undefined); + + const result = await fieldAttacher({ + projectId: 'proj-999', + orgId: 'org-1', + destinationStackId: 'stack-1', + region: 'NA', + user_id: 'user-1', + }); + + expect(result).toEqual([]); + }); + + it('should handle multiple content types in content_mapper', async () => { + const contentType1 = { id: 'ct-1', fieldMapping: [] }; + const contentType2 = { id: 'ct-2', fieldMapping: [] }; + + mockProjectChain.value.mockReturnValue({ + id: 'proj-1', + org_id: 'org-1', + content_mapper: ['ct-1', 'ct-2'], + stackDetails: {}, + mapperKeys: {}, + }); + + mockContentTypesChain.value + .mockReturnValueOnce(contentType1) + .mockReturnValueOnce(contentType2); + + const result = await fieldAttacher({ + projectId: 'proj-1', + orgId: 'org-1', + destinationStackId: 'stack-1', + region: 'NA', + user_id: 'user-1', + }); + + expect(mockContenTypeMaker).toHaveBeenCalledTimes(2); + expect(result).toHaveLength(2); + }); +}); diff --git a/api/tests/unit/utils/get-project.utils.test.ts b/api/tests/unit/utils/get-project.utils.test.ts new file mode 100644 index 000000000..e8224cf60 --- /dev/null +++ b/api/tests/unit/utils/get-project.utils.test.ts @@ -0,0 +1,90 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockRead, mockFind, mockFindIndex, mockChain } = vi.hoisted(() => { + const mockFind = vi.fn(); + const mockFindIndex = vi.fn(); + const mockChain = { + get: vi.fn().mockReturnValue({ + find: mockFind, + findIndex: mockFindIndex, + }), + }; + return { + mockRead: vi.fn(), + mockFind, + mockFindIndex, + mockChain, + }; +}); + +vi.mock('../../../src/models/project-lowdb.js', () => ({ + default: { + read: mockRead, + chain: mockChain, + }, +})); + +vi.mock('../../../src/utils/logger.js', () => ({ + default: { error: vi.fn(), info: vi.fn(), warn: vi.fn() }, +})); + +vi.mock('../../../src/utils/custom-logger.utils.js', () => ({ + default: vi.fn().mockResolvedValue(undefined), +})); + +import getProjectUtil from '../../../src/utils/get-project.utils.js'; + +describe('get-project.utils', () => { + const validUuid = 'f47ac10b-58cc-4372-a567-0e02b2c3d479'; + const mockQuery = { id: validUuid, org_id: 'org-123', region: 'NA', owner: 'user-123' }; + + beforeEach(() => { + vi.clearAllMocks(); + mockRead.mockResolvedValue(undefined); + }); + + it('should throw BadRequestError for invalid UUID', async () => { + await expect(getProjectUtil('invalid-id', mockQuery)).rejects.toThrow( + 'Provided project ID is invalid.' + ); + }); + + it('should return project when found', async () => { + const mockProject = { id: validUuid, name: 'Test' }; + mockFind.mockReturnValue({ value: () => mockProject }); + mockChain.get.mockReturnValue({ find: () => ({ value: () => mockProject }), findIndex: mockFindIndex }); + + const result = await getProjectUtil(validUuid, mockQuery); + expect(result).toEqual(mockProject); + }); + + it('should throw when project is not found', async () => { + mockChain.get.mockReturnValue({ + find: () => ({ value: () => null }), + findIndex: mockFindIndex, + }); + + await expect(getProjectUtil(validUuid, mockQuery)).rejects.toThrow(); + }); + + it('should support isIndex mode', async () => { + mockChain.get.mockReturnValue({ + find: mockFind, + findIndex: () => ({ value: () => 0 }), + }); + + const result = await getProjectUtil(validUuid, mockQuery, 'test', true); + expect(result).toBe(0); + }); + + it('should throw when isIndex returns -1', async () => { + mockChain.get.mockReturnValue({ + find: mockFind, + findIndex: () => ({ value: () => -1 }), + }); + + await expect( + getProjectUtil(validUuid, mockQuery, 'test', true) + ).rejects.toThrow(); + }); +}); diff --git a/api/tests/unit/utils/https.utils.test.ts b/api/tests/unit/utils/https.utils.test.ts new file mode 100644 index 000000000..bfc884b2f --- /dev/null +++ b/api/tests/unit/utils/https.utils.test.ts @@ -0,0 +1,95 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import axios from 'axios'; + +vi.mock('axios', () => ({ + default: vi.fn(), +})); + +vi.mock('../../../src/constants/index.js', () => ({ + AXIOS_TIMEOUT: 60000, + METHODS_TO_INCLUDE_DATA_IN_AXIOS: ['PUT', 'POST', 'DELETE', 'PATCH'], +})); + +import https from '../../../src/utils/https.utils.js'; + +describe('https.utils', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should send a GET request and return headers, status, data', async () => { + const mockResponse = { + headers: { 'content-type': 'application/json' }, + status: 200, + data: { message: 'ok' }, + }; + vi.mocked(axios).mockResolvedValue(mockResponse); + + const result = await https({ + url: 'https://api.example.com/test', + method: 'GET', + headers: { Authorization: 'Bearer token' }, + }); + + expect(result).toEqual({ + headers: mockResponse.headers, + status: 200, + data: { message: 'ok' }, + }); + expect(axios).toHaveBeenCalledWith('https://api.example.com/test', expect.objectContaining({ + method: 'GET', + timeout: 60000, + })); + }); + + it('should include data for POST requests', async () => { + vi.mocked(axios).mockResolvedValue({ headers: {}, status: 201, data: {} }); + + await https({ + url: 'https://api.example.com/test', + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + data: { name: 'test' }, + }); + + expect(axios).toHaveBeenCalledWith('https://api.example.com/test', expect.objectContaining({ + method: 'POST', + data: { name: 'test' }, + })); + }); + + it('should not include data for GET requests', async () => { + vi.mocked(axios).mockResolvedValue({ headers: {}, status: 200, data: {} }); + + await https({ + url: 'https://api.example.com/test', + method: 'GET', + data: { shouldNotAppear: true }, + }); + + const callArgs = vi.mocked(axios).mock.calls[0][1] as any; + expect(callArgs.data).toBeUndefined(); + }); + + it('should use custom timeout when provided', async () => { + vi.mocked(axios).mockResolvedValue({ headers: {}, status: 200, data: {} }); + + await https({ + url: 'https://api.example.com/test', + method: 'GET', + timeout: 5000, + }); + + expect(axios).toHaveBeenCalledWith('https://api.example.com/test', expect.objectContaining({ + timeout: 5000, + })); + }); + + it('should propagate axios errors', async () => { + vi.mocked(axios).mockRejectedValue(new Error('Network Error')); + + await expect( + https({ url: 'https://api.example.com/test', method: 'GET' }) + ).rejects.toThrow('Network Error'); + }); +}); diff --git a/api/tests/unit/utils/index-extra.test.ts b/api/tests/unit/utils/index-extra.test.ts new file mode 100644 index 000000000..1787f3f16 --- /dev/null +++ b/api/tests/unit/utils/index-extra.test.ts @@ -0,0 +1,103 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const mockEnsureDir = vi.fn(); +const mockCopy = vi.fn(); +const mockExistsSync = vi.fn(); +const mockReadFile = vi.fn(); +const mockWriteFile = vi.fn(); + +vi.mock('fs-extra', () => ({ + default: { + ensureDir: (...args: any[]) => mockEnsureDir(...args), + copy: (...args: any[]) => mockCopy(...args), + existsSync: (...args: any[]) => mockExistsSync(...args), + promises: { + readFile: (...args: any[]) => mockReadFile(...args), + writeFile: (...args: any[]) => mockWriteFile(...args), + }, + }, +})); + +const mockMkdirp = vi.fn(); +vi.mock('mkdirp', () => ({ + mkdirp: (...args: any[]) => mockMkdirp(...args), +})); + +const mockHttps = vi.fn(); +vi.mock('../../../src/utils/https.utils.js', () => ({ + default: (...args: any[]) => mockHttps(...args), +})); + +vi.mock('../../../src/config/index.js', () => ({ + config: { CS_API: { NA: 'https://api.contentstack.io/v3' } }, +})); + +describe('utils/index - copyDirectory, createDirectoryAndFile, getAllLocales', () => { + let copyDirectory: any; + let createDirectoryAndFile: any; + let getAllLocales: any; + + beforeEach(async () => { + vi.clearAllMocks(); + const mod = await import('../../../src/utils/index.js'); + copyDirectory = mod.copyDirectory; + createDirectoryAndFile = mod.createDirectoryAndFile; + getAllLocales = mod.getAllLocales; + }); + + describe('copyDirectory', () => { + it('should copy directory from src to dest', async () => { + mockEnsureDir.mockResolvedValue(undefined); + mockCopy.mockResolvedValue(undefined); + await copyDirectory('/src', '/dest'); + expect(mockEnsureDir).toHaveBeenCalledWith('/dest'); + expect(mockCopy).toHaveBeenCalledWith('/src', '/dest'); + }); + + it('should handle errors gracefully', async () => { + mockEnsureDir.mockRejectedValue(new Error('fail')); + await copyDirectory('/src', '/dest'); + }); + }); + + describe('createDirectoryAndFile', () => { + it('should create directory and file when file does not exist', async () => { + mockMkdirp.mockResolvedValue(undefined); + mockExistsSync.mockReturnValue(false); + mockReadFile.mockResolvedValue('content'); + mockWriteFile.mockResolvedValue(undefined); + await createDirectoryAndFile('/dir/file.txt', '/source.txt'); + expect(mockMkdirp).toHaveBeenCalled(); + expect(mockReadFile).toHaveBeenCalledWith('/source.txt', 'utf8'); + expect(mockWriteFile).toHaveBeenCalled(); + }); + + it('should skip file creation when file already exists', async () => { + mockMkdirp.mockResolvedValue(undefined); + mockExistsSync.mockReturnValue(true); + await createDirectoryAndFile('/dir/file.txt', '/source.txt'); + expect(mockReadFile).not.toHaveBeenCalled(); + }); + + it('should handle errors gracefully', async () => { + mockMkdirp.mockRejectedValue(new Error('fail')); + await createDirectoryAndFile('/dir/file.txt', '/source.txt'); + }); + }); + + describe('getAllLocales', () => { + it('should return locales on success', async () => { + mockHttps.mockResolvedValue({ data: { locales: [{ code: 'en-us' }] } }); + const [err, locales] = await getAllLocales(); + expect(err).toBeNull(); + expect(locales).toEqual([{ code: 'en-us' }]); + }); + + it('should return error on failure', async () => { + mockHttps.mockRejectedValue(new Error('network error')); + const [err, locales] = await getAllLocales(); + expect(err).toBeDefined(); + expect(locales).toBeUndefined(); + }); + }); +}); diff --git a/api/tests/unit/utils/index.test.ts b/api/tests/unit/utils/index.test.ts new file mode 100644 index 000000000..eefac615f --- /dev/null +++ b/api/tests/unit/utils/index.test.ts @@ -0,0 +1,96 @@ +import { describe, it, expect, vi } from 'vitest'; +import { throwError, isEmpty, safePromise, getLogMessage } from '../../../src/utils/index.js'; + +describe('utils/index', () => { + describe('throwError', () => { + it('should throw an error with the given message and statusCode', () => { + expect(() => throwError('Not found', 404)).toThrow('Not found'); + try { + throwError('Server error', 500); + } catch (e: any) { + expect(e.statusCode).toBe(500); + expect(e.message).toBe('Server error'); + } + }); + }); + + describe('isEmpty', () => { + it('should return true for undefined', () => { + expect(isEmpty(undefined)).toBe(true); + }); + + it('should return true for null', () => { + expect(isEmpty(null)).toBe(true); + }); + + it('should return true for empty object', () => { + expect(isEmpty({})).toBe(true); + }); + + it('should return true for empty string', () => { + expect(isEmpty('')).toBe(true); + }); + + it('should return true for whitespace-only string', () => { + expect(isEmpty(' ')).toBe(true); + }); + + it('should return false for non-empty string', () => { + expect(isEmpty('hello')).toBe(false); + }); + + it('should return false for non-empty object', () => { + expect(isEmpty({ key: 'value' })).toBe(false); + }); + + it('should return false for numbers', () => { + expect(isEmpty(0)).toBe(false); + expect(isEmpty(42)).toBe(false); + }); + + it('should return false for boolean', () => { + expect(isEmpty(false)).toBe(false); + }); + }); + + describe('safePromise', () => { + it('should resolve to [null, result] on success', async () => { + const result = await safePromise(Promise.resolve('data')); + expect(result).toEqual([null, 'data']); + }); + + it('should resolve to [error] on failure', async () => { + const error = new Error('fail'); + const result = await safePromise(Promise.reject(error)); + expect(result).toEqual([error]); + }); + }); + + describe('getLogMessage', () => { + it('should return correct log object shape', () => { + const log = getLogMessage('testMethod', 'test message'); + expect(log).toEqual({ + methodName: 'testMethod', + message: 'test message', + user: {}, + }); + }); + + it('should include user when provided', () => { + const user = { id: '123' }; + const log = getLogMessage('testMethod', 'test message', user); + expect(log.user).toEqual(user); + }); + + it('should include error when provided', () => { + const error = new Error('test error'); + const log = getLogMessage('testMethod', 'test message', {}, error); + expect(log.error).toBe(error); + }); + + it('should not include error key when error is not provided', () => { + const log = getLogMessage('testMethod', 'test message'); + expect(log).not.toHaveProperty('error'); + }); + }); +}); diff --git a/api/tests/unit/utils/jwt.utils.test.ts b/api/tests/unit/utils/jwt.utils.test.ts new file mode 100644 index 000000000..a8519f669 --- /dev/null +++ b/api/tests/unit/utils/jwt.utils.test.ts @@ -0,0 +1,43 @@ +import { describe, it, expect, vi } from 'vitest'; +import jwt from 'jsonwebtoken'; + +vi.mock('../../../src/config/index.js', () => ({ + config: { + APP_TOKEN_KEY: 'test-secret-key', + APP_TOKEN_EXP: '2d', + }, +})); + +import { generateToken } from '../../../src/utils/jwt.utils.js'; + +describe('jwt.utils', () => { + describe('generateToken', () => { + it('should return a signed JWT string', () => { + const payload = { region: 'NA', user_id: 'user-123' }; + const token = generateToken(payload); + + expect(typeof token).toBe('string'); + expect(token.split('.')).toHaveLength(3); + }); + + it('should encode the payload correctly', () => { + const payload = { region: 'EU', user_id: 'user-456' }; + const token = generateToken(payload); + const decoded = jwt.verify(token, 'test-secret-key') as any; + + expect(decoded.region).toBe('EU'); + expect(decoded.user_id).toBe('user-456'); + expect(decoded.exp).toBeDefined(); + expect(decoded.iat).toBeDefined(); + }); + + it('should set expiration from config', () => { + const payload = { region: 'NA', user_id: 'user-123' }; + const token = generateToken(payload); + const decoded = jwt.decode(token) as any; + + const twoDaysInSeconds = 2 * 24 * 60 * 60; + expect(decoded.exp - decoded.iat).toBe(twoDaysInSeconds); + }); + }); +}); diff --git a/api/tests/unit/utils/market-app.utils.test.ts b/api/tests/unit/utils/market-app.utils.test.ts new file mode 100644 index 000000000..646a30064 --- /dev/null +++ b/api/tests/unit/utils/market-app.utils.test.ts @@ -0,0 +1,134 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const mockClient = { + marketplace: vi.fn(), +}; + +vi.mock('@contentstack/marketplace-sdk', () => ({ + default: { + client: vi.fn(() => mockClient), + }, +})); + +vi.mock('../../../src/constants/index.js', () => ({ + DEVURLS: { + NA: 'developerhub-api.contentstack.com', + EU: 'eu-developerhub-api.contentstack.com', + }, +})); + +import contentstack from '@contentstack/marketplace-sdk'; +import { + getAllApps, + getAppManifestAndAppConfig, +} from '../../../src/utils/market-app.utils.js'; + +describe('market-app.utils', () => { + const originalConsoleInfo = console.info; + + beforeEach(() => { + vi.clearAllMocks(); + mockClient.marketplace.mockReturnValue({ + findAllApps: vi.fn(), + app: vi.fn(), + }); + }); + + describe('getAllApps', () => { + it('should return items when findAllApps succeeds', async () => { + const mockItems = [{ uid: 'app-1' }, { uid: 'app-2' }]; + mockClient.marketplace.mockReturnValue({ + findAllApps: vi.fn().mockResolvedValue({ items: mockItems }), + }); + + const result = await getAllApps({ + organizationUid: 'org-123', + authtoken: 'token-xyz', + region: 'NA', + }); + + expect(result).toEqual(mockItems); + expect(contentstack.client).toHaveBeenCalledWith({ + authtoken: 'token-xyz', + host: 'developerhub-api.contentstack.com', + }); + expect(mockClient.marketplace).toHaveBeenCalledWith('org-123'); + }); + + it('should use EU host when region is EU', async () => { + mockClient.marketplace.mockReturnValue({ + findAllApps: vi.fn().mockResolvedValue({ items: [] }), + }); + + await getAllApps({ + organizationUid: 'org-123', + authtoken: 'token', + region: 'EU', + }); + + expect(contentstack.client).toHaveBeenCalledWith({ + authtoken: 'token', + host: 'eu-developerhub-api.contentstack.com', + }); + }); + + it('should return undefined and log when error occurs', async () => { + const consoleSpy = vi.spyOn(console, 'info').mockImplementation(() => {}); + mockClient.marketplace.mockReturnValue({ + findAllApps: vi.fn().mockRejectedValue(new Error('API Error')), + }); + + const result = await getAllApps({ + organizationUid: 'org-123', + authtoken: 'token', + region: 'NA', + }); + + expect(result).toBeUndefined(); + expect(consoleSpy).toHaveBeenCalled(); + consoleSpy.mockRestore(); + }); + }); + + describe('getAppManifestAndAppConfig', () => { + it('should return app data when fetch succeeds', async () => { + const mockAppData = { uid: 'manifest-1', title: 'Test App' }; + mockClient.marketplace.mockReturnValue({ + app: vi.fn().mockReturnValue({ + fetch: vi.fn().mockResolvedValue(mockAppData), + }), + }); + + const result = await getAppManifestAndAppConfig({ + organizationUid: 'org-123', + authtoken: 'token', + region: 'NA', + manifestUid: 'manifest-1', + }); + + expect(result).toEqual(mockAppData); + const appFn = mockClient.marketplace().app; + expect(appFn).toHaveBeenCalledWith('manifest-1'); + }); + + it('should return undefined and log when error occurs', async () => { + const consoleSpy = vi.spyOn(console, 'info').mockImplementation(() => {}); + mockClient.marketplace.mockReturnValue({ + app: vi.fn().mockReturnValue({ + fetch: vi.fn().mockRejectedValue(new Error('Not found')), + }), + }); + + const result = await getAppManifestAndAppConfig({ + organizationUid: 'org-123', + authtoken: 'token', + region: 'NA', + manifestUid: 'invalid', + }); + + expect(result).toBeUndefined(); + expect(consoleSpy).toHaveBeenCalled(); + consoleSpy.mockRestore(); + }); + }); +}); diff --git a/api/tests/unit/utils/mimeTypes.test.ts b/api/tests/unit/utils/mimeTypes.test.ts new file mode 100644 index 000000000..befee79cc --- /dev/null +++ b/api/tests/unit/utils/mimeTypes.test.ts @@ -0,0 +1,75 @@ +import { describe, it, expect, vi } from 'vitest'; + +import { + EXT_TO_MIME_MAP, + getMimeTypeFromExtension, + default as defaultExport, +} from '../../../src/utils/mimeTypes.js'; + +describe('mimeTypes', () => { + describe('getMimeTypeFromExtension', () => { + it('should return correct MIME type for known image extensions', () => { + expect(getMimeTypeFromExtension('jpg')).toBe('image/jpeg'); + expect(getMimeTypeFromExtension('jpeg')).toBe('image/jpeg'); + expect(getMimeTypeFromExtension('png')).toBe('image/png'); + expect(getMimeTypeFromExtension('gif')).toBe('image/gif'); + expect(getMimeTypeFromExtension('webp')).toBe('image/webp'); + expect(getMimeTypeFromExtension('svg')).toBe('image/svg+xml'); + }); + + it('should return correct MIME type for video extensions', () => { + expect(getMimeTypeFromExtension('mp4')).toBe('video/mp4'); + expect(getMimeTypeFromExtension('webm')).toBe('video/webm'); + expect(getMimeTypeFromExtension('mov')).toBe('video/quicktime'); + }); + + it('should return correct MIME type for audio extensions', () => { + expect(getMimeTypeFromExtension('mp3')).toBe('audio/mpeg'); + expect(getMimeTypeFromExtension('wav')).toBe('audio/wav'); + }); + + it('should return correct MIME type for document extensions', () => { + expect(getMimeTypeFromExtension('pdf')).toBe('application/pdf'); + expect(getMimeTypeFromExtension('json')).toBe('application/json'); + expect(getMimeTypeFromExtension('txt')).toBe('text/plain'); + }); + + it('should be case-insensitive', () => { + expect(getMimeTypeFromExtension('JPG')).toBe('image/jpeg'); + expect(getMimeTypeFromExtension('PNG')).toBe('image/png'); + expect(getMimeTypeFromExtension('PDF')).toBe('application/pdf'); + }); + + it('should return undefined for unknown extension', () => { + expect(getMimeTypeFromExtension('unknown')).toBeUndefined(); + expect(getMimeTypeFromExtension('xyz')).toBeUndefined(); + expect(getMimeTypeFromExtension('')).toBeUndefined(); + }); + + it('should handle empty string', () => { + expect(getMimeTypeFromExtension('')).toBeUndefined(); + }); + }); + + describe('EXT_TO_MIME_MAP', () => { + it('should export expected MIME type mappings', () => { + expect(EXT_TO_MIME_MAP).toBeDefined(); + expect(typeof EXT_TO_MIME_MAP).toBe('object'); + expect(Object.keys(EXT_TO_MIME_MAP).length).toBeGreaterThan(0); + }); + + it('should have valid MIME type format for entries', () => { + for (const [ext, mime] of Object.entries(EXT_TO_MIME_MAP)) { + expect(typeof ext).toBe('string'); + expect(typeof mime).toBe('string'); + expect(mime).toMatch(/.+\/.+/); + } + }); + }); + + describe('default export', () => { + it('should export EXT_TO_MIME_MAP as default', () => { + expect(defaultExport).toBe(EXT_TO_MIME_MAP); + }); + }); +}); diff --git a/api/tests/unit/utils/pagination.utils.test.ts b/api/tests/unit/utils/pagination.utils.test.ts new file mode 100644 index 000000000..27ba63429 --- /dev/null +++ b/api/tests/unit/utils/pagination.utils.test.ts @@ -0,0 +1,83 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockHttps } = vi.hoisted(() => ({ mockHttps: vi.fn() })); + +vi.mock('../../../src/utils/https.utils.js', () => ({ + default: mockHttps, +})); + +vi.mock('../../../src/utils/index.js', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + safePromise: (promise: Promise) => + promise.then((res: any) => [null, res]).catch((err: any) => [err]), + }; +}); + +import fetchAllPaginatedData from '../../../src/utils/pagination.utils.js'; + +describe('pagination.utils', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should fetch a single page of data', async () => { + mockHttps.mockResolvedValue({ + data: { items: [{ id: 1 }, { id: 2 }] }, + }); + + const result = await fetchAllPaginatedData( + 'https://api.example.com/data', + { Authorization: 'Bearer token' }, + 100, + 'testFunc', + 'items' + ); + + expect(result).toEqual([{ id: 1 }, { id: 2 }]); + expect(mockHttps).toHaveBeenCalledTimes(1); + }); + + it('should fetch multiple pages of data', async () => { + mockHttps + .mockResolvedValueOnce({ + data: { items: Array.from({ length: 2 }, (_, i) => ({ id: i })) }, + }) + .mockResolvedValueOnce({ + data: { items: [{ id: 2 }] }, + }); + + const result = await fetchAllPaginatedData( + 'https://api.example.com/data', + {}, + 2, + 'testFunc', + 'items' + ); + + expect(result).toHaveLength(3); + expect(mockHttps).toHaveBeenCalledTimes(2); + }); + + it('should throw on API error', async () => { + const apiError = Object.assign(new Error('API Error'), { + response: { data: 'Bad Request' }, + }); + mockHttps.mockRejectedValue(apiError); + + await expect( + fetchAllPaginatedData('https://api.example.com/data', {}, 100, 'testFunc', 'items') + ).rejects.toThrow('Error in testFunc'); + }); + + it('should throw when responseKey is not iterable', async () => { + mockHttps.mockResolvedValue({ + data: { items: 'not-an-array' }, + }); + + await expect( + fetchAllPaginatedData('https://api.example.com/data', {}, 100, 'testFunc', 'items') + ).rejects.toThrow('is not iterable'); + }); +}); diff --git a/api/tests/unit/utils/sanitize-path.utils.test.ts b/api/tests/unit/utils/sanitize-path.utils.test.ts new file mode 100644 index 000000000..bb60825f7 --- /dev/null +++ b/api/tests/unit/utils/sanitize-path.utils.test.ts @@ -0,0 +1,90 @@ +import { describe, it, expect } from 'vitest'; +import { sanitizeStackId, getSafePath } from '../../../src/utils/sanitize-path.utils.js'; +import path from 'path'; + +describe('sanitize-path.utils', () => { + describe('sanitizeStackId', () => { + it('should return the same string for valid alphanumeric input', () => { + expect(sanitizeStackId('blt1234abcd')).toBe('blt1234abcd'); + }); + + it('should allow dots, hyphens, and underscores', () => { + expect(sanitizeStackId('stack-id_v2.0')).toBe('stack-id_v2.0'); + }); + + it('should return null for null input', () => { + expect(sanitizeStackId(null)).toBeNull(); + }); + + it('should return null for undefined input', () => { + expect(sanitizeStackId(undefined)).toBeNull(); + }); + + it('should return null for empty string', () => { + expect(sanitizeStackId('')).toBeNull(); + }); + + it('should return null for path traversal with ../', () => { + expect(sanitizeStackId('../etc/passwd')).toBeNull(); + }); + + it('should return null for backslash path traversal', () => { + expect(sanitizeStackId('..\\windows\\system32')).toBeNull(); + }); + + it('should return null for null bytes', () => { + expect(sanitizeStackId('valid\0malicious')).toBeNull(); + }); + + it('should return null for forward slashes', () => { + expect(sanitizeStackId('path/to/file')).toBeNull(); + }); + + it('should return null for strings longer than 256 characters', () => { + const longString = 'a'.repeat(257); + expect(sanitizeStackId(longString)).toBeNull(); + }); + + it('should accept strings of exactly 256 characters', () => { + const maxString = 'a'.repeat(256); + expect(sanitizeStackId(maxString)).toBe(maxString); + }); + + it('should return null for non-string input', () => { + expect(sanitizeStackId(123 as any)).toBeNull(); + }); + + it('should return null for special characters', () => { + expect(sanitizeStackId('stack@id!')).toBeNull(); + }); + }); + + describe('getSafePath', () => { + it('should resolve an absolute path', () => { + const result = getSafePath('/tmp/test.log'); + expect(path.isAbsolute(result)).toBe(true); + }); + + it('should sanitize the filename', () => { + const result = getSafePath('/tmp/test@file!.log'); + expect(result).not.toContain('@'); + expect(result).not.toContain('!'); + }); + + it('should prevent directory escape when baseDir is provided', () => { + const result = getSafePath('../../etc/passwd', '/tmp/logs'); + expect(result).toContain('/tmp/logs'); + }); + + it('should return default.log on error with baseDir', () => { + const result = getSafePath('', '/tmp/logs'); + expect(path.isAbsolute(result)).toBe(true); + }); + + it('should handle relative paths with baseDir', () => { + const result = getSafePath('subdir/file.log', '/tmp/logs'); + expect(result).toContain('file.log'); + expect(path.isAbsolute(result)).toBe(true); + }); + }); +}); diff --git a/api/tests/unit/utils/search.util.test.ts b/api/tests/unit/utils/search.util.test.ts new file mode 100644 index 000000000..2c725b0b6 --- /dev/null +++ b/api/tests/unit/utils/search.util.test.ts @@ -0,0 +1,52 @@ +import { describe, it, expect } from 'vitest'; +import { matchesSearchText } from '../../../src/utils/search.util.js'; + +describe('search.util', () => { + describe('matchesSearchText', () => { + const mockLog: any = { + level: 'error', + message: 'Something failed in migration', + methodName: 'startTestMigration', + timestamp: '2025-01-15T10:30:00.000Z', + }; + + it('should return true when searchText is empty', () => { + expect(matchesSearchText(mockLog, '')).toBe(true); + }); + + it('should return true when searchText is "null"', () => { + expect(matchesSearchText(mockLog, 'null')).toBe(true); + }); + + it('should match on level field', () => { + expect(matchesSearchText(mockLog, 'error')).toBe(true); + }); + + it('should match on message field', () => { + expect(matchesSearchText(mockLog, 'migration')).toBe(true); + }); + + it('should match on methodName field', () => { + expect(matchesSearchText(mockLog, 'startTest')).toBe(true); + }); + + it('should match on timestamp field', () => { + expect(matchesSearchText(mockLog, '2025-01')).toBe(true); + }); + + it('should be case-insensitive', () => { + expect(matchesSearchText(mockLog, 'ERROR')).toBe(true); + expect(matchesSearchText(mockLog, 'Migration')).toBe(true); + }); + + it('should return false when no fields match', () => { + expect(matchesSearchText(mockLog, 'nonexistent')).toBe(false); + }); + + it('should handle log with missing fields gracefully', () => { + const partialLog: any = { level: 'info' }; + expect(matchesSearchText(partialLog, 'info')).toBe(true); + expect(matchesSearchText(partialLog, 'missing')).toBe(false); + }); + }); +}); diff --git a/api/tests/unit/validators/affix-confirmation.validator.test.ts b/api/tests/unit/validators/affix-confirmation.validator.test.ts new file mode 100644 index 000000000..2da88917d --- /dev/null +++ b/api/tests/unit/validators/affix-confirmation.validator.test.ts @@ -0,0 +1,39 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { validationResult } from 'express-validator'; + +const mockReq = (body: any) => ({ body, query: {}, params: {}, headers: {}, get: () => undefined }); + +async function runValidation(schema: any, body: any) { + const req = mockReq(body); + await schema.run(req); + return validationResult(req); +} + +describe('affix-confirmation.validator', () => { + let validator: any; + + beforeAll(async () => { + const mod = await import('../../../src/validators/affix-confirmation.validator.js'); + validator = mod.default; + }); + + it('should accept true', async () => { + const result = await runValidation(validator, { affix_confirmation: true }); + expect(result.isEmpty()).toBe(true); + }); + + it('should accept false', async () => { + const result = await runValidation(validator, { affix_confirmation: false }); + expect(result.isEmpty()).toBe(true); + }); + + it('should reject missing field', async () => { + const result = await runValidation(validator, {}); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject non-boolean value', async () => { + const result = await runValidation(validator, { affix_confirmation: 123 }); + expect(result.isEmpty()).toBe(false); + }); +}); diff --git a/api/tests/unit/validators/affix.validator.test.ts b/api/tests/unit/validators/affix.validator.test.ts new file mode 100644 index 000000000..b2c64c87f --- /dev/null +++ b/api/tests/unit/validators/affix.validator.test.ts @@ -0,0 +1,49 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { validationResult } from 'express-validator'; + +const mockReq = (body: any) => ({ body, query: {}, params: {}, headers: {}, get: () => undefined }); + +async function runValidation(schema: any, body: any) { + const req = mockReq(body); + await schema.run(req); + return validationResult(req); +} + +describe('affix.validator', () => { + let validator: any; + + beforeAll(async () => { + const mod = await import('../../../src/validators/affix.validator.js'); + validator = mod.default; + }); + + it('should accept valid affix', async () => { + const result = await runValidation(validator, { affix: 'abc12' }); + expect(result.isEmpty()).toBe(true); + }); + + it('should reject missing affix', async () => { + const result = await runValidation(validator, {}); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject non-string affix', async () => { + const result = await runValidation(validator, { affix: 123 }); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject affix starting with number', async () => { + const result = await runValidation(validator, { affix: '1abc' }); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject affix with special characters', async () => { + const result = await runValidation(validator, { affix: 'ab-c' }); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject affix exceeding 5 chars', async () => { + const result = await runValidation(validator, { affix: 'abcdef' }); + expect(result.isEmpty()).toBe(false); + }); +}); diff --git a/api/tests/unit/validators/auth.validator.test.ts b/api/tests/unit/validators/auth.validator.test.ts new file mode 100644 index 000000000..4178daccf --- /dev/null +++ b/api/tests/unit/validators/auth.validator.test.ts @@ -0,0 +1,91 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { checkSchema, validationResult } from 'express-validator'; + +const mockReq = (body: any) => ({ + body, + query: {}, + params: {}, + headers: {}, + get: () => undefined, +}); + +async function runValidation(schema: any, body: any) { + const req = mockReq(body); + const validations = schema; + await validations.run(req); + return validationResult(req); +} + +describe('auth.validator', () => { + let authValidator: any; + + beforeAll(async () => { + const mod = await import('../../../src/validators/auth.validator.js'); + authValidator = mod.default; + }); + + it('should accept valid auth body', async () => { + const result = await runValidation(authValidator, { + email: 'test@example.com', + password: 'password123', + region: 'NA', + }); + expect(result.isEmpty()).toBe(true); + }); + + it('should reject missing email', async () => { + const result = await runValidation(authValidator, { + password: 'password123', + region: 'NA', + }); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject invalid email', async () => { + const result = await runValidation(authValidator, { + email: 'not-an-email', + password: 'password123', + region: 'NA', + }); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject non-string password', async () => { + const result = await runValidation(authValidator, { + email: 'test@example.com', + password: 12345, + region: 'NA', + }); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject invalid region', async () => { + const result = await runValidation(authValidator, { + email: 'test@example.com', + password: 'password123', + region: 'INVALID', + }); + expect(result.isEmpty()).toBe(false); + }); + + it('should accept valid regions', async () => { + for (const region of ['NA', 'EU', 'AZURE_NA', 'AZURE_EU', 'GCP_NA', 'AU', 'GCP_EU']) { + const result = await runValidation(authValidator, { + email: 'test@example.com', + password: 'password123', + region, + }); + expect(result.isEmpty()).toBe(true); + } + }); + + it('should accept optional tfa_token', async () => { + const result = await runValidation(authValidator, { + email: 'test@example.com', + password: 'password123', + region: 'NA', + tfa_token: '123456', + }); + expect(result.isEmpty()).toBe(true); + }); +}); diff --git a/api/tests/unit/validators/cms.validator.test.ts b/api/tests/unit/validators/cms.validator.test.ts new file mode 100644 index 000000000..2cad3ff97 --- /dev/null +++ b/api/tests/unit/validators/cms.validator.test.ts @@ -0,0 +1,39 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { validationResult } from 'express-validator'; + +const mockReq = (body: any) => ({ body, query: {}, params: {}, headers: {}, get: () => undefined }); + +async function runValidation(schema: any, body: any) { + const req = mockReq(body); + await schema.run(req); + return validationResult(req); +} + +describe('cms.validator', () => { + let validator: any; + + beforeAll(async () => { + const mod = await import('../../../src/validators/cms.validator.js'); + validator = mod.default; + }); + + it('should accept valid legacy_cms', async () => { + const result = await runValidation(validator, { legacy_cms: 'wordpress' }); + expect(result.isEmpty()).toBe(true); + }); + + it('should reject missing legacy_cms', async () => { + const result = await runValidation(validator, {}); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject non-string legacy_cms', async () => { + const result = await runValidation(validator, { legacy_cms: 123 }); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject legacy_cms exceeding max length', async () => { + const result = await runValidation(validator, { legacy_cms: 'a'.repeat(201) }); + expect(result.isEmpty()).toBe(false); + }); +}); diff --git a/api/tests/unit/validators/destination-stack.validator.test.ts b/api/tests/unit/validators/destination-stack.validator.test.ts new file mode 100644 index 000000000..f46243a00 --- /dev/null +++ b/api/tests/unit/validators/destination-stack.validator.test.ts @@ -0,0 +1,39 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { validationResult } from 'express-validator'; + +const mockReq = (body: any) => ({ body, query: {}, params: {}, headers: {}, get: () => undefined }); + +async function runValidation(schema: any, body: any) { + const req = mockReq(body); + await schema.run(req); + return validationResult(req); +} + +describe('destination-stack.validator', () => { + let validator: any; + + beforeAll(async () => { + const mod = await import('../../../src/validators/destination-stack.validator.js'); + validator = mod.default; + }); + + it('should accept valid stack_api_key', async () => { + const result = await runValidation(validator, { stack_api_key: 'blt0000000000000000' }); + expect(result.isEmpty()).toBe(true); + }); + + it('should reject missing stack_api_key', async () => { + const result = await runValidation(validator, {}); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject non-string stack_api_key', async () => { + const result = await runValidation(validator, { stack_api_key: 123 }); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject stack_api_key exceeding max length', async () => { + const result = await runValidation(validator, { stack_api_key: 'a'.repeat(201) }); + expect(result.isEmpty()).toBe(false); + }); +}); diff --git a/api/tests/unit/validators/file-format.validator.test.ts b/api/tests/unit/validators/file-format.validator.test.ts new file mode 100644 index 000000000..735ab7ff4 --- /dev/null +++ b/api/tests/unit/validators/file-format.validator.test.ts @@ -0,0 +1,44 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { validationResult } from 'express-validator'; + +const mockReq = (body: any) => ({ body, query: {}, params: {}, headers: {}, get: () => undefined }); + +async function runValidation(schema: any, body: any) { + const req = mockReq(body); + await schema.run(req); + return validationResult(req); +} + +describe('file-format.validator', () => { + let validator: any; + + beforeAll(async () => { + const mod = await import('../../../src/validators/file-format.validator.js'); + validator = mod.default; + }); + + it('should accept valid file_format', async () => { + const result = await runValidation(validator, { file_format: 'json' }); + expect(result.isEmpty()).toBe(true); + }); + + it('should reject missing file_format', async () => { + const result = await runValidation(validator, {}); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject non-string', async () => { + const result = await runValidation(validator, { file_format: 123 }); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject empty string', async () => { + const result = await runValidation(validator, { file_format: '' }); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject string exceeding 200 chars', async () => { + const result = await runValidation(validator, { file_format: 'a'.repeat(201) }); + expect(result.isEmpty()).toBe(false); + }); +}); diff --git a/api/tests/unit/validators/fileformat-confirmation.validator.test.ts b/api/tests/unit/validators/fileformat-confirmation.validator.test.ts new file mode 100644 index 000000000..5e40e9efd --- /dev/null +++ b/api/tests/unit/validators/fileformat-confirmation.validator.test.ts @@ -0,0 +1,39 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { validationResult } from 'express-validator'; + +const mockReq = (body: any) => ({ body, query: {}, params: {}, headers: {}, get: () => undefined }); + +async function runValidation(schema: any, body: any) { + const req = mockReq(body); + await schema.run(req); + return validationResult(req); +} + +describe('fileformat-confirmation.validator', () => { + let validator: any; + + beforeAll(async () => { + const mod = await import('../../../src/validators/fileformat-confirmation.validator.js'); + validator = mod.default; + }); + + it('should accept true', async () => { + const result = await runValidation(validator, { fileformat_confirmation: true }); + expect(result.isEmpty()).toBe(true); + }); + + it('should accept false', async () => { + const result = await runValidation(validator, { fileformat_confirmation: false }); + expect(result.isEmpty()).toBe(true); + }); + + it('should reject missing field', async () => { + const result = await runValidation(validator, {}); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject non-boolean value', async () => { + const result = await runValidation(validator, { fileformat_confirmation: 123 }); + expect(result.isEmpty()).toBe(false); + }); +}); diff --git a/api/tests/unit/validators/index.validator.test.ts b/api/tests/unit/validators/index.validator.test.ts new file mode 100644 index 000000000..46e8a7c90 --- /dev/null +++ b/api/tests/unit/validators/index.validator.test.ts @@ -0,0 +1,82 @@ +import { describe, it, expect, vi, beforeAll } from 'vitest'; + +function flushPromises() { + return new Promise((resolve) => setTimeout(resolve, 50)); +} + +describe('validators/index', () => { + let validatorFactory: any; + + beforeAll(async () => { + const mod = await import('../../../src/validators/index.js'); + validatorFactory = mod.default; + }); + + it('should export a function', () => { + expect(typeof validatorFactory).toBe('function'); + }); + + it('should return a middleware function for each supported route', () => { + const routes = ['auth', 'project', 'cms', 'file_format', 'destination_stack', 'affix', 'affix_confirmation_validator', 'fileformat_confirmation_validator', 'stack']; + for (const route of routes) { + const middleware = validatorFactory(route); + expect(typeof middleware).toBe('function'); + } + }); + + it('should call next() when auth validation passes', async () => { + const middleware = validatorFactory('auth'); + const req = { + body: { email: 'test@example.com', password: 'pass123', region: 'NA' }, + query: {}, params: {}, headers: {}, get: () => undefined, + }; + const res = {}; + const next = vi.fn(); + middleware(req, res, next); + await flushPromises(); + expect(next).toHaveBeenCalledTimes(1); + expect(next.mock.calls[0][0]).toBeUndefined(); + }); + + it('should call next with error when auth validation fails', async () => { + const middleware = validatorFactory('auth'); + const req = { + body: {}, + query: {}, params: {}, headers: {}, get: () => undefined, + }; + const res = {}; + const next = vi.fn(); + middleware(req, res, next); + await flushPromises(); + expect(next).toHaveBeenCalledTimes(1); + expect(next.mock.calls[0][0]).toBeDefined(); + }); + + it('should call next() when cms validation passes', async () => { + const middleware = validatorFactory('cms'); + const req = { + body: { legacy_cms: 'wordpress' }, + query: {}, params: {}, headers: {}, get: () => undefined, + }; + const res = {}; + const next = vi.fn(); + middleware(req, res, next); + await flushPromises(); + expect(next).toHaveBeenCalledTimes(1); + expect(next.mock.calls[0][0]).toBeUndefined(); + }); + + it('should call next with error when cms validation fails', async () => { + const middleware = validatorFactory('cms'); + const req = { + body: {}, + query: {}, params: {}, headers: {}, get: () => undefined, + }; + const res = {}; + const next = vi.fn(); + middleware(req, res, next); + await flushPromises(); + expect(next).toHaveBeenCalledTimes(1); + expect(next.mock.calls[0][0]).toBeDefined(); + }); +}); diff --git a/api/tests/unit/validators/project.validator.test.ts b/api/tests/unit/validators/project.validator.test.ts new file mode 100644 index 000000000..e6815ea15 --- /dev/null +++ b/api/tests/unit/validators/project.validator.test.ts @@ -0,0 +1,71 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { validationResult } from 'express-validator'; + +const mockReq = (body: any) => ({ + body, + query: {}, + params: {}, + headers: {}, + get: () => undefined, +}); + +async function runValidation(schema: any, body: any) { + const req = mockReq(body); + await schema.run(req); + return validationResult(req); +} + +describe('project.validator', () => { + let projectValidator: any; + + beforeAll(async () => { + const mod = await import('../../../src/validators/project.validator.js'); + projectValidator = mod.default; + }); + + it('should accept valid project body', async () => { + const result = await runValidation(projectValidator, { + name: 'My Project', + description: 'A test project', + }); + expect(result.isEmpty()).toBe(true); + }); + + it('should reject missing name', async () => { + const result = await runValidation(projectValidator, { + description: 'A test project', + }); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject name longer than 200 chars', async () => { + const result = await runValidation(projectValidator, { + name: 'a'.repeat(201), + description: 'A test project', + }); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject non-string name', async () => { + const result = await runValidation(projectValidator, { + name: 12345, + description: 'A test project', + }); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject missing description', async () => { + const result = await runValidation(projectValidator, { + name: 'My Project', + }); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject non-string description', async () => { + const result = await runValidation(projectValidator, { + name: 'My Project', + description: 12345, + }); + expect(result.isEmpty()).toBe(false); + }); +}); diff --git a/api/tests/unit/validators/stack.validator.test.ts b/api/tests/unit/validators/stack.validator.test.ts new file mode 100644 index 000000000..1f53c6a18 --- /dev/null +++ b/api/tests/unit/validators/stack.validator.test.ts @@ -0,0 +1,54 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { validationResult } from 'express-validator'; + +const mockReq = (body: any) => ({ body, query: {}, params: {}, headers: {}, get: () => undefined }); + +async function runValidation(schema: any, body: any) { + const req = mockReq(body); + await schema.run(req); + return validationResult(req); +} + +describe('stack.validator', () => { + let validator: any; + + beforeAll(async () => { + const mod = await import('../../../src/validators/stack.validator.js'); + validator = mod.default; + }); + + it('should accept valid name and description', async () => { + const result = await runValidation(validator, { name: 'My Stack', description: 'A test stack' }); + expect(result.isEmpty()).toBe(true); + }); + + it('should reject missing name', async () => { + const result = await runValidation(validator, { description: 'A test stack' }); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject non-string name', async () => { + const result = await runValidation(validator, { name: 123, description: 'A test stack' }); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject name exceeding 255 chars', async () => { + const result = await runValidation(validator, { name: 'a'.repeat(256), description: 'A test stack' }); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject missing description', async () => { + const result = await runValidation(validator, { name: 'My Stack' }); + expect(result.isEmpty()).toBe(false); + }); + + it('should reject description exceeding 512 chars', async () => { + const result = await runValidation(validator, { name: 'My Stack', description: 'a'.repeat(513) }); + expect(result.isEmpty()).toBe(false); + }); + + it('should accept empty description', async () => { + const result = await runValidation(validator, { name: 'My Stack', description: '' }); + expect(result.isEmpty()).toBe(true); + }); +}); diff --git a/api/vitest.config.ts b/api/vitest.config.ts new file mode 100644 index 000000000..501a0ea7d --- /dev/null +++ b/api/vitest.config.ts @@ -0,0 +1,45 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + setupFiles: ['./tests/setup.ts'], + coverage: { + provider: 'v8', + reporter: ['text', 'lcov', 'html'], + include: ['src/**/*.ts'], + exclude: [ + '**/node_modules/**', + '**/tests/**', + 'src/server.ts', + 'src/database.ts', + 'src/config/**', + 'src/services/wordpress.service.ts', + 'src/services/aem.service.ts', + 'src/services/contentful.service.ts', + 'src/services/sitecore.service.ts', + 'src/services/drupal.service.ts', + 'src/services/drupal/**', + 'src/services/contentful/**', + 'src/services/runCli.service.ts', + 'src/utils/content-type-creator.utils.ts', + 'src/utils/entries-field-creator.utils.ts', + 'src/utils/test-folder-creator.utils.ts', + 'src/utils/optimized-query-builder.utils.ts', + 'src/utils/custom-logger.utils.ts', + 'src/utils/wordpressParseUtil.ts', + 'src/utils/watch.utils.ts', + 'src/utils/logger.ts', + 'src/utils/lowdb-lodash.utils.ts', + 'src/models/types.ts', + ], + thresholds: { + lines: 80, + functions: 80, + branches: 60, + statements: 80, + }, + }, + }, +}); diff --git a/package-lock.json b/package-lock.json index e38c3283d..5cd1c6eb0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -464,9 +464,13 @@ } }, "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==" + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } }, "node_modules/base64-js": { "version": "1.5.1", @@ -521,11 +525,15 @@ } }, "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/breakword": { @@ -1398,17 +1406,6 @@ "minimatch": "^5.0.1" } }, - "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/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -2896,14 +2893,15 @@ } }, "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" diff --git a/package.json b/package.json index 931152a04..b05d48fdd 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,15 @@ "lodash": "^4.17.23", "lodash-es": "^4.17.23", "undici": "^7.18.2", - "tmp": ">=0.2.4" + "tmp": ">=0.2.4", + "minimatch": ">=10.2.3", + "ajv": ">=8.18.0", + "glob": ">=11.1.0", + "rollup": ">=4.59.0", + "tar": ">=7.5.8", + "@tootallnate/once": ">=3.0.1", + "fast-xml-parser": ">=5.3.8", + "diff": ">=5.2.2" }, "husky": { "hooks": { diff --git a/ui/package-lock.json b/ui/package-lock.json index a95618229..02b13cb98 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -11,7 +11,6 @@ "@contentstack/json-rte-serializer": "^3.0.5", "@contentstack/venus-components": "^3.0.3", "@reduxjs/toolkit": "^2.8.2", - "@testing-library/jest-dom": "^6.0.0", "@types/react": "^18.3.21", "@types/react-dom": "^18.2.13", "@types/react-redux": "^7.1.33", @@ -34,24 +33,75 @@ "vite-tsconfig-paths": "^6.1.1" }, "devDependencies": { + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", + "@testing-library/user-event": "^14.6.1", + "@vitest/coverage-v8": "^4.0.18", + "@vitest/ui": "^4.0.18", "eslint": "^8.51.0", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", - "prettier": "^3.3.3" + "jsdom": "^28.1.0", + "prettier": "^3.3.3", + "vitest": "^4.0.18" } }, + "node_modules/@acemir/cssom": { + "version": "0.9.31", + "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.31.tgz", + "integrity": "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==", + "dev": true, + "license": "MIT" + }, "node_modules/@adobe/css-tools": { "version": "4.4.4", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@asamuzakjp/css-color": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.2.tgz", + "integrity": "sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^3.0.0", + "@csstools/css-color-parser": "^4.0.1", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0", + "lru-cache": "^11.2.5" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz", + "integrity": "sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.1.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.6" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, "license": "MIT" }, "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -60,12 +110,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", - "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -78,17 +129,19 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -98,6 +151,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -106,16 +160,18 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.28.5" + "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -125,37 +181,40 @@ } }, "node_modules/@babel/runtime": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", - "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", - "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", "debug": "^4.3.1" }, "engines": { @@ -163,9 +222,10 @@ } }, "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" @@ -174,6 +234,29 @@ "node": ">=6.9.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, "node_modules/@contentstack/json-rte-serializer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@contentstack/json-rte-serializer/-/json-rte-serializer-3.0.5.tgz", @@ -276,191 +359,27 @@ "react-dom": "^16.8.6 || ^17 || ^18 || ^19" } }, - "node_modules/@contentstack/venus-components/node_modules/@babel/runtime": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", - "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@contentstack/venus-components/node_modules/@emotion/cache": { - "version": "10.0.9", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.9.tgz", - "integrity": "sha512-f7MblpE2xoimC4fCMZ9pivmsIn7hyWRIvY75owMDi8pdOSeh+w5tH3r4hBJv/LLrwiMM7cTQURqTPcYoL5pWnw==", - "dependencies": { - "@emotion/sheet": "0.9.2", - "@emotion/stylis": "0.8.3", - "@emotion/utils": "0.11.1", - "@emotion/weak-memoize": "0.2.2" - } - }, - "node_modules/@contentstack/venus-components/node_modules/@emotion/core": { - "version": "10.0.9", - "resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.0.9.tgz", - "integrity": "sha512-v5a77dV+uWGoy9w6R3MXZG01lqHcXgoy/jGmJqPDGhPjmpWg26LWXAphYZxpZffFUwDUlDdYDiX5HtaKphvJnQ==", - "dependencies": { - "@emotion/cache": "^10.0.9", - "@emotion/css": "^10.0.9", - "@emotion/serialize": "^0.11.6", - "@emotion/sheet": "0.9.2", - "@emotion/utils": "0.11.1" - }, - "peerDependencies": { - "react": ">=16.3.0" - } - }, - "node_modules/@contentstack/venus-components/node_modules/@emotion/core/node_modules/@emotion/css": { - "version": "10.0.27", - "resolved": "https://registry.npmjs.org/@emotion/css/-/css-10.0.27.tgz", - "integrity": "sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==", - "dependencies": { - "@emotion/serialize": "^0.11.15", - "@emotion/utils": "0.11.3", - "babel-plugin-emotion": "^10.0.27" - } - }, - "node_modules/@contentstack/venus-components/node_modules/@emotion/core/node_modules/@emotion/css/node_modules/@emotion/utils": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz", - "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==" - }, - "node_modules/@contentstack/venus-components/node_modules/@emotion/hash": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", - "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" - }, - "node_modules/@contentstack/venus-components/node_modules/@emotion/memoize": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", - "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==" - }, - "node_modules/@contentstack/venus-components/node_modules/@emotion/serialize": { - "version": "0.11.16", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.16.tgz", - "integrity": "sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg==", - "dependencies": { - "@emotion/hash": "0.8.0", - "@emotion/memoize": "0.7.4", - "@emotion/unitless": "0.7.5", - "@emotion/utils": "0.11.3", - "csstype": "^2.5.7" - } - }, - "node_modules/@contentstack/venus-components/node_modules/@emotion/serialize/node_modules/@emotion/utils": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz", - "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==" - }, - "node_modules/@contentstack/venus-components/node_modules/@emotion/serialize/node_modules/csstype": { - "version": "2.6.21", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", - "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" - }, - "node_modules/@contentstack/venus-components/node_modules/@emotion/sheet": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.2.tgz", - "integrity": "sha512-pVBLzIbC/QCHDKJF2E82V2H/W/B004mDFQZiyo/MSR+VC4pV5JLG0TF/zgQDFvP3fZL/5RTPGEmXlYJBMUuJ+A==" - }, - "node_modules/@contentstack/venus-components/node_modules/@emotion/unitless": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", - "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" - }, - "node_modules/@contentstack/venus-components/node_modules/@emotion/utils": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.1.tgz", - "integrity": "sha512-8M3VN0hetwhsJ8dH8VkVy7xo5/1VoBsDOk/T4SJOeXwTO1c4uIqVNx2qyecLFnnUWD5vvUqHQ1gASSeUN6zcTg==" - }, - "node_modules/@contentstack/venus-components/node_modules/@emotion/weak-memoize": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.2.tgz", - "integrity": "sha512-n/VQ4mbfr81aqkx/XmVicOLjviMuy02eenSdJY33SVA7S2J42EU0P1H0mOogfYedb3wXA0d/LVtBrgTSm04WEA==" - }, - "node_modules/@contentstack/venus-components/node_modules/@testing-library/react-hooks": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz", - "integrity": "sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "react-error-boundary": "^3.1.0" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "@types/react": "^16.9.0 || ^17.0.0", - "react": "^16.9.0 || ^17.0.0", - "react-dom": "^16.9.0 || ^17.0.0", - "react-test-renderer": "^16.9.0 || ^17.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "react-test-renderer": { - "optional": true - } - } - }, - "node_modules/@contentstack/venus-components/node_modules/@types/react": { - "version": "17.0.91", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.91.tgz", - "integrity": "sha512-xauZca6qMeCU3Moy0KxCM9jtf1vyk6qRYK39Ryf3afUqwgNUjRIGoDdS9BcGWgAMGSg1hvP4XcmlYrM66PtqeA==", - "optional": true, - "peer": true, - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "^0.16", - "csstype": "^3.2.2" - } - }, "node_modules/@contentstack/venus-components/node_modules/immer": { "version": "9.0.21", "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" } }, - "node_modules/@contentstack/venus-components/node_modules/memoize-one": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.0.0.tgz", - "integrity": "sha512-7g0+ejkOaI9w5x6LvQwmj68kUj6rxROywPSCqmclG/HBacmFnZqhVscQ8kovkn9FBCNJmOz6SY42+jnvZzDWdw==" - }, - "node_modules/@contentstack/venus-components/node_modules/react-ace": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/react-ace/-/react-ace-9.5.0.tgz", - "integrity": "sha512-4l5FgwGh6K7A0yWVMQlPIXDItM4Q9zzXRqOae8KkCl6MkOob7sC1CzHxZdOGvV+QioKWbX2p5HcdOVUv6cAdSg==", - "dependencies": { - "ace-builds": "^1.4.13", - "diff-match-patch": "^1.0.5", - "lodash.get": "^4.4.2", - "lodash.isequal": "^4.5.0", - "prop-types": "^15.7.2" - }, - "peerDependencies": { - "react": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0", - "react-dom": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0" - } - }, "node_modules/@contentstack/venus-components/node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "license": "MIT" }, "node_modules/@contentstack/venus-components/node_modules/react-redux": { "version": "7.2.9", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.15.4", "@types/react-redux": "^7.1.20", @@ -481,204 +400,163 @@ } } }, - "node_modules/@contentstack/venus-components/node_modules/react-select": { - "name": "@contentstack/react-select", - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/@contentstack/react-select/-/react-select-3.2.8.tgz", - "integrity": "sha512-ffc5PKxXgOS0oc1UDUIhZNFZ+XNymw9sk9USRaemzFlCEI5ywl68uJRICiRySO1YMxZIN96aG0cUXTXKfXE4qg==", + "node_modules/@contentstack/venus-components/node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", "license": "MIT", "dependencies": { - "@babel/runtime": "7.26.10", - "@emotion/cache": "10.0.9", - "@emotion/core": "10.0.9", - "@emotion/css": "10.0.9", - "memoize-one": "5.0.0", - "react-input-autosize": "3.0.0", - "react-transition-group": "4.3.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0", - "react-dom": "^16.8.0 || ^17.0.0" - } - }, - "node_modules/@contentstack/venus-components/node_modules/react-select-async-paginate": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/react-select-async-paginate/-/react-select-async-paginate-0.4.1.tgz", - "integrity": "sha512-zWeaN9C9PVQej4bz1+OvU6/ylHE6rHscDYcP+KiWdBedVQ5j2vXBjf/5RWLEvobvtUUHBOTbUF8+m2HDoeIcvQ==", - "dependencies": { - "@babel/runtime": "^7.11.2", - "@seznam/compose-react-refs": "^1.0.4", - "react-is-mounted-hook": "^1.0.3", - "sleep-promise": "^8.0.1" - }, - "peerDependencies": { - "react": "^16.8.0", - "react-select": "^2.0.0 || ^3.0.0" - } - }, - "node_modules/@contentstack/venus-components/node_modules/react-select-async-paginate/node_modules/react-dom": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", - "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "scheduler": "^0.20.2" - }, - "peerDependencies": { - "react": "17.0.2" - } - }, - "node_modules/@contentstack/venus-components/node_modules/react-select-async-paginate/node_modules/react-is-mounted-hook": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/react-is-mounted-hook/-/react-is-mounted-hook-1.1.2.tgz", - "integrity": "sha512-yjq3Tj34CiFcdVOS/h6JerWLOLdJqEGKMNpTHc4kWebzz2YtIpgqMRrqxdmQhewM1KJREojdAV2tsNvBsUWyhA==", - "peerDependencies": { - "react": "^16.8.6 || ^17", - "react-dom": "^16.8.6 || ^17" + "@babel/runtime": "^7.9.2" } }, - "node_modules/@contentstack/venus-components/node_modules/react-select/node_modules/@emotion/css": { - "version": "10.0.9", - "resolved": "https://registry.npmjs.org/@emotion/css/-/css-10.0.9.tgz", - "integrity": "sha512-jtHhUSWw+L7yxYgNtC+KJ3Ory90/jiAtpG1qT+gTQQ/RR5AMiigs9/lDHu/vnwljaq2S48FoKb/FZZMlJcC4bw==", + "node_modules/@contentstack/venus-components/node_modules/slate": { + "version": "0.77.2", + "resolved": "https://registry.npmjs.org/slate/-/slate-0.77.2.tgz", + "integrity": "sha512-raG/eyYyRDThz+r/opUD+wxWxwXQi4fH0ViZh41x8qBu8uOPMTBYl10RA7KsF8IK48DS207uPrvWGGBsnMCLwg==", + "license": "MIT", "dependencies": { - "@emotion/serialize": "^0.11.6", - "@emotion/utils": "0.11.1", - "babel-plugin-emotion": "^10.0.9" + "immer": "^9.0.6", + "is-plain-object": "^5.0.0", + "tiny-warning": "^1.0.3" } }, - "node_modules/@contentstack/venus-components/node_modules/react-select/node_modules/react-input-autosize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-3.0.0.tgz", - "integrity": "sha512-nL9uS7jEs/zu8sqwFE5MAPx6pPkNAriACQ2rGLlqmKr2sPGtN7TXTyDdQt4lbNXVx7Uzadb40x8qotIuru6Rhg==", - "dependencies": { - "prop-types": "^15.5.8" - }, - "peerDependencies": { - "react": "^16.3.0 || ^17.0.0" + "node_modules/@csstools/color-helpers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.1.tgz", + "integrity": "sha512-NmXRccUJMk2AWA5A7e5a//3bCIMyOu2hAtdRYrhPPHjDxINuCwX1w6rnIZ4xjLcp0ayv6h8Pc3X0eJUGiAAXHQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" } }, - "node_modules/@contentstack/venus-components/node_modules/react-sortable-hoc": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/react-sortable-hoc/-/react-sortable-hoc-1.11.0.tgz", - "integrity": "sha512-v1CDCvdfoR3zLGNp6qsBa4J1BWMEVH25+UKxF/RvQRh+mrB+emqtVHMgZ+WreUiKJoEaiwYoScaueIKhMVBHUg==", - "dependencies": { - "@babel/runtime": "^7.2.0", - "invariant": "^2.2.4", - "prop-types": "^15.5.7" + "node_modules/@csstools/css-calc": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.1.1.tgz", + "integrity": "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" }, "peerDependencies": { - "prop-types": "^15.5.7", - "react": "^0.14.0 || ^15.0.0 || ^16.0.0", - "react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0" + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" } }, - "node_modules/@contentstack/venus-components/node_modules/react-transition-group": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.3.0.tgz", - "integrity": "sha512-1qRV1ZuVSdxPlPf4O8t7inxUGpdyO5zG9IoNfJxSO0ImU2A1YWkEQvFPuIPZmMLkg5hYs7vv5mMOyfgSkvAwvw==", + "node_modules/@csstools/css-color-parser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.0.1.tgz", + "integrity": "sha512-vYwO15eRBEkeF6xjAno/KQ61HacNhfQuuU/eGwH67DplL0zD5ZixUa563phQvUelA07yDczIXdtmYojCphKJcw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" + "@csstools/color-helpers": "^6.0.1", + "@csstools/css-calc": "^3.0.0" }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, - "node_modules/@contentstack/venus-components/node_modules/react-virtualized-auto-sizer": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.5.tgz", - "integrity": "sha512-kivjYVWX15TX2IUrm8F1jaCEX8EXrpy3DD+u41WGqJ1ZqbljWpiwscV+VxOM1l7sSIM1jwi2LADjhhAJkJ9dxA==", "engines": { - "node": ">8.0.0" + "node": ">=20.19.0" }, "peerDependencies": { - "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0", - "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0" + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" } }, - "node_modules/@contentstack/venus-components/node_modules/reactjs-social-embed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/reactjs-social-embed/-/reactjs-social-embed-1.2.0.tgz", - "integrity": "sha512-Avi6yyQJ5rbLmAzLkJJHBL+86+3if40QGgOUEu0/GxpARWyMd6w4R7C4AAufQqGC2x7V+HFpMkUovdk4/HjHSg==", - "dependencies": { - "jsonp-p": "^2.0.0" - }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", "engines": { - "node": ">=8", - "npm": ">=5" + "node": ">=20.19.0" }, "peerDependencies": { - "prop-types": "^15.5.4", - "react": "^15.0.0 || ^16.0.0", - "react-dom": "^15.0.0 || ^16.0.0" + "@csstools/css-tokenizer": "^4.0.0" } }, - "node_modules/@contentstack/venus-components/node_modules/redux": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", - "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", - "dependencies": { - "@babel/runtime": "^7.9.2" - } - }, - "node_modules/@contentstack/venus-components/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==", - "license": "MIT" - }, - "node_modules/@contentstack/venus-components/node_modules/scheduler": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", - "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, - "node_modules/@contentstack/venus-components/node_modules/slate": { - "version": "0.77.2", - "resolved": "https://registry.npmjs.org/slate/-/slate-0.77.2.tgz", - "integrity": "sha512-raG/eyYyRDThz+r/opUD+wxWxwXQi4fH0ViZh41x8qBu8uOPMTBYl10RA7KsF8IK48DS207uPrvWGGBsnMCLwg==", - "dependencies": { - "immer": "^9.0.6", - "is-plain-object": "^5.0.0", - "tiny-warning": "^1.0.3" - } + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.0.27", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.27.tgz", + "integrity": "sha512-sxP33Jwg1bviSUXAV43cVYdmjt2TLnLXNqCWl9xmxHawWVjGz/kEbdkr7F9pxJNBN2Mh+dq0crgItbW6tQvyow==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0" }, - "node_modules/@contentstack/venus-components/node_modules/storybook-addon-whats-new": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/storybook-addon-whats-new/-/storybook-addon-whats-new-1.0.3.tgz", - "integrity": "sha512-5je0qJh2ahPbbS2pwOkgKKmUDJAT0aAn3cSmap0Hp3hzFlu4uaDPiY00T4mPF3BsavBrkPNmy7kKIHTXnmRu5A==", - "peerDependencies": { - "@storybook/addons": "^6.4.0", - "@storybook/api": "^6.4.0", - "@storybook/components": "^6.4.0", - "@storybook/core-events": "^6.4.0", - "@storybook/theming": "^6.4.0", - "react": "^16.8.0 || ^17.0.0", - "react-dom": "^16.8.0 || ^17.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" }, - "react-dom": { - "optional": true + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" } }, "node_modules/@emotion/babel-plugin": { "version": "11.13.5", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.16.7", "@babel/runtime": "^7.18.3", @@ -693,15 +571,11 @@ "stylis": "4.2.0" } }, - "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, "node_modules/@emotion/cache": { "version": "11.14.0", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", "dependencies": { "@emotion/memoize": "^0.9.0", "@emotion/sheet": "^1.4.0", @@ -711,16 +585,16 @@ } }, "node_modules/@emotion/core": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.3.1.tgz", - "integrity": "sha512-447aUEjPIm0MnE6QYIaFz9VQOHSXf4Iu6EWOIqq11EAPqinkSZmfymPTmlOE3QjLv846lH4JVZBUOtwGbuQoww==", + "version": "10.0.9", + "resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.0.9.tgz", + "integrity": "sha512-v5a77dV+uWGoy9w6R3MXZG01lqHcXgoy/jGmJqPDGhPjmpWg26LWXAphYZxpZffFUwDUlDdYDiX5HtaKphvJnQ==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.5.5", - "@emotion/cache": "^10.0.27", - "@emotion/css": "^10.0.27", - "@emotion/serialize": "^0.11.15", - "@emotion/sheet": "0.9.4", - "@emotion/utils": "0.11.3" + "@emotion/cache": "^10.0.9", + "@emotion/css": "^10.0.9", + "@emotion/serialize": "^0.11.6", + "@emotion/sheet": "0.9.2", + "@emotion/utils": "0.11.1" }, "peerDependencies": { "react": ">=16.3.0" @@ -730,6 +604,7 @@ "version": "10.0.29", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz", "integrity": "sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==", + "license": "MIT", "dependencies": { "@emotion/sheet": "0.9.4", "@emotion/stylis": "0.8.5", @@ -737,30 +612,52 @@ "@emotion/weak-memoize": "0.2.5" } }, + "node_modules/@emotion/core/node_modules/@emotion/cache/node_modules/@emotion/sheet": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz", + "integrity": "sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA==", + "license": "MIT" + }, + "node_modules/@emotion/core/node_modules/@emotion/cache/node_modules/@emotion/utils": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz", + "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==", + "license": "MIT" + }, "node_modules/@emotion/core/node_modules/@emotion/css": { "version": "10.0.27", "resolved": "https://registry.npmjs.org/@emotion/css/-/css-10.0.27.tgz", "integrity": "sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==", + "license": "MIT", "dependencies": { "@emotion/serialize": "^0.11.15", "@emotion/utils": "0.11.3", "babel-plugin-emotion": "^10.0.27" } }, + "node_modules/@emotion/core/node_modules/@emotion/css/node_modules/@emotion/utils": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz", + "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==", + "license": "MIT" + }, "node_modules/@emotion/core/node_modules/@emotion/hash": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", - "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "license": "MIT" }, "node_modules/@emotion/core/node_modules/@emotion/memoize": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", - "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==" + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "license": "MIT" }, "node_modules/@emotion/core/node_modules/@emotion/serialize": { "version": "0.11.16", "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.16.tgz", "integrity": "sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg==", + "license": "MIT", "dependencies": { "@emotion/hash": "0.8.0", "@emotion/memoize": "0.7.4", @@ -769,40 +666,47 @@ "csstype": "^2.5.7" } }, - "node_modules/@emotion/core/node_modules/@emotion/sheet": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz", - "integrity": "sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA==" + "node_modules/@emotion/core/node_modules/@emotion/serialize/node_modules/@emotion/utils": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz", + "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==", + "license": "MIT" }, - "node_modules/@emotion/core/node_modules/@emotion/stylis": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", - "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==" + "node_modules/@emotion/core/node_modules/@emotion/sheet": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.2.tgz", + "integrity": "sha512-pVBLzIbC/QCHDKJF2E82V2H/W/B004mDFQZiyo/MSR+VC4pV5JLG0TF/zgQDFvP3fZL/5RTPGEmXlYJBMUuJ+A==", + "license": "MIT" }, "node_modules/@emotion/core/node_modules/@emotion/unitless": { "version": "0.7.5", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", - "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "license": "MIT" }, "node_modules/@emotion/core/node_modules/@emotion/utils": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz", - "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==" + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.1.tgz", + "integrity": "sha512-8M3VN0hetwhsJ8dH8VkVy7xo5/1VoBsDOk/T4SJOeXwTO1c4uIqVNx2qyecLFnnUWD5vvUqHQ1gASSeUN6zcTg==", + "license": "MIT" }, "node_modules/@emotion/core/node_modules/@emotion/weak-memoize": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", - "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==" + "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==", + "license": "MIT" }, "node_modules/@emotion/core/node_modules/csstype": { "version": "2.6.21", "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", - "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==", + "license": "MIT" }, "node_modules/@emotion/css": { "version": "11.13.5", "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.13.5.tgz", "integrity": "sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==", + "license": "MIT", "dependencies": { "@emotion/babel-plugin": "^11.13.5", "@emotion/cache": "^11.13.5", @@ -814,12 +718,14 @@ "node_modules/@emotion/hash": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", - "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" }, "node_modules/@emotion/is-prop-valid": { "version": "0.8.8", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "license": "MIT", "dependencies": { "@emotion/memoize": "0.7.4" } @@ -827,17 +733,20 @@ "node_modules/@emotion/is-prop-valid/node_modules/@emotion/memoize": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", - "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==" + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "license": "MIT" }, "node_modules/@emotion/memoize": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", - "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" }, "node_modules/@emotion/serialize": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", "dependencies": { "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", @@ -849,12 +758,14 @@ "node_modules/@emotion/sheet": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", - "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" }, "node_modules/@emotion/styled": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-10.3.0.tgz", "integrity": "sha512-GgcUpXBBEU5ido+/p/mCT2/Xx+Oqmp9JzQRuC+a4lYM4i4LBBn/dWvc0rQ19N9ObA8/T4NWMrPNe79kMBDJqoQ==", + "license": "MIT", "dependencies": { "@emotion/styled-base": "^10.3.0", "babel-plugin-emotion": "^10.0.27" @@ -868,6 +779,7 @@ "version": "10.3.0", "resolved": "https://registry.npmjs.org/@emotion/styled-base/-/styled-base-10.3.0.tgz", "integrity": "sha512-PBRqsVKR7QRNkmfH78hTSSwHWcwDpecH9W6heujWAcyp2wdz/64PP73s7fWS1dIPm8/Exc8JAzYS8dEWXjv60w==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.5.5", "@emotion/is-prop-valid": "0.8.8", @@ -882,17 +794,20 @@ "node_modules/@emotion/styled-base/node_modules/@emotion/hash": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", - "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "license": "MIT" }, "node_modules/@emotion/styled-base/node_modules/@emotion/memoize": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", - "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==" + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "license": "MIT" }, "node_modules/@emotion/styled-base/node_modules/@emotion/serialize": { "version": "0.11.16", "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.16.tgz", "integrity": "sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg==", + "license": "MIT", "dependencies": { "@emotion/hash": "0.8.0", "@emotion/memoize": "0.7.4", @@ -904,37 +819,44 @@ "node_modules/@emotion/styled-base/node_modules/@emotion/unitless": { "version": "0.7.5", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", - "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "license": "MIT" }, "node_modules/@emotion/styled-base/node_modules/@emotion/utils": { "version": "0.11.3", "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz", - "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==" + "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==", + "license": "MIT" }, "node_modules/@emotion/styled-base/node_modules/csstype": { "version": "2.6.21", "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", - "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==", + "license": "MIT" }, "node_modules/@emotion/stylis": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.3.tgz", - "integrity": "sha512-M3nMfJ6ndJMYloSIbYEBq6G3eqoYD41BpDOxreE8j0cb4fzz/5qvmqU9Mb2hzsXcCnIlGlWhS03PCzVGvTAe0Q==" + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", + "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==", + "license": "MIT" }, "node_modules/@emotion/unitless": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", - "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==" + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" }, "node_modules/@emotion/utils": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", - "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==" + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" }, "node_modules/@emotion/weak-memoize": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", - "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.3", @@ -943,6 +865,7 @@ "cpu": [ "ppc64" ], + "license": "MIT", "optional": true, "os": [ "aix" @@ -958,6 +881,7 @@ "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -973,6 +897,7 @@ "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -988,6 +913,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -1003,6 +929,7 @@ "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1018,6 +945,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1033,6 +961,7 @@ "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -1048,6 +977,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -1063,6 +993,7 @@ "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1078,6 +1009,7 @@ "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1093,6 +1025,7 @@ "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1108,6 +1041,7 @@ "cpu": [ "loong64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1123,6 +1057,7 @@ "cpu": [ "mips64el" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1138,6 +1073,7 @@ "cpu": [ "ppc64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1153,6 +1089,7 @@ "cpu": [ "riscv64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1168,6 +1105,7 @@ "cpu": [ "s390x" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1183,6 +1121,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1198,6 +1137,7 @@ "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -1213,6 +1153,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -1228,6 +1169,7 @@ "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -1243,6 +1185,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -1258,6 +1201,7 @@ "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "openharmony" @@ -1273,6 +1217,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "sunos" @@ -1288,6 +1233,7 @@ "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -1303,6 +1249,7 @@ "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -1318,6 +1265,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -1331,6 +1279,7 @@ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, + "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" }, @@ -1349,6 +1298,7 @@ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -1358,6 +1308,7 @@ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -1381,16 +1332,36 @@ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, + "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@exodus/bytes": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.14.1.tgz", + "integrity": "sha512-OhkBFWI6GcRMUroChZiopRiSp2iAMvEBK47NhJooDqz1RERO4QuZIZnjP63TXX8GAiLABkYmX+fuQsdJ1dd2QQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "dev": true, + "license": "Apache-2.0", "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", @@ -1405,6 +1376,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -1418,12 +1390,14 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "deprecated": "Use @eslint/object-schema instead", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@icons/material": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz", "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==", + "license": "MIT", "peerDependencies": { "react": "*" } @@ -1432,6 +1406,7 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -1448,6 +1423,7 @@ "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -1459,6 +1435,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -1473,6 +1450,7 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" @@ -1482,6 +1460,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -1489,12 +1468,14 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -1504,6 +1485,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz", "integrity": "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==", + "license": "MIT", "dependencies": { "@types/mdx": "^2.0.0" }, @@ -1521,6 +1503,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -1534,6 +1517,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } @@ -1543,6 +1527,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -1554,13 +1539,15 @@ "node_modules/@one-ini/wasm": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", - "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==" + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "license": "MIT" }, "node_modules/@parcel/watcher": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.4.tgz", - "integrity": "sha512-WYa2tUVV5HiArWPB3ydlOc4R2ivq0IDrlqhMi3l7mVsFEXNcTfxYFPIHXHXIh/ca/y/V5N4E1zecyxdIBjYnkQ==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", + "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", "hasInstallScript": true, + "license": "MIT", "optional": true, "dependencies": { "detect-libc": "^2.0.3", @@ -1574,33 +1561,274 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.5.4", - "@parcel/watcher-darwin-arm64": "2.5.4", - "@parcel/watcher-darwin-x64": "2.5.4", - "@parcel/watcher-freebsd-x64": "2.5.4", - "@parcel/watcher-linux-arm-glibc": "2.5.4", - "@parcel/watcher-linux-arm-musl": "2.5.4", - "@parcel/watcher-linux-arm64-glibc": "2.5.4", - "@parcel/watcher-linux-arm64-musl": "2.5.4", - "@parcel/watcher-linux-x64-glibc": "2.5.4", - "@parcel/watcher-linux-x64-musl": "2.5.4", - "@parcel/watcher-win32-arm64": "2.5.4", - "@parcel/watcher-win32-ia32": "2.5.4", - "@parcel/watcher-win32-x64": "2.5.4" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.6", + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6", + "@parcel/watcher-freebsd-x64": "2.5.6", + "@parcel/watcher-linux-arm-glibc": "2.5.6", + "@parcel/watcher-linux-arm-musl": "2.5.6", + "@parcel/watcher-linux-arm64-glibc": "2.5.6", + "@parcel/watcher-linux-arm64-musl": "2.5.6", + "@parcel/watcher-linux-x64-glibc": "2.5.6", + "@parcel/watcher-linux-x64-musl": "2.5.6", + "@parcel/watcher-win32-arm64": "2.5.6", + "@parcel/watcher-win32-ia32": "2.5.6", + "@parcel/watcher-win32-x64": "2.5.6" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", + "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", + "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", + "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", + "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", + "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", + "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", + "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", + "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", + "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.4.tgz", - "integrity": "sha512-kphKy377pZiWpAOyTgQYPE5/XEKVMaj6VUjKT5VkNyUJlr2qZAn8gIc7CPzx+kbhvqHDT9d7EqdOqRXT6vk0zw==", + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", + "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", "cpu": [ - "arm64" + "x64" ], + "license": "MIT", "optional": true, "os": [ - "darwin" + "win32" ], "engines": { "node": ">= 10.0.0" @@ -1614,15 +1842,24 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", "optional": true, "engines": { "node": ">=14" } }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -1631,22 +1868,26 @@ "node_modules/@react-dnd/asap": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-4.0.1.tgz", - "integrity": "sha512-kLy0PJDDwvwwTXxqTFNAAllPHD73AycE9ypWeln/IguoGBEbvFcPDbCV03G52bEcC5E+YgupBE0VzHGdC8SIXg==" + "integrity": "sha512-kLy0PJDDwvwwTXxqTFNAAllPHD73AycE9ypWeln/IguoGBEbvFcPDbCV03G52bEcC5E+YgupBE0VzHGdC8SIXg==", + "license": "MIT" }, "node_modules/@react-dnd/invariant": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-2.0.0.tgz", - "integrity": "sha512-xL4RCQBCBDJ+GRwKTFhGUW8GXa4yoDfJrPbLblc3U09ciS+9ZJXJ3Qrcs/x2IODOdIE5kQxvMmE2UKyqUictUw==" + "integrity": "sha512-xL4RCQBCBDJ+GRwKTFhGUW8GXa4yoDfJrPbLblc3U09ciS+9ZJXJ3Qrcs/x2IODOdIE5kQxvMmE2UKyqUictUw==", + "license": "MIT" }, "node_modules/@react-dnd/shallowequal": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz", - "integrity": "sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==" + "integrity": "sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==", + "license": "MIT" }, "node_modules/@reduxjs/toolkit": { "version": "2.11.2", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", "integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==", + "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.0.0", "@standard-schema/utils": "^0.3.0", @@ -1671,303 +1912,329 @@ "node_modules/@rolldown/pluginutils": { "version": "1.0.0-rc.2", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz", - "integrity": "sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==" + "integrity": "sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==", + "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", - "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", - "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", - "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", - "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", - "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", - "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", - "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", - "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", - "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", - "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", - "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", "cpu": [ "loong64" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", - "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", "cpu": [ "loong64" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", - "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", "cpu": [ "ppc64" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", - "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", "cpu": [ "ppc64" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", - "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", "cpu": [ "riscv64" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", - "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", "cpu": [ "riscv64" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", - "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", "cpu": [ "s390x" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", - "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", - "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", - "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "openbsd" ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", - "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "openharmony" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", - "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", - "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", - "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", - "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -1976,27 +2243,32 @@ "node_modules/@seznam/compose-react-refs": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@seznam/compose-react-refs/-/compose-react-refs-1.0.6.tgz", - "integrity": "sha512-izzOXQfeQLonzrIQb8u6LQ8dk+ymz3WXTIXjvOlTXHq6sbzROg3NWU+9TTAOpEoK9Bth24/6F/XrfHJ5yR5n6Q==" + "integrity": "sha512-izzOXQfeQLonzrIQb8u6LQ8dk+ymz3WXTIXjvOlTXHq6sbzROg3NWU+9TTAOpEoK9Bth24/6F/XrfHJ5yR5n6Q==", + "license": "ISC" }, "node_modules/@socket.io/component-emitter": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==" + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" }, "node_modules/@standard-schema/utils": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", - "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==" + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" }, "node_modules/@storybook/addons": { "version": "6.5.16", "resolved": "https://registry.npmjs.org/@storybook/addons/-/addons-6.5.16.tgz", "integrity": "sha512-p3DqQi+8QRL5k7jXhXmJZLsE/GqHqyY6PcoA1oNTJr0try48uhTGUOYkgzmqtDaa/qPFO5LP+xCPzZXckGtquQ==", + "license": "MIT", "peer": true, "dependencies": { "@storybook/api": "6.5.16", @@ -2020,10 +2292,23 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/@storybook/addons/node_modules/core-js": { + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", + "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==", + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/@storybook/api": { "version": "6.5.16", "resolved": "https://registry.npmjs.org/@storybook/api/-/api-6.5.16.tgz", "integrity": "sha512-HOsuT8iomqeTMQJrRx5U8nsC7lJTwRr1DhdD0SzlqL4c80S/7uuCy4IZvOt4sYQjOzW5fOo/kamcoBXyLproTA==", + "license": "MIT", "peer": true, "dependencies": { "@storybook/channels": "6.5.16", @@ -2053,10 +2338,23 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/@storybook/api/node_modules/core-js": { + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", + "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==", + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/@storybook/channels": { "version": "6.5.16", "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-6.5.16.tgz", "integrity": "sha512-VylzaWQZaMozEwZPJdyJoz+0jpDa8GRyaqu9TGG6QGv+KU5POoZaGLDkRE7TzWkyyP0KQLo80K99MssZCpgSeg==", + "license": "MIT", "peer": true, "dependencies": { "core-js": "^3.8.2", @@ -2068,10 +2366,23 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@storybook/channels/node_modules/core-js": { + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", + "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==", + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/@storybook/client-logger": { "version": "6.5.16", "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.5.16.tgz", "integrity": "sha512-pxcNaCj3ItDdicPTXTtmYJE3YC1SjxFrBmHcyrN+nffeNyiMuViJdOOZzzzucTUG0wcOOX8jaSyak+nnHg5H1Q==", + "license": "MIT", "peer": true, "dependencies": { "core-js": "^3.8.2", @@ -2082,10 +2393,23 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@storybook/client-logger/node_modules/core-js": { + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", + "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==", + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/@storybook/components": { "version": "6.5.16", "resolved": "https://registry.npmjs.org/@storybook/components/-/components-6.5.16.tgz", "integrity": "sha512-LzBOFJKITLtDcbW9jXl0/PaG+4xAz25PK8JxPZpIALbmOpYWOAPcO6V9C2heX6e6NgWFMUxjplkULEk9RCQMNA==", + "license": "MIT", "peer": true, "dependencies": { "@storybook/client-logger": "6.5.16", @@ -2106,10 +2430,23 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/@storybook/components/node_modules/core-js": { + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", + "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==", + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/@storybook/core-events": { "version": "6.5.16", "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.5.16.tgz", "integrity": "sha512-qMZQwmvzpH5F2uwNUllTPg6eZXr2OaYZQRRN8VZJiuorZzDNdAFmiVWMWdkThwmyLEJuQKXxqCL8lMj/7PPM+g==", + "license": "MIT", "peer": true, "dependencies": { "core-js": "^3.8.2" @@ -2119,10 +2456,23 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@storybook/core-events/node_modules/core-js": { + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", + "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==", + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/@storybook/csf": { "version": "0.0.2--canary.4566f4d.1", "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.0.2--canary.4566f4d.1.tgz", "integrity": "sha512-9OVvMVh3t9znYZwb0Svf/YQoxX2gVOeQTGe2bses2yj+a3+OJnCrUF3/hGv6Em7KujtOdL2LL+JnG49oMVGFgQ==", + "license": "MIT", "peer": true, "dependencies": { "lodash": "^4.17.15" @@ -2132,6 +2482,7 @@ "version": "6.5.16", "resolved": "https://registry.npmjs.org/@storybook/router/-/router-6.5.16.tgz", "integrity": "sha512-ZgeP8a5YV/iuKbv31V8DjPxlV4AzorRiR8OuSt/KqaiYXNXlOoQDz/qMmiNcrshrfLpmkzoq7fSo4T8lWo2UwQ==", + "license": "MIT", "peer": true, "dependencies": { "@storybook/client-logger": "6.5.16", @@ -2149,10 +2500,23 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/@storybook/router/node_modules/core-js": { + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", + "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==", + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/@storybook/semver": { "version": "7.3.2", "resolved": "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz", "integrity": "sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg==", + "license": "ISC", "peer": true, "dependencies": { "core-js": "^3.6.5", @@ -2165,10 +2529,79 @@ "node": ">=10" } }, + "node_modules/@storybook/semver/node_modules/core-js": { + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", + "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==", + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/@storybook/semver/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "peer": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/semver/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "peer": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/semver/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "peer": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@storybook/semver/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "peer": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@storybook/theming": { "version": "6.5.16", "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-6.5.16.tgz", "integrity": "sha512-hNLctkjaYLRdk1+xYTkC1mg4dYz2wSv6SqbLpcKMbkPHTE0ElhddGPHQqB362md/w9emYXNkt1LSMD8Xk9JzVQ==", + "license": "MIT", "peer": true, "dependencies": { "@storybook/client-logger": "6.5.16", @@ -2185,11 +2618,24 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/@storybook/theming/node_modules/core-js": { + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", + "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==", + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/@swc/core": { "version": "1.15.11", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.11.tgz", "integrity": "sha512-iLmLTodbYxU39HhMPaMUooPwO/zqJWvsqkrXv1ZI38rMb048p6N7qtAtTp37sw9NzSrvH6oli8EdDygo09IZ/w==", "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.25" @@ -2229,6 +2675,7 @@ "cpu": [ "arm64" ], + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "darwin" @@ -2244,6 +2691,7 @@ "cpu": [ "x64" ], + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "darwin" @@ -2259,6 +2707,7 @@ "cpu": [ "arm" ], + "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -2274,6 +2723,7 @@ "cpu": [ "arm64" ], + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" @@ -2289,6 +2739,7 @@ "cpu": [ "arm64" ], + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" @@ -2304,6 +2755,7 @@ "cpu": [ "x64" ], + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" @@ -2319,6 +2771,7 @@ "cpu": [ "x64" ], + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" @@ -2334,6 +2787,7 @@ "cpu": [ "arm64" ], + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "win32" @@ -2349,6 +2803,7 @@ "cpu": [ "ia32" ], + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "win32" @@ -2364,6 +2819,7 @@ "cpu": [ "x64" ], + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "win32" @@ -2372,42 +2828,158 @@ "node": ">=10" } }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" - }, - "node_modules/@swc/types": { - "version": "0.1.25", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz", - "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, + "node_modules/@swc/types": { + "version": "0.1.25", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz", + "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/@testing-library/dom/node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/react": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", + "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@testing-library/react-hooks": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz", + "integrity": "sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==", + "license": "MIT", "dependencies": { - "@swc/counter": "^0.1.3" + "@babel/runtime": "^7.12.5", + "react-error-boundary": "^3.1.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "@types/react": "^16.9.0 || ^17.0.0", + "react": "^16.9.0 || ^17.0.0", + "react-dom": "^16.9.0 || ^17.0.0", + "react-test-renderer": "^16.9.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-test-renderer": { + "optional": true + } } }, - "node_modules/@testing-library/jest-dom": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", - "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "node_modules/@testing-library/user-event": { + "version": "14.6.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", + "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", + "dev": true, "license": "MIT", - "dependencies": { - "@adobe/css-tools": "^4.4.0", - "aria-query": "^5.0.0", - "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.6.3", - "picocolors": "^1.1.1", - "redent": "^3.0.0" - }, "engines": { - "node": ">=14", - "npm": ">=6", - "yarn": ">=1" + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" } }, "node_modules/@tippyjs/react": { "version": "4.2.6", "resolved": "https://registry.npmjs.org/@tippyjs/react/-/react-4.2.6.tgz", "integrity": "sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==", + "license": "MIT", "dependencies": { "tippy.js": "^6.3.1" }, @@ -2416,25 +2988,48 @@ "react-dom": ">=16.8" } }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, "node_modules/@types/d3-array": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", - "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==" + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" }, "node_modules/@types/d3-color": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", - "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" }, "node_modules/@types/d3-ease": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", - "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" }, "node_modules/@types/d3-interpolate": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", "dependencies": { "@types/d3-color": "*" } @@ -2442,20 +3037,23 @@ "node_modules/@types/d3-path": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", - "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==" + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" }, "node_modules/@types/d3-scale": { "version": "4.0.9", "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", "dependencies": { "@types/d3-time": "*" } }, "node_modules/@types/d3-shape": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", - "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", "dependencies": { "@types/d3-path": "*" } @@ -2463,22 +3061,33 @@ "node_modules/@types/d3-time": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", - "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==" + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" }, "node_modules/@types/d3-timer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", - "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" }, "node_modules/@types/hoist-non-react-statics": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz", "integrity": "sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==", + "license": "MIT", "dependencies": { "hoist-non-react-statics": "^3.3.0" }, @@ -2490,37 +3099,44 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@types/is-function/-/is-function-1.0.3.tgz", "integrity": "sha512-/CLhCW79JUeLKznI6mbVieGbl4QU5Hfn+6udw1YHZoofASjbQ5zaP5LzAUZYDpRYEjS4/P+DhEgyJ/PQmGGTWw==", + "license": "MIT", "peer": true }, "node_modules/@types/is-hotkey": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/@types/is-hotkey/-/is-hotkey-0.1.10.tgz", - "integrity": "sha512-RvC8KMw5BCac1NvRRyaHgMMEtBaZ6wh0pyPTBu7izn4Sj/AX9Y4aXU5c7rX8PnM/knsuUpC1IeoBkANtxBypsQ==" + "integrity": "sha512-RvC8KMw5BCac1NvRRyaHgMMEtBaZ6wh0pyPTBu7izn4Sj/AX9Y4aXU5c7rX8PnM/knsuUpC1IeoBkANtxBypsQ==", + "license": "MIT" }, "node_modules/@types/lodash": { "version": "4.17.23", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==" + "integrity": "sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==", + "license": "MIT" }, "node_modules/@types/mdx": { "version": "2.0.13", "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", - "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==" + "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", + "license": "MIT" }, "node_modules/@types/parse-json": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" }, "node_modules/@types/prop-types": { "version": "15.7.15", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", - "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==" + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.27", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", - "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", + "version": "18.3.28", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", + "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", + "license": "MIT", "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -2530,6 +3146,7 @@ "version": "18.3.7", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "license": "MIT", "peerDependencies": { "@types/react": "^18.0.0" } @@ -2538,6 +3155,7 @@ "version": "7.1.34", "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.34.tgz", "integrity": "sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==", + "license": "MIT", "dependencies": { "@types/hoist-non-react-statics": "^3.3.0", "@types/react": "*", @@ -2549,44 +3167,43 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.9.2" } }, - "node_modules/@types/scheduler": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", - "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", - "optional": true, - "peer": true - }, "node_modules/@types/trusted-types": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", "optional": true }, "node_modules/@types/use-sync-external-store": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", - "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==" + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" }, "node_modules/@types/webpack-env": { "version": "1.18.8", "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.18.8.tgz", "integrity": "sha512-G9eAoJRMLjcvN4I08wB5I7YofOb/kaJNd5uoCMX+LbKXTPCF+ZIHuqTnFaK9Jz1rgs035f9JUPUhNFtqgucy/A==", + "license": "MIT", "peer": true }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/@vitejs/plugin-react-swc": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-4.2.3.tgz", "integrity": "sha512-QIluDil2prhY1gdA3GGwxZzTAmLdi8cQ2CcuMW4PB/Wu4e/1pzqrwhYWVd09LInCRlDUidQjd0B70QWbjWtLxA==", + "license": "MIT", "dependencies": { "@rolldown/pluginutils": "1.0.0-rc.2", "@swc/core": "^1.15.11" @@ -2598,24 +3215,191 @@ "vite": "^4 || ^5 || ^6 || ^7" } }, + "node_modules/@vitest/coverage-v8": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.18.tgz", + "integrity": "sha512-7i+N2i0+ME+2JFZhfuz7Tg/FqKtilHjGyGvoHYQ6iLV0zahbsJ9sljC9OcFcPDbhYKCet+sG8SsVqlyGvPflZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.0.18", + "ast-v8-to-istanbul": "^0.3.10", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.1", + "obug": "^2.1.1", + "std-env": "^3.10.0", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.0.18", + "vitest": "4.0.18" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", + "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz", + "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.18", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", + "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz", + "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.18", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", + "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.18", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz", + "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/ui": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.0.18.tgz", + "integrity": "sha512-CGJ25bc8fRi8Lod/3GHSvXRKi7nBo3kxh0ApW4yCjmrWmRmlT53B5E08XRSZRliygG0aVNxLrBEqPYdz/KcCtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.18", + "fflate": "^0.8.2", + "flatted": "^3.3.3", + "pathe": "^2.0.3", + "sirv": "^3.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "4.0.18" + } + }, + "node_modules/@vitest/utils": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz", + "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.18", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/abbrev": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/ace-builds": { - "version": "1.43.5", - "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.43.5.tgz", - "integrity": "sha512-iH5FLBKdB7SVn9GR37UgA/tpQS8OTWIxWAuq3Ofaw+Qbc69FfPXsXd9jeW7KRG2xKpKMqBDnu0tHBrCWY5QI7A==" + "version": "1.43.6", + "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.43.6.tgz", + "integrity": "sha512-L1ddibQ7F3vyXR2k2fg+I8TQTPWVA6CKeDQr/h2+8CeyTp3W6EQL8xNFZRTztuP8xNOAqL3IYPqdzs31GCjDvg==", + "license": "BSD-3-Clause" }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -2628,15 +3412,27 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2652,6 +3448,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -2660,6 +3457,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -2674,12 +3472,14 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/aria-query": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">= 0.4" @@ -2690,6 +3490,7 @@ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" @@ -2705,6 +3506,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-flat-polyfill/-/array-flat-polyfill-1.0.1.tgz", "integrity": "sha512-hfJmKupmQN0lwi0xG6FQ5U8Rd97RnIERplymOv/qpq8AoNKPPAnxJadjFA23FNWm88wykh9HmpLJUUwUtNU/iw==", + "license": "CC0-1.0", "engines": { "node": ">=6.0.0" } @@ -2714,6 +3516,7 @@ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", @@ -2735,6 +3538,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/array-move/-/array-move-3.0.1.tgz", "integrity": "sha512-H3Of6NIn2nNU1gsVDqDnYKY/LCdWvCMMOWifNGhKcVQgiZ6nOek39aESOvro6zmueP07exSl93YLvkN4fZOkSg==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -2747,6 +3551,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -2767,6 +3572,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -2785,6 +3591,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -2803,6 +3610,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -2819,6 +3627,7 @@ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", @@ -2835,16 +3644,47 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-v8-to-istanbul": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.11.tgz", + "integrity": "sha512-Qya9fkoofMjCBNVdWINMjB5KZvkYfaO9/anwkWnjxibpWUxo5iHl2sOdP7/uAqaRuUYuoo8rDwnbaaKVFxoUvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, "node_modules/async": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", - "integrity": "sha512-l6ToIJIotphWahxxHyzK9bnLR6kM4jJIIgLShZeqLY7iboHoGkdgFl7W2/Ivi4SkMJYGKqW8vSuk0uKUj6qsSw==" + "integrity": "sha512-l6ToIJIotphWahxxHyzK9bnLR6kM4jJIIgLShZeqLY7iboHoGkdgFl7W2/Ivi4SkMJYGKqW8vSuk0uKUj6qsSw==", + "license": "MIT" }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -2852,12 +3692,14 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" }, "node_modules/attr-accept": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz", "integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==", + "license": "MIT", "engines": { "node": ">=4" } @@ -2867,6 +3709,7 @@ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, + "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -2881,6 +3724,7 @@ "version": "1.13.5", "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", @@ -2891,6 +3735,7 @@ "version": "10.2.2", "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.2.2.tgz", "integrity": "sha512-SMSkGoqTbTyUTDeuVuPIWifPdUGkTk1Kf9BWRiXIOIcuyMfsdp2EjeiiFvOzX8NOBvEh/ypKYvUh2rkgAJMCLA==", + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.0.0", "@emotion/hash": "0.8.0", @@ -2907,17 +3752,20 @@ "node_modules/babel-plugin-emotion/node_modules/@emotion/hash": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", - "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "license": "MIT" }, "node_modules/babel-plugin-emotion/node_modules/@emotion/memoize": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", - "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==" + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "license": "MIT" }, "node_modules/babel-plugin-emotion/node_modules/@emotion/serialize": { "version": "0.11.16", "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.16.tgz", "integrity": "sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg==", + "license": "MIT", "dependencies": { "@emotion/hash": "0.8.0", "@emotion/memoize": "0.7.4", @@ -2929,32 +3777,31 @@ "node_modules/babel-plugin-emotion/node_modules/@emotion/unitless": { "version": "0.7.5", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", - "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "license": "MIT" }, "node_modules/babel-plugin-emotion/node_modules/@emotion/utils": { "version": "0.11.3", "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz", - "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==" + "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==", + "license": "MIT" }, "node_modules/babel-plugin-emotion/node_modules/babel-plugin-macros": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.7.2", "cosmiconfig": "^6.0.0", "resolve": "^1.12.0" } }, - "node_modules/babel-plugin-emotion/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, "node_modules/babel-plugin-emotion/node_modules/cosmiconfig": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "license": "MIT", "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.1.0", @@ -2969,12 +3816,14 @@ "node_modules/babel-plugin-emotion/node_modules/csstype": { "version": "2.6.21", "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", - "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==", + "license": "MIT" }, "node_modules/babel-plugin-emotion/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", "engines": { "node": ">=0.8.0" } @@ -2983,6 +3832,7 @@ "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", "engines": { "node": ">= 6" } @@ -2991,6 +3841,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", @@ -3004,47 +3855,60 @@ "node_modules/babel-plugin-syntax-jsx": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", - "integrity": "sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==" + "integrity": "sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==", + "license": "MIT" }, "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } }, "node_modules/block-elements": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/block-elements/-/block-elements-1.2.0.tgz", - "integrity": "sha512-4E+pnt4v8HSEEH3Dwe2Bcu8TIbdReez7b5Qjs11dJIdbGFaNSobDgphWxy9NtjYB9ZsZd7DzByDbeXy4DvYz5Q==" + "integrity": "sha512-4E+pnt4v8HSEEH3Dwe2Bcu8TIbdReez7b5Qjs11dJIdbGFaNSobDgphWxy9NtjYB9ZsZd7DzByDbeXy4DvYz5Q==", + "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/browser-detect": { "version": "0.2.28", "resolved": "https://registry.npmjs.org/browser-detect/-/browser-detect-0.2.28.tgz", "integrity": "sha512-KeWGHqYQmHDkCFG2dIiX/2wFUgqevbw/rd6wNi9N6rZbaSJFtG5kel0HtprRwCGp8sqpQP79LzDJXf/WCx4WAw==", + "license": "MIT", "dependencies": { "core-js": "^2.5.7" } }, - "node_modules/browser-detect/node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "hasInstallScript": true - }, "node_modules/cache": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cache/-/cache-3.0.0.tgz", "integrity": "sha512-sNoM5jithfalxIceo/uFFm5bOlGjux2y8jEvjNb0F/cACWQaMmWuEPTLl6GzLHdFcNsbWBBdqkBd9NyefZ5UQQ==", + "license": "ISC", "dependencies": { "ds": "^1.4.2" } @@ -3053,6 +3917,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", @@ -3070,6 +3935,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -3082,6 +3948,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" @@ -3097,15 +3964,27 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", "engines": { "node": ">=6" } }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3117,15 +3996,32 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/classnames": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" }, "node_modules/clean": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/clean/-/clean-4.0.2.tgz", "integrity": "sha512-2LGVh4dNtI16L4UzqDHO6Hbl74YjG1vWvEUU78dgLO4kuyqJZFMNMPBx+EGtYKTFb14e24p+gWXgkabqxc1EUw==", + "license": "MIT", "dependencies": { "async": "^0.9.0", "minimist": "^1.1.0", @@ -3137,6 +4033,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -3145,6 +4042,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -3155,12 +4053,14 @@ "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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -3172,6 +4072,7 @@ "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "license": "MIT", "engines": { "node": ">=14" } @@ -3179,18 +4080,14 @@ "node_modules/compute-scroll-into-view": { "version": "1.0.20", "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz", - "integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==" - }, - "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-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==", + "license": "MIT" }, "node_modules/condense-newlines": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/condense-newlines/-/condense-newlines-0.2.1.tgz", "integrity": "sha512-P7X+QL9Hb9B/c8HI5BFFKmjgBu2XpQuF98WZ9XkO+dBGgk5XgwiQz7o1SmpglNWId3581UcS0SFAWfoIhMHPfg==", + "license": "MIT", "dependencies": { "extend-shallow": "^2.0.1", "is-whitespace": "^0.3.0", @@ -3204,15 +4101,23 @@ "version": "1.1.13", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "license": "MIT", "dependencies": { "ini": "^1.3.4", "proto-list": "~1.2.1" } }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, "node_modules/cookie": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", "engines": { "node": ">=18" }, @@ -3222,20 +4127,18 @@ } }, "node_modules/core-js": { - "version": "3.47.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", - "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", "hasInstallScript": true, - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } + "license": "MIT" }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -3251,6 +4154,7 @@ "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", "engines": { "node": ">= 6" } @@ -3259,6 +4163,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", + "license": "MIT", "dependencies": { "node-fetch": "^2.7.0" } @@ -3267,6 +4172,7 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -3280,6 +4186,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", + "license": "MIT", "dependencies": { "tiny-invariant": "^1.0.6" } @@ -3290,21 +4197,54 @@ "integrity": "sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==", "license": "BSD" }, + "node_modules/css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, "node_modules/css.escape": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, "license": "MIT" }, + "node_modules/cssstyle": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-6.0.1.tgz", + "integrity": "sha512-IoJs7La+oFp/AB033wBStxNOJt4+9hHMxsXUPANcoXL2b3W4DZKghlJ2cI/eyeRZIQ9ysvYEorVhjrcYctWbog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^4.1.2", + "@csstools/css-syntax-patches-for-csstree": "^1.0.26", + "css-tree": "^3.1.0", + "lru-cache": "^11.2.5" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==" + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" }, "node_modules/d3-array": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", "dependencies": { "internmap": "1 - 2" }, @@ -3316,6 +4256,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", "engines": { "node": ">=12" } @@ -3324,14 +4265,16 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", "engines": { "node": ">=12" } }, "node_modules/d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", "engines": { "node": ">=12" } @@ -3340,6 +4283,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", "dependencies": { "d3-color": "1 - 3" }, @@ -3351,6 +4295,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", "engines": { "node": ">=12" } @@ -3359,6 +4304,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", @@ -3374,6 +4320,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", "dependencies": { "d3-path": "^3.1.0" }, @@ -3385,6 +4332,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", "dependencies": { "d3-array": "2 - 3" }, @@ -3396,6 +4344,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", "dependencies": { "d3-time": "1 - 3" }, @@ -3407,15 +4356,31 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", "engines": { "node": ">=12" } }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -3433,6 +4398,7 @@ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -3450,6 +4416,7 @@ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -3466,6 +4433,7 @@ "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==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.21.0" }, @@ -3481,6 +4449,7 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -3493,15 +4462,24 @@ } } }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, "node_modules/decimal.js-light": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", - "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" }, "node_modules/deep-equal": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "license": "MIT", "dependencies": { "is-arguments": "^1.1.1", "is-date-object": "^1.0.5", @@ -3521,12 +4499,14 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/deepmerge": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -3535,6 +4515,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -3551,6 +4532,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -3567,19 +4549,33 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", "engines": { "node": ">=0.4.0" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/detect-browser": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.3.0.tgz", - "integrity": "sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==" + "integrity": "sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==", + "license": "MIT" }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", "optional": true, "engines": { "node": ">=8" @@ -3588,12 +4584,14 @@ "node_modules/diff-match-patch": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", - "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==" + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", + "license": "Apache-2.0" }, "node_modules/direction": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/direction/-/direction-1.0.4.tgz", "integrity": "sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==", + "license": "MIT", "bin": { "direction": "cli.js" }, @@ -3606,6 +4604,7 @@ "version": "11.1.3", "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-11.1.3.tgz", "integrity": "sha512-QugF55dNW+h+vzxVJ/LSJeTeUw9MCJ2cllhmVThVPEtF16ooBkxj0WBE5RB+AceFxMFo1rO6bJKXtqKl+JNnyA==", + "license": "MIT", "dependencies": { "@react-dnd/asap": "^4.0.0", "@react-dnd/invariant": "^2.0.0", @@ -3616,6 +4615,7 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.9.2" } @@ -3625,6 +4625,7 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, + "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -3636,12 +4637,14 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, "license": "MIT" }, "node_modules/dom-helpers": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" @@ -3651,6 +4654,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -3675,12 +4679,14 @@ "type": "github", "url": "https://github.com/sponsors/fb55" } - ] + ], + "license": "BSD-2-Clause" }, "node_modules/domhandler": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", "dependencies": { "domelementtype": "^2.3.0" }, @@ -3692,9 +4698,13 @@ } }, "node_modules/dompurify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz", - "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.2.tgz", + "integrity": "sha512-6obghkliLdmKa56xdbLOpUZ43pAR6xFy1uOrxBaIDjT+yaRuuybLjGS9eVBoSR/UPU5fq3OXClEHLJNGvbxKpQ==", + "license": "(MPL-2.0 OR Apache-2.0)", + "engines": { + "node": ">=20" + }, "optionalDependencies": { "@types/trusted-types": "^2.0.7" } @@ -3703,6 +4713,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -3715,12 +4726,14 @@ "node_modules/ds": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/ds/-/ds-1.4.2.tgz", - "integrity": "sha512-d5nMCjfod+srvE/1Bnt/u+L++6N8KJx3ZAi95AGp0g6RtfuGDNlGciWL/iiwKHsFVBVnA3/HEFUq5SW1NgTQ3Q==" + "integrity": "sha512-d5nMCjfod+srvE/1Bnt/u+L++6N8KJx3ZAi95AGp0g6RtfuGDNlGciWL/iiwKHsFVBVnA3/HEFUq5SW1NgTQ3Q==", + "license": "MIT" }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -3733,12 +4746,14 @@ "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" }, "node_modules/editorconfig": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "license": "MIT", "dependencies": { "@one-ini/wasm": "0.1.1", "commander": "^10.0.0", @@ -3752,32 +4767,11 @@ "node": ">=14" } }, - "node_modules/editorconfig/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/editorconfig/node_modules/minimatch": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", - "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/editorconfig/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -3788,12 +4782,14 @@ "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" }, "node_modules/engine.io-client": { "version": "6.6.4", "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.4.tgz", "integrity": "sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw==", + "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", @@ -3802,30 +4798,11 @@ "xmlhttprequest-ssl": "~2.1.1" } }, - "node_modules/engine.io-client/node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/engine.io-parser": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", "engines": { "node": ">=10.0.0" } @@ -3834,6 +4811,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", "engines": { "node": ">=0.12" }, @@ -3845,6 +4823,7 @@ "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" } @@ -3854,6 +4833,7 @@ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", "dev": true, + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", @@ -3921,6 +4901,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -3929,6 +4910,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -3938,6 +4920,7 @@ "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz", "integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", @@ -3960,10 +4943,18 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -3975,6 +4966,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -3990,6 +4982,7 @@ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, + "license": "MIT", "dependencies": { "hasown": "^2.0.2" }, @@ -4002,6 +4995,7 @@ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, + "license": "MIT", "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", @@ -4019,6 +5013,7 @@ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -4058,6 +5053,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -4071,6 +5067,7 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -4126,6 +5123,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "dev": true, + "license": "MIT", "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", @@ -4158,6 +5156,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -4170,6 +5169,7 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -4178,18 +5178,25 @@ } }, "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "version": "2.0.0-next.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", + "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", "dev": true, + "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4199,6 +5206,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -4210,89 +5218,17 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/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" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, + "license": "Apache-2.0", "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint" } }, "node_modules/espree": { @@ -4300,6 +5236,7 @@ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -4317,6 +5254,7 @@ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -4329,6 +5267,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -4341,15 +5280,27 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } @@ -4357,17 +5308,30 @@ "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" }, "node_modules/exenv": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", - "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==" + "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==", + "license": "BSD-3-Clause" + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } }, "node_modules/extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "license": "MIT", "dependencies": { "is-extendable": "^0.1.0" }, @@ -4378,12 +5342,14 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" }, "node_modules/fast-equals": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==", + "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -4392,19 +5358,22 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", "dev": true, + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } @@ -4413,6 +5382,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", "engines": { "node": ">=12.0.0" }, @@ -4428,13 +5398,22 @@ "node_modules/fetch-retry": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/fetch-retry/-/fetch-retry-4.1.1.tgz", - "integrity": "sha512-e6eB7zN6UBSwGVwrbWVH+gdLnkW9WwHhmq2YDK1Sh30pzx1onRVGBvogTlUeWxwTa+L86NYdo4hFkh7O8ZjSnA==" + "integrity": "sha512-e6eB7zN6UBSwGVwrbWVH+gdLnkW9WwHhmq2YDK1Sh30pzx1onRVGBvogTlUeWxwTa+L86NYdo4hFkh7O8ZjSnA==", + "license": "MIT" + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, + "license": "MIT", "dependencies": { "flat-cache": "^3.0.4" }, @@ -4446,6 +5425,7 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.4.0.tgz", "integrity": "sha512-iACCiXeMYOvZqlF1kTiYINzgepRBymz1wwjiuup9u9nayhb6g4fSwiyJ/6adli+EPwrWtpgQAh2PoS7HukEGEg==", + "license": "MIT", "dependencies": { "tslib": "^2.0.3" }, @@ -4457,6 +5437,7 @@ "version": "4.20.10", "resolved": "https://registry.npmjs.org/final-form/-/final-form-4.20.10.tgz", "integrity": "sha512-TL48Pi1oNHeMOHrKv1bCJUrWZDcD3DIG6AGYVNOnyZPr7Bd/pStN0pL+lfzF5BNoj/FclaoiaLenk4XUIFVYng==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.10.0" }, @@ -4471,19 +5452,24 @@ "node_modules/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" }, "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "peer": true, + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", + "locate-path": "^6.0.0", "path-exists": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat-cache": { @@ -4491,6 +5477,7 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, + "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -4504,7 +5491,8 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/follow-redirects": { "version": "1.15.11", @@ -4516,6 +5504,7 @@ "url": "https://github.com/sponsors/RubenVerborgh" } ], + "license": "MIT", "engines": { "node": ">=4.0" }, @@ -4530,6 +5519,7 @@ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, + "license": "MIT", "dependencies": { "is-callable": "^1.2.7" }, @@ -4544,6 +5534,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" @@ -4559,6 +5550,7 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -4580,6 +5572,7 @@ "url": "https://opencollective.com/formik" } ], + "license": "Apache-2.0", "dependencies": { "@types/hoist-non-react-statics": "^3.3.1", "deepmerge": "^2.1.1", @@ -4598,13 +5591,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -4617,6 +5612,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4626,6 +5622,7 @@ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -4645,6 +5642,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4654,6 +5652,7 @@ "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -4662,6 +5661,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -4685,6 +5685,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -4698,6 +5699,7 @@ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -4714,6 +5716,8 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -4729,32 +5733,24 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=10.13.0" } }, "node_modules/global": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "license": "MIT", "peer": true, "dependencies": { "min-document": "^2.19.0", @@ -4766,6 +5762,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -4781,6 +5778,7 @@ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, + "license": "MIT", "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" @@ -4795,12 +5793,14 @@ "node_modules/globrex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==" + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "license": "MIT" }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4812,13 +5812,15 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4831,6 +5833,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -4839,6 +5842,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" }, @@ -4851,6 +5855,7 @@ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, + "license": "MIT", "dependencies": { "dunder-proto": "^1.0.0" }, @@ -4865,6 +5870,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4876,6 +5882,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, @@ -4890,6 +5897,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -4901,6 +5909,7 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", "dependencies": { "react-is": "^16.7.0" } @@ -4909,15 +5918,37 @@ "version": "5.0.3", "resolved": "https://registry.npmjs.org/html-dom-parser/-/html-dom-parser-5.0.3.tgz", "integrity": "sha512-slsc6ipw88OUZjAayRs5NTmfOQCwcUa3hNyk6AdsbQxY09H5Lr1Y3CZ4ZlconMKql3Ga6sWg3HMoUzo7KSItaQ==", + "license": "MIT", "dependencies": { "domhandler": "5.0.3", "htmlparser2": "9.0.0" } }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, "node_modules/html-react-parser": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-4.2.10.tgz", "integrity": "sha512-JyKZVQ+kQ8PdycISwkuLbEEvV/k4hWhU6cb6TT7yGaYwdqA7cPt4VRYXkCZcix2vlQtgDBSMJUmPI2jpNjPGvg==", + "license": "MIT", "dependencies": { "domhandler": "5.0.3", "html-dom-parser": "5.0.3", @@ -4939,6 +5970,7 @@ "url": "https://github.com/sponsors/fb55" } ], + "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", @@ -4946,6 +5978,34 @@ "entities": "^4.5.0" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/hyphenate-style-name": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz", @@ -4957,28 +6017,32 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/immer": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.3.tgz", - "integrity": "sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q==", + "version": "11.1.4", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.4.tgz", + "integrity": "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==", + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" } }, "node_modules/immutable": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", - "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==" + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", + "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", + "license": "MIT" }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -4995,6 +6059,7 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.19" } @@ -5003,6 +6068,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5014,6 +6080,7 @@ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, + "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -5023,23 +6090,27 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" }, "node_modules/inline-style-parser": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.2.tgz", - "integrity": "sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ==" + "integrity": "sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ==", + "license": "MIT" }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", @@ -5053,6 +6124,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", "engines": { "node": ">=12" } @@ -5061,6 +6133,7 @@ "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", "dependencies": { "loose-envify": "^1.0.0" } @@ -5069,6 +6142,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" @@ -5085,6 +6159,7 @@ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -5100,13 +6175,15 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" }, "node_modules/is-async-function": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, + "license": "MIT", "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", @@ -5126,6 +6203,7 @@ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, + "license": "MIT", "dependencies": { "has-bigints": "^1.0.2" }, @@ -5141,6 +6219,7 @@ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" @@ -5155,13 +6234,15 @@ "node_modules/is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5173,6 +6254,7 @@ "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", "dependencies": { "hasown": "^2.0.2" }, @@ -5188,6 +6270,7 @@ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", @@ -5204,6 +6287,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" @@ -5219,6 +6303,7 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5228,6 +6313,7 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "devOptional": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5237,6 +6323,7 @@ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.3" }, @@ -5251,6 +6338,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", "engines": { "node": ">=8" } @@ -5259,6 +6347,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", + "license": "MIT", "peer": true }, "node_modules/is-generator-function": { @@ -5266,6 +6355,7 @@ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", @@ -5285,6 +6375,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "devOptional": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -5295,13 +6386,15 @@ "node_modules/is-hotkey": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.2.0.tgz", - "integrity": "sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==" + "integrity": "sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==", + "license": "MIT" }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5314,6 +6407,7 @@ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5326,6 +6420,7 @@ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" @@ -5342,6 +6437,7 @@ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -5350,14 +6446,23 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", @@ -5376,6 +6481,7 @@ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5388,6 +6494,7 @@ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.3" }, @@ -5403,6 +6510,7 @@ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" @@ -5418,6 +6526,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", @@ -5435,6 +6544,7 @@ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, + "license": "MIT", "dependencies": { "which-typed-array": "^1.1.16" }, @@ -5448,13 +6558,15 @@ "node_modules/is-url": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", - "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==" + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "license": "MIT" }, "node_modules/is-weakmap": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5467,6 +6579,7 @@ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.3" }, @@ -5482,6 +6595,7 @@ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" @@ -5497,6 +6611,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/is-whitespace/-/is-whitespace-0.3.0.tgz", "integrity": "sha512-RydPhl4S6JwAyj0JJjshWJEFG6hNye3pZFBRZaTUfZFwGHxzppNaNOVgQuS/E/SlhrApuMXrpnK1EEIXfdo3Dg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5505,27 +6620,70 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" }, "node_modules/isobject": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", + "license": "MIT", "peer": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/iterator.prototype": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dev": true, + "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", @@ -5542,6 +6700,7 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -5556,6 +6715,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz", "integrity": "sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==", + "license": "MIT", "dependencies": { "cross-fetch": "^3.0.4", "promise-polyfill": "^8.1.3" @@ -5565,6 +6725,7 @@ "version": "1.15.1", "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.1.tgz", "integrity": "sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==", + "license": "MIT", "dependencies": { "config-chain": "^1.1.13", "editorconfig": "^1.0.4", @@ -5585,6 +6746,7 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", "engines": { "node": ">=14" } @@ -5592,13 +6754,15 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -5606,10 +6770,52 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdom": { + "version": "28.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-28.1.0.tgz", + "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@acemir/cssom": "^0.9.31", + "@asamuzakjp/dom-selector": "^6.8.1", + "@bramus/specificity": "^2.4.2", + "@exodus/bytes": "^1.11.0", + "cssstyle": "^6.0.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "parse5": "^8.0.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.0", + "undici": "^7.21.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -5621,24 +6827,28 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/jsonp": { "version": "0.2.1", @@ -5652,6 +6862,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/jsonp-p/-/jsonp-p-2.0.0.tgz", "integrity": "sha512-r4Cx+919EUlt9qBYiEMNySduoEPsIlmv+6N3jgWqF6PPFa3WmzSGodhpUiTT9XG/ofhpnrzl3Tg3f73KjzmBWw==", + "license": "MIT", "dependencies": { "jsonp": "^0.2.0" } @@ -5660,6 +6871,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -5667,13 +6879,15 @@ "node_modules/jsonp/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, + "license": "MIT", "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", @@ -5688,6 +6902,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "license": "MIT", "engines": { "node": ">=18" } @@ -5697,6 +6912,7 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, + "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } @@ -5705,6 +6921,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "license": "MIT", "dependencies": { "is-buffer": "^1.1.5" }, @@ -5717,6 +6934,7 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -5728,18 +6946,23 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" }, "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "peer": true, + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", "dependencies": { - "p-locate": "^4.1.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash": { @@ -5749,67 +6972,79 @@ "license": "MIT" }, "node_modules/lodash-es": { - "version": "4.17.22", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.22.tgz", - "integrity": "sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q==" + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", + "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==", + "license": "MIT" }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" }, "node_modules/lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "license": "MIT" }, "node_modules/lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead." + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", + "license": "MIT" }, "node_modules/lodash.isempty": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", - "integrity": "sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==" + "integrity": "sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==", + "license": "MIT" }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead." + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" }, "node_modules/lodash.isobject": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", - "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==" + "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==", + "license": "MIT" }, "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==" + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" }, "node_modules/lodash.isundefined": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", - "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==" + "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==", + "license": "MIT" }, "node_modules/lodash.kebabcase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", - "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==" + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -5817,18 +7052,92 @@ "loose-envify": "cli.js" } }, + "node_modules/lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", + "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" + } + }, "node_modules/make-array": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/make-array/-/make-array-0.1.2.tgz", "integrity": "sha512-bcFmxgZ+OTaMYJp/w6eifElKTcfum7Gi5H7vQ8KzAf9X6swdxkVuilCaG3ZjXr/qJsQT4JJ2Rq9SDYScWEdu9Q==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/map-or-similar": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/map-or-similar/-/map-or-similar-1.5.0.tgz", "integrity": "sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==", + "license": "MIT", "peer": true }, "node_modules/matchmediaquery": { @@ -5843,25 +7152,36 @@ "node_modules/material-colors": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz", - "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==" + "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==", + "license": "ISC" }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", "engines": { "node": ">= 0.4" } }, + "node_modules/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/memoize-one": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", - "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", + "license": "MIT" }, "node_modules/memoizerific": { "version": "1.11.3", "resolved": "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz", "integrity": "sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==", + "license": "MIT", "peer": true, "dependencies": { "map-or-similar": "^1.5.0" @@ -5871,6 +7191,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -5879,6 +7200,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -5890,6 +7212,7 @@ "version": "2.19.2", "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.2.tgz", "integrity": "sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==", + "license": "MIT", "peer": true, "dependencies": { "dom-walk": "^0.1.0" @@ -5899,35 +7222,41 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^5.0.2" }, "engines": { - "node": "*" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } @@ -5936,6 +7265,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/mix2/-/mix2-1.0.5.tgz", "integrity": "sha512-ybWz7nY+WHBBIyliND5eYaJKzkoa+qXRYNTmVqAxSLlFtL/umT2iv+pmyTu1oU7WNkrirwheqR8d9EaKVz0e5g==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5944,14 +7274,26 @@ "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", "engines": { "node": "*" } }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "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==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/nanoid": { "version": "3.3.11", @@ -5963,6 +7305,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -5974,18 +7317,40 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/node-addon-api": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT", "optional": true }, + "node_modules/node-exports-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", + "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -6001,10 +7366,33 @@ } } }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/nopt": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "license": "ISC", "dependencies": { "abbrev": "^2.0.0" }, @@ -6019,6 +7407,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6027,6 +7416,7 @@ "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -6038,6 +7428,7 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1" @@ -6053,6 +7444,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -6062,6 +7454,7 @@ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -6082,6 +7475,7 @@ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", @@ -6097,6 +7491,7 @@ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -6115,6 +7510,7 @@ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -6128,11 +7524,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, + "license": "ISC", "dependencies": { "wrappy": "1" } @@ -6142,6 +7550,7 @@ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, + "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -6159,6 +7568,7 @@ "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", "dev": true, + "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", @@ -6172,36 +7582,42 @@ } }, "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "peer": true, + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", "dependencies": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=6" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "peer": true, + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", "dependencies": { - "p-limit": "^2.2.0" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", "peer": true, "engines": { "node": ">=6" @@ -6210,12 +7626,14 @@ "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -6227,6 +7645,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -6240,15 +7659,43 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "license": "MIT" }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", "engines": { "node": ">=8" } @@ -6258,6 +7705,7 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6266,6 +7714,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", "engines": { "node": ">=8" } @@ -6273,12 +7722,14 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -6293,25 +7744,36 @@ "node_modules/path-scurry/node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -6324,6 +7786,7 @@ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -6346,6 +7809,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -6360,15 +7824,17 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } }, "node_modules/prettier": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", - "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, + "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -6383,6 +7849,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/pretty/-/pretty-2.0.0.tgz", "integrity": "sha512-G9xUchgTEiNpormdYBl+Pha50gOUovT18IvAe7EYMZ1/f9W/WWMPRn+xI68yXNMUk3QXHDwo/1wV/4NejVNe1w==", + "license": "MIT", "dependencies": { "condense-newlines": "^0.2.1", "extend-shallow": "^2.0.1", @@ -6392,10 +7859,49 @@ "node": ">=0.10.0" } }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/prism-react-renderer": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz", "integrity": "sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg==", + "license": "MIT", "peerDependencies": { "react": ">=0.14.9" } @@ -6404,6 +7910,7 @@ "version": "1.30.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", "engines": { "node": ">=6" } @@ -6412,6 +7919,7 @@ "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", "peer": true, "engines": { "node": ">= 0.6.0" @@ -6420,12 +7928,14 @@ "node_modules/promise-polyfill": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.3.0.tgz", - "integrity": "sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==" + "integrity": "sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==", + "license": "MIT" }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -6435,31 +7945,36 @@ "node_modules/property-expr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", - "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==" + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==", + "license": "MIT" }, "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==" + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "license": "ISC" }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/qs": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", - "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "license": "BSD-3-Clause", "peer": true, "dependencies": { "side-channel": "^1.1.0" @@ -6489,17 +8004,20 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/raf-schd": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", - "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" + "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==", + "license": "MIT" }, "node_modules/re-resizable": { "version": "6.11.2", "resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.11.2.tgz", "integrity": "sha512-2xI2P3OHs5qw7K0Ud1aLILK6MQxW50TcO+DetD9eIV58j84TqYeHoZcL9H4GXFXXIh7afhH8mv5iUCXII7OW7A==", + "license": "MIT", "peerDependencies": { "react": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -6509,11 +8027,29 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" }, - "engines": { - "node": ">=0.10.0" + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-ace": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/react-ace/-/react-ace-9.5.0.tgz", + "integrity": "sha512-4l5FgwGh6K7A0yWVMQlPIXDItM4Q9zzXRqOae8KkCl6MkOob7sC1CzHxZdOGvV+QioKWbX2p5HcdOVUv6cAdSg==", + "license": "MIT", + "dependencies": { + "ace-builds": "^1.4.13", + "diff-match-patch": "^1.0.5", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0", + "react-dom": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0" } }, "node_modules/react-beautiful-dnd": { @@ -6521,6 +8057,7 @@ "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz", "integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==", "deprecated": "react-beautiful-dnd is now deprecated. Context and options: https://github.com/atlassian/react-beautiful-dnd/issues/2672", + "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.9.2", "css-box-model": "^1.2.0", @@ -6538,12 +8075,14 @@ "node_modules/react-beautiful-dnd/node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "license": "MIT" }, "node_modules/react-beautiful-dnd/node_modules/react-redux": { "version": "7.2.9", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.15.4", "@types/react-redux": "^7.1.20", @@ -6568,6 +8107,7 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.9.2" } @@ -6576,6 +8116,7 @@ "version": "2.19.3", "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz", "integrity": "sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==", + "license": "MIT", "dependencies": { "@icons/material": "^0.2.4", "lodash": "^4.17.15", @@ -6594,6 +8135,7 @@ "version": "4.25.7", "resolved": "https://registry.npmjs.org/@contentstack/react-datepicker/-/react-datepicker-4.25.7.tgz", "integrity": "sha512-wNcKiFBgmOzp/sLRB/7YR/jeDTCRfY3leTACR14LaJmiX944HbjqC2i0wEBQLHKacPT6H2GRNX2z9uPAhgw/dg==", + "license": "MIT", "dependencies": { "@popperjs/core": "^2.11.8", "classnames": "^2.2.6", @@ -6611,6 +8153,7 @@ "version": "11.1.3", "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-11.1.3.tgz", "integrity": "sha512-8rtzzT8iwHgdSC89VktwhqdKKtfXaAyC4wiqp0SywpHG12TTLvfOoL6xNEIUWXwIEWu+CFfDn4GZJyynCEuHIQ==", + "license": "MIT", "dependencies": { "@react-dnd/shallowequal": "^2.0.0", "@types/hoist-non-react-statics": "^3.3.1", @@ -6626,6 +8169,7 @@ "version": "11.1.3", "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-11.1.3.tgz", "integrity": "sha512-/1FjNlJbW/ivkUxlxQd7o3trA5DE33QiRZgxent3zKme8DwF4Nbw3OFVhTRFGaYhHFNL1rZt6Rdj1D78BjnNLw==", + "license": "MIT", "dependencies": { "dnd-core": "^11.1.3" } @@ -6634,6 +8178,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -6646,6 +8191,7 @@ "version": "11.7.1", "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-11.7.1.tgz", "integrity": "sha512-zxCMwhfPy1olUEbw3FLNPLhAm/HnaYH5aELIEglRbqabizKAdHs0h+WuyOpmA+v1JXn0++fpQDdNfUagWt5hJQ==", + "license": "MIT", "dependencies": { "attr-accept": "^2.2.2", "file-selector": "^0.4.0", @@ -6662,6 +8208,7 @@ "version": "3.1.4", "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz", "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5" }, @@ -6676,12 +8223,14 @@ "node_modules/react-fast-compare": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", - "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" + "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==", + "license": "MIT" }, "node_modules/react-final-form": { "version": "6.5.9", "resolved": "https://registry.npmjs.org/react-final-form/-/react-final-form-6.5.9.tgz", "integrity": "sha512-x3XYvozolECp3nIjly+4QqxdjSSWfcnpGEL5K8OBT6xmGrq5kBqbA6+/tOqoom9NwqIPPbxPNsOViFlbKgowbA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.15.4" }, @@ -6698,6 +8247,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", + "license": "MIT", "dependencies": { "object-assign": "^4.1.1", "prop-types": "^15.7.2", @@ -6711,12 +8261,14 @@ "node_modules/react-helmet/node_modules/react-fast-compare": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", - "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "license": "MIT" }, "node_modules/react-infinite-scroll-component": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/react-infinite-scroll-component/-/react-infinite-scroll-component-6.1.1.tgz", "integrity": "sha512-R8YoOyiNDynSWmfVme5LHslsKrP+/xcRUWR2ies8UgUab9dtyw5ECnMCVPPmnmjjF4MWQmfVdRwRWcWaDgeyMA==", + "license": "MIT", "dependencies": { "throttle-debounce": "^2.1.0" }, @@ -6724,10 +8276,23 @@ "react": ">=16.0.0" } }, + "node_modules/react-input-autosize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-3.0.0.tgz", + "integrity": "sha512-nL9uS7jEs/zu8sqwFE5MAPx6pPkNAriACQ2rGLlqmKr2sPGtN7TXTyDdQt4lbNXVx7Uzadb40x8qotIuru6Rhg==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.5.8" + }, + "peerDependencies": { + "react": "^16.3.0 || ^17.0.0" + } + }, "node_modules/react-input-mask": { "version": "3.0.0-alpha.2", "resolved": "https://registry.npmjs.org/react-input-mask/-/react-input-mask-3.0.0-alpha.2.tgz", "integrity": "sha512-9U7qL+mvDMOJcbOFPdt6Vj+zzmCMNnBjhhjGDrL8BGQmymgvMVKhu/oOVfAkl+5VWOsLr+G3EhZOmae5fBcAkA==", + "license": "MIT", "dependencies": { "invariant": "^2.2.4", "prop-types": "^15.7.2", @@ -6741,17 +8306,30 @@ "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-is-mounted-hook": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/react-is-mounted-hook/-/react-is-mounted-hook-1.1.2.tgz", + "integrity": "sha512-yjq3Tj34CiFcdVOS/h6JerWLOLdJqEGKMNpTHc4kWebzz2YtIpgqMRrqxdmQhewM1KJREojdAV2tsNvBsUWyhA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.6 || ^17", + "react-dom": "^16.8.6 || ^17" + } }, "node_modules/react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "license": "MIT" }, "node_modules/react-mentions": { "version": "4.4.10", "resolved": "https://registry.npmjs.org/react-mentions/-/react-mentions-4.4.10.tgz", "integrity": "sha512-JHiQlgF1oSZR7VYPjq32wy97z1w1oE4x10EuhKjPr4WUKhVzG1uFQhQjKqjQkbVqJrmahf+ldgBTv36NrkpKpA==", + "license": "BSD-3-Clause", "dependencies": { "@babel/runtime": "7.4.5", "invariant": "^2.2.4", @@ -6763,19 +8341,11 @@ "react-dom": ">=16.8.3" } }, - "node_modules/react-mentions/node_modules/@babel/runtime": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz", - "integrity": "sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.13.2" - } - }, "node_modules/react-modal": { "version": "3.16.3", "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.3.tgz", "integrity": "sha512-yCYRJB5YkeQDQlTt17WGAgFJ7jr2QYcWa1SHqZ3PluDmnKJ/7+tVU+E6uKyZ0nODaeEj+xCpK4LcSnKXLMC0Nw==", + "license": "MIT", "dependencies": { "exenv": "^1.2.0", "prop-types": "^15.7.2", @@ -6791,6 +8361,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==", + "license": "MIT", "dependencies": { "react-fast-compare": "^3.0.1", "warning": "^4.0.2" @@ -6804,17 +8375,20 @@ "node_modules/react-popper/node_modules/react-fast-compare": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", - "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "license": "MIT" }, "node_modules/react-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.2.tgz", - "integrity": "sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==" + "integrity": "sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==", + "license": "MIT" }, "node_modules/react-redux": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", + "license": "MIT", "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" @@ -6852,9 +8426,10 @@ } }, "node_modules/react-router": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.12.0.tgz", - "integrity": "sha512-kTPDYPFzDVGIIGNLS5VJykK0HfHLY5MF3b+xj0/tTyNYL1gF1qs7u67Z9jEhQk2sQ98SUaHxlG31g1JtF7IfVw==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.0.tgz", + "integrity": "sha512-PZgus8ETambRT17BUm/LL8lX3Of+oiLaPuVTRH3l1eLvSPpKO3AvhAEb5N7ihAFZQrYDqkvvWfFh9p0z9VsjLw==", + "license": "MIT", "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" @@ -6873,11 +8448,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.12.0.tgz", - "integrity": "sha512-pfO9fiBcpEfX4Tx+iTYKDtPbrSLLCbwJ5EqP+SPYQu1VYCXdy79GSj0wttR0U4cikVdlImZuEZ/9ZNCgoaxwBA==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.0.tgz", + "integrity": "sha512-5CO/l5Yahi2SKC6rGZ+HDEjpjkGaG/ncEP7eWFTvFxbHP8yeeI0PxTDjimtpXYlR3b3i9/WIL4VJttPrESIf2g==", + "license": "MIT", "dependencies": { - "react-router": "7.12.0" + "react-router": "7.13.0" }, "engines": { "node": ">=20.0.0" @@ -6887,10 +8463,143 @@ "react-dom": ">=18" } }, + "node_modules/react-select": { + "name": "@contentstack/react-select", + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@contentstack/react-select/-/react-select-3.2.8.tgz", + "integrity": "sha512-ffc5PKxXgOS0oc1UDUIhZNFZ+XNymw9sk9USRaemzFlCEI5ywl68uJRICiRySO1YMxZIN96aG0cUXTXKfXE4qg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "7.26.10", + "@emotion/cache": "10.0.9", + "@emotion/core": "10.0.9", + "@emotion/css": "10.0.9", + "memoize-one": "5.0.0", + "react-input-autosize": "3.0.0", + "react-transition-group": "4.3.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + } + }, + "node_modules/react-select-async-paginate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/react-select-async-paginate/-/react-select-async-paginate-0.4.1.tgz", + "integrity": "sha512-zWeaN9C9PVQej4bz1+OvU6/ylHE6rHscDYcP+KiWdBedVQ5j2vXBjf/5RWLEvobvtUUHBOTbUF8+m2HDoeIcvQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.2", + "@seznam/compose-react-refs": "^1.0.4", + "react-is-mounted-hook": "^1.0.3", + "sleep-promise": "^8.0.1" + }, + "peerDependencies": { + "react": "^16.8.0", + "react-select": "^2.0.0 || ^3.0.0" + } + }, + "node_modules/react-select/node_modules/@emotion/cache": { + "version": "10.0.9", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.9.tgz", + "integrity": "sha512-f7MblpE2xoimC4fCMZ9pivmsIn7hyWRIvY75owMDi8pdOSeh+w5tH3r4hBJv/LLrwiMM7cTQURqTPcYoL5pWnw==", + "license": "MIT", + "dependencies": { + "@emotion/sheet": "0.9.2", + "@emotion/stylis": "0.8.3", + "@emotion/utils": "0.11.1", + "@emotion/weak-memoize": "0.2.2" + } + }, + "node_modules/react-select/node_modules/@emotion/css": { + "version": "10.0.9", + "resolved": "https://registry.npmjs.org/@emotion/css/-/css-10.0.9.tgz", + "integrity": "sha512-jtHhUSWw+L7yxYgNtC+KJ3Ory90/jiAtpG1qT+gTQQ/RR5AMiigs9/lDHu/vnwljaq2S48FoKb/FZZMlJcC4bw==", + "license": "MIT", + "dependencies": { + "@emotion/serialize": "^0.11.6", + "@emotion/utils": "0.11.1", + "babel-plugin-emotion": "^10.0.9" + } + }, + "node_modules/react-select/node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "license": "MIT" + }, + "node_modules/react-select/node_modules/@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "license": "MIT" + }, + "node_modules/react-select/node_modules/@emotion/serialize": { + "version": "0.11.16", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.16.tgz", + "integrity": "sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "0.8.0", + "@emotion/memoize": "0.7.4", + "@emotion/unitless": "0.7.5", + "@emotion/utils": "0.11.3", + "csstype": "^2.5.7" + } + }, + "node_modules/react-select/node_modules/@emotion/serialize/node_modules/@emotion/utils": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz", + "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==", + "license": "MIT" + }, + "node_modules/react-select/node_modules/@emotion/sheet": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.2.tgz", + "integrity": "sha512-pVBLzIbC/QCHDKJF2E82V2H/W/B004mDFQZiyo/MSR+VC4pV5JLG0TF/zgQDFvP3fZL/5RTPGEmXlYJBMUuJ+A==", + "license": "MIT" + }, + "node_modules/react-select/node_modules/@emotion/stylis": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.3.tgz", + "integrity": "sha512-M3nMfJ6ndJMYloSIbYEBq6G3eqoYD41BpDOxreE8j0cb4fzz/5qvmqU9Mb2hzsXcCnIlGlWhS03PCzVGvTAe0Q==", + "license": "MIT" + }, + "node_modules/react-select/node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "license": "MIT" + }, + "node_modules/react-select/node_modules/@emotion/utils": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.1.tgz", + "integrity": "sha512-8M3VN0hetwhsJ8dH8VkVy7xo5/1VoBsDOk/T4SJOeXwTO1c4uIqVNx2qyecLFnnUWD5vvUqHQ1gASSeUN6zcTg==", + "license": "MIT" + }, + "node_modules/react-select/node_modules/@emotion/weak-memoize": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.2.tgz", + "integrity": "sha512-n/VQ4mbfr81aqkx/XmVicOLjviMuy02eenSdJY33SVA7S2J42EU0P1H0mOogfYedb3wXA0d/LVtBrgTSm04WEA==", + "license": "MIT" + }, + "node_modules/react-select/node_modules/csstype": { + "version": "2.6.21", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==", + "license": "MIT" + }, + "node_modules/react-select/node_modules/memoize-one": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.0.0.tgz", + "integrity": "sha512-7g0+ejkOaI9w5x6LvQwmj68kUj6rxROywPSCqmclG/HBacmFnZqhVscQ8kovkn9FBCNJmOz6SY42+jnvZzDWdw==", + "license": "MIT" + }, "node_modules/react-shallow-renderer": { "version": "16.15.0", "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz", "integrity": "sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==", + "license": "MIT", "dependencies": { "object-assign": "^4.1.1", "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0" @@ -6903,6 +8612,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz", "integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==", + "license": "MIT", "peerDependencies": { "react": "^16.3.0 || ^17.0.0 || ^18.0.0" } @@ -6911,6 +8621,7 @@ "version": "0.11.3", "resolved": "https://registry.npmjs.org/react-simple-code-editor/-/react-simple-code-editor-0.11.3.tgz", "integrity": "sha512-7bVI4Yd1aNCeuldErXUt8ksaAG5Fi+GZ6vp3mtFBnckKdzsQtrgkDvdwMFXIhwTGG+mUYmk5ZpMo0axSW9JBzA==", + "license": "MIT", "peerDependencies": { "react": "*", "react-dom": "*" @@ -6920,6 +8631,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", + "license": "MIT", "dependencies": { "fast-equals": "^5.0.1", "prop-types": "^15.8.1", @@ -6930,10 +8642,43 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/react-smooth/node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/react-sortable-hoc": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/react-sortable-hoc/-/react-sortable-hoc-1.11.0.tgz", + "integrity": "sha512-v1CDCvdfoR3zLGNp6qsBa4J1BWMEVH25+UKxF/RvQRh+mrB+emqtVHMgZ+WreUiKJoEaiwYoScaueIKhMVBHUg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.2.0", + "invariant": "^2.2.4", + "prop-types": "^15.5.7" + }, + "peerDependencies": { + "prop-types": "^15.5.7", + "react": "^0.14.0 || ^15.0.0 || ^16.0.0", + "react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0" + } + }, "node_modules/react-style-tag": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/react-style-tag/-/react-style-tag-3.0.1.tgz", "integrity": "sha512-XeiiTQtG04uT+Lrtr0YvA5I2qcbUzHMCtS40iH3/bzP98xhPUm1qzRUOoOXgMfyJRPwHh36MH8ebj1C7nuaolg==", + "license": "MIT", "dependencies": { "stylis": "^4.1.1" }, @@ -6946,6 +8691,7 @@ "version": "7.8.0", "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz", "integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" @@ -6958,6 +8704,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-18.3.1.tgz", "integrity": "sha512-KkAgygexHUkQqtvvx/otwxtuFu5cVjfzTCtjXLH9boS19/Nbtg84zS7wIQn39G8IlrhThBpQsMKkq5ZHZIYFXA==", + "license": "MIT", "dependencies": { "react-is": "^18.3.1", "react-shallow-renderer": "^16.15.0", @@ -6970,12 +8717,14 @@ "node_modules/react-test-renderer/node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" }, "node_modules/react-tiktok": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/react-tiktok/-/react-tiktok-1.0.0.tgz", "integrity": "sha512-t7CDkzEI6CWTxuqW7NJGF4u4N+21qvA1NGn06+kOzWDphueoAFg+BdxEKz0jtyzJYB+2nvuoPHuPK4cYdLIXvg==", + "license": "MIT", "dependencies": { "fetch-retry": "^4.0.1", "react-helmet": "^6.1.0" @@ -6990,6 +8739,7 @@ "version": "6.1.5", "resolved": "https://registry.npmjs.org/@contentstack/react-toastify/-/react-toastify-6.1.5.tgz", "integrity": "sha512-ibY34/1kdHytblf7PRI7QBSPaUV31bVP/WPVJMqTdpjDX1AEz+ZczbFBb9l3modcBMPBos+bDgbMsuOkiK00dw==", + "license": "MIT", "dependencies": { "clsx": "^1.1.1", "prop-types": "^15.7.2", @@ -6999,10 +8749,27 @@ "react": ">=16" } }, - "node_modules/react-transition-group": { + "node_modules/react-toastify/node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.3.0.tgz", + "integrity": "sha512-1qRV1ZuVSdxPlPf4O8t7inxUGpdyO5zG9IoNfJxSO0ImU2A1YWkEQvFPuIPZmMLkg5hYs7vv5mMOyfgSkvAwvw==", + "license": "BSD-3-Clause", "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", @@ -7018,6 +8785,7 @@ "version": "3.2.4", "resolved": "https://registry.npmjs.org/react-treebeard/-/react-treebeard-3.2.4.tgz", "integrity": "sha512-TsvdUq2kbLavRXa8k4mmqfPse8HmSA9G9s1SZUtIpiYSccSwa0Tm6miMgx7DZ5gpKofQ+j/3Ua0rjsahM3/FQg==", + "license": "MIT", "dependencies": { "@emotion/core": "^10.0.10", "@emotion/styled": "^10.0.10", @@ -7033,48 +8801,106 @@ "react-dom": ">=16.7.0" } }, - "node_modules/react-treebeard/node_modules/dom-helpers": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", - "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "node_modules/react-treebeard/node_modules/@emotion/cache": { + "version": "10.0.29", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz", + "integrity": "sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.1.2" + "@emotion/sheet": "0.9.4", + "@emotion/stylis": "0.8.5", + "@emotion/utils": "0.11.3", + "@emotion/weak-memoize": "0.2.5" } }, - "node_modules/react-treebeard/node_modules/react-transition-group": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", - "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "node_modules/react-treebeard/node_modules/@emotion/core": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.3.1.tgz", + "integrity": "sha512-447aUEjPIm0MnE6QYIaFz9VQOHSXf4Iu6EWOIqq11EAPqinkSZmfymPTmlOE3QjLv846lH4JVZBUOtwGbuQoww==", + "license": "MIT", "dependencies": { - "dom-helpers": "^3.4.0", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2", - "react-lifecycles-compat": "^3.0.4" + "@babel/runtime": "^7.5.5", + "@emotion/cache": "^10.0.27", + "@emotion/css": "^10.0.27", + "@emotion/serialize": "^0.11.15", + "@emotion/sheet": "0.9.4", + "@emotion/utils": "0.11.3" }, "peerDependencies": { - "react": ">=15.0.0", - "react-dom": ">=15.0.0" + "react": ">=16.3.0" + } + }, + "node_modules/react-treebeard/node_modules/@emotion/css": { + "version": "10.0.27", + "resolved": "https://registry.npmjs.org/@emotion/css/-/css-10.0.27.tgz", + "integrity": "sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==", + "license": "MIT", + "dependencies": { + "@emotion/serialize": "^0.11.15", + "@emotion/utils": "0.11.3", + "babel-plugin-emotion": "^10.0.27" } }, - "node_modules/react-treebeard/node_modules/velocity-react": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/velocity-react/-/velocity-react-1.4.3.tgz", - "integrity": "sha512-zvefGm85A88S3KdF9/dz5vqyFLAiwKYlXGYkHH2EbXl+CZUD1OT0a0aS1tkX/WXWTa/FUYqjBaAzAEFYuSobBQ==", + "node_modules/react-treebeard/node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "license": "MIT" + }, + "node_modules/react-treebeard/node_modules/@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "license": "MIT" + }, + "node_modules/react-treebeard/node_modules/@emotion/serialize": { + "version": "0.11.16", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.16.tgz", + "integrity": "sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg==", + "license": "MIT", "dependencies": { - "lodash": "^4.17.5", - "prop-types": "^15.5.8", - "react-transition-group": "^2.0.0", - "velocity-animate": "^1.4.0" - }, - "peerDependencies": { - "react": "^15.3.0 || ^16.0.0", - "react-dom": "^15.3.0 || ^16.0.0" + "@emotion/hash": "0.8.0", + "@emotion/memoize": "0.7.4", + "@emotion/unitless": "0.7.5", + "@emotion/utils": "0.11.3", + "csstype": "^2.5.7" } }, + "node_modules/react-treebeard/node_modules/@emotion/sheet": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz", + "integrity": "sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA==", + "license": "MIT" + }, + "node_modules/react-treebeard/node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "license": "MIT" + }, + "node_modules/react-treebeard/node_modules/@emotion/utils": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz", + "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==", + "license": "MIT" + }, + "node_modules/react-treebeard/node_modules/@emotion/weak-memoize": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", + "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==", + "license": "MIT" + }, + "node_modules/react-treebeard/node_modules/csstype": { + "version": "2.6.21", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==", + "license": "MIT" + }, "node_modules/react-virtualized": { "version": "9.22.6", "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.22.6.tgz", "integrity": "sha512-U5j7KuUQt3AaMatlMJ0UJddqSiX+Km0YJxSqbAzIiGw5EmNz0khMyqP2hzgu4+QUtm+QPIrxzUX4raJxmVJnHg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.7.2", "clsx": "^1.0.4", @@ -7088,10 +8914,24 @@ "react-dom": "^16.3.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/react-virtualized-auto-sizer": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.5.tgz", + "integrity": "sha512-kivjYVWX15TX2IUrm8F1jaCEX8EXrpy3DD+u41WGqJ1ZqbljWpiwscV+VxOM1l7sSIM1jwi2LADjhhAJkJ9dxA==", + "license": "MIT", + "engines": { + "node": ">8.0.0" + }, + "peerDependencies": { + "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0", + "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0" + } + }, "node_modules/react-window": { "version": "1.8.11", "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.11.tgz", "integrity": "sha512-+SRbUVT2scadgFSWx+R1P754xHPEqvcfSfVX10QYg6POOz+WNgkN48pS+BtZNIMGiL1HYrSEiCkwsMS15QogEQ==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.0.0", "memoize-one": ">=3.1.1 <6" @@ -7108,6 +8948,7 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/react-window-infinite-loader/-/react-window-infinite-loader-1.0.10.tgz", "integrity": "sha512-NO/csdHlxjWqA2RJZfzQgagAjGHspbO2ik9GtWZb0BY1Nnapq0auG8ErI+OhGCzpjYJsCYerqUlK6hkq9dfAAA==", + "license": "MIT", "engines": { "node": ">8.0.0" }, @@ -7120,14 +8961,47 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz", "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==", + "license": "MIT", "dependencies": { "lodash": "^4.0.1" } }, + "node_modules/reactjs-social-embed": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/reactjs-social-embed/-/reactjs-social-embed-1.2.0.tgz", + "integrity": "sha512-Avi6yyQJ5rbLmAzLkJJHBL+86+3if40QGgOUEu0/GxpARWyMd6w4R7C4AAufQqGC2x7V+HFpMkUovdk4/HjHSg==", + "license": "MIT", + "dependencies": { + "jsonp-p": "^2.0.0" + }, + "engines": { + "node": ">=8", + "npm": ">=5" + }, + "peerDependencies": { + "prop-types": "^15.5.4", + "react": "^15.0.0 || ^16.0.0", + "react-dom": "^15.0.0 || ^16.0.0" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/recharts": { "version": "2.15.4", "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", + "license": "MIT", "dependencies": { "clsx": "^2.0.0", "eventemitter3": "^4.0.1", @@ -7150,6 +9024,7 @@ "version": "0.4.5", "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "license": "MIT", "dependencies": { "decimal.js-light": "^2.4.1" } @@ -7158,6 +9033,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", "engines": { "node": ">=6" } @@ -7165,12 +9041,14 @@ "node_modules/recharts/node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, "license": "MIT", "dependencies": { "indent-string": "^4.0.0", @@ -7183,12 +9061,14 @@ "node_modules/redux": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", - "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==" + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" }, "node_modules/redux-persist": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz", "integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==", + "license": "MIT", "peerDependencies": { "redux": ">4.0.0" } @@ -7197,6 +9077,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", "peerDependencies": { "redux": "^5.0.0" } @@ -7206,6 +9087,7 @@ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -7226,12 +9108,15 @@ "node_modules/regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT", + "peer": true }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -7247,20 +9132,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/reselect": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", - "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==" + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" }, "node_modules/resize-observer-polyfill": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", - "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "license": "MIT" }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "license": "MIT", "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", @@ -7280,6 +9178,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", "engines": { "node": ">=4" } @@ -7289,6 +9188,7 @@ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, + "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -7300,6 +9200,7 @@ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, + "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -7314,8 +9215,9 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -7332,9 +9234,10 @@ } }, "node_modules/rollup": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", - "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "license": "MIT", "dependencies": { "@types/estree": "1.0.8" }, @@ -7346,38 +9249,39 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.57.1", - "@rollup/rollup-android-arm64": "4.57.1", - "@rollup/rollup-darwin-arm64": "4.57.1", - "@rollup/rollup-darwin-x64": "4.57.1", - "@rollup/rollup-freebsd-arm64": "4.57.1", - "@rollup/rollup-freebsd-x64": "4.57.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", - "@rollup/rollup-linux-arm-musleabihf": "4.57.1", - "@rollup/rollup-linux-arm64-gnu": "4.57.1", - "@rollup/rollup-linux-arm64-musl": "4.57.1", - "@rollup/rollup-linux-loong64-gnu": "4.57.1", - "@rollup/rollup-linux-loong64-musl": "4.57.1", - "@rollup/rollup-linux-ppc64-gnu": "4.57.1", - "@rollup/rollup-linux-ppc64-musl": "4.57.1", - "@rollup/rollup-linux-riscv64-gnu": "4.57.1", - "@rollup/rollup-linux-riscv64-musl": "4.57.1", - "@rollup/rollup-linux-s390x-gnu": "4.57.1", - "@rollup/rollup-linux-x64-gnu": "4.57.1", - "@rollup/rollup-linux-x64-musl": "4.57.1", - "@rollup/rollup-openbsd-x64": "4.57.1", - "@rollup/rollup-openharmony-arm64": "4.57.1", - "@rollup/rollup-win32-arm64-msvc": "4.57.1", - "@rollup/rollup-win32-ia32-msvc": "4.57.1", - "@rollup/rollup-win32-x64-gnu": "4.57.1", - "@rollup/rollup-win32-x64-msvc": "4.57.1", + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" } }, "node_modules/rtl-detect": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.1.2.tgz", - "integrity": "sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ==" + "integrity": "sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ==", + "license": "BSD-3-Clause" }, "node_modules/run-parallel": { "version": "1.2.0", @@ -7398,6 +9302,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } @@ -7407,6 +9312,7 @@ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", @@ -7426,6 +9332,7 @@ "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" @@ -7441,6 +9348,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -7454,9 +9362,10 @@ } }, "node_modules/sass": { - "version": "1.97.2", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.2.tgz", - "integrity": "sha512-y5LWb0IlbO4e97Zr7c3mlpabcbBtS+ieiZ9iwDooShpFKWXf62zz5pEPdwrLYm+Bxn1fnbwFGzHuCLSA9tBmrw==", + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz", + "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", + "license": "MIT", "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -7472,36 +9381,24 @@ "@parcel/watcher": "^2.4.1" } }, - "node_modules/sass/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" + "xmlchars": "^2.2.0" }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/sass/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" + "node": ">=v12.22.7" } }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" } @@ -7510,6 +9407,7 @@ "version": "2.2.31", "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz", "integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==", + "license": "MIT", "dependencies": { "compute-scroll-into-view": "^1.0.20" } @@ -7519,6 +9417,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -7526,12 +9425,14 @@ "node_modules/set-cookie-parser": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", - "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==" + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" }, "node_modules/set-function-length": { "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==", + "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -7548,6 +9449,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -7563,6 +9465,7 @@ "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", "dev": true, + "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", @@ -7581,12 +9484,14 @@ "node_modules/shallowequal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "license": "MIT" }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -7598,6 +9503,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", "engines": { "node": ">=8" } @@ -7606,6 +9512,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", @@ -7624,6 +9531,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" @@ -7639,6 +9547,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -7656,6 +9565,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -7670,10 +9580,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", "engines": { "node": ">=14" }, @@ -7681,10 +9599,26 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/skema": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/skema/-/skema-1.0.2.tgz", "integrity": "sha512-5LWfF2RSW2B3xfOaY6j49X8aNwsnj9cRVrM5QMF7it+cZvpv5ufiOUT13ps2U52sIbAzs11bdRP6mi5qyg75VQ==", + "license": "MIT", "dependencies": { "async": "^0.9.0", "make-array": "^0.1.2", @@ -7695,6 +9629,7 @@ "version": "0.103.0", "resolved": "https://registry.npmjs.org/slate/-/slate-0.103.0.tgz", "integrity": "sha512-eCUOVqUpADYMZ59O37QQvUdnFG+8rin0OGQAXNHvHbQeVJ67Bu0spQbcy621vtf8GQUXTEQBlk6OP9atwwob4w==", + "license": "MIT", "dependencies": { "immer": "^10.0.3", "is-plain-object": "^5.0.0", @@ -7705,6 +9640,7 @@ "version": "0.66.0", "resolved": "https://registry.npmjs.org/slate-history/-/slate-history-0.66.0.tgz", "integrity": "sha512-6MWpxGQZiMvSINlCbMW43E2YBSVMCMCIwQfBzGssjWw4kb0qfvj0pIdblWNRQZD0hR6WHP+dHHgGSeVdMWzfng==", + "license": "MIT", "dependencies": { "is-plain-object": "^5.0.0" }, @@ -7716,6 +9652,7 @@ "version": "0.77.0", "resolved": "https://registry.npmjs.org/slate-hyperscript/-/slate-hyperscript-0.77.0.tgz", "integrity": "sha512-M6uRpttwKnosniQORNPYQABHQ9XWC7qaSr/127LWWPjTOR5MSSwrHGrghN81BhZVqpICHrI7jkPA2813cWdHNA==", + "license": "MIT", "dependencies": { "is-plain-object": "^5.0.0" }, @@ -7727,6 +9664,7 @@ "version": "0.77.4", "resolved": "https://registry.npmjs.org/slate-react/-/slate-react-0.77.4.tgz", "integrity": "sha512-e3gYuEhjbEX4IhydC4kWZgcvH5fgJkJMuu9paarO6gdW5vRRSWnys/EbOs5ALp4e9NWr5u1XzQkJnbpyW5AqBA==", + "license": "MIT", "dependencies": { "@types/is-hotkey": "^0.1.1", "@types/lodash": "^4.14.149", @@ -7746,17 +9684,20 @@ "node_modules/slate-react/node_modules/is-hotkey": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.1.8.tgz", - "integrity": "sha512-qs3NZ1INIS+H+yeo7cD9pDfwYV/jqRh1JG9S9zYrNudkoUQg7OL7ziXqRKu+InFjUIDoP2o6HIkLYMh1pcWgyQ==" + "integrity": "sha512-qs3NZ1INIS+H+yeo7cD9pDfwYV/jqRh1JG9S9zYrNudkoUQg7OL7ziXqRKu+InFjUIDoP2o6HIkLYMh1pcWgyQ==", + "license": "MIT" }, "node_modules/slate-react/node_modules/tiny-invariant": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.6.tgz", - "integrity": "sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA==" + "integrity": "sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA==", + "license": "MIT" }, "node_modules/slate/node_modules/immer": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz", "integrity": "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==", + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" @@ -7765,12 +9706,14 @@ "node_modules/sleep-promise": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/sleep-promise/-/sleep-promise-8.0.1.tgz", - "integrity": "sha512-nfwyX+G1dsx2R1DMMKWLpNxuHMOCL7JIRBUw0fl7Z4nZ1YZK0apZuGY8MDexn0HDZzgbERgj/CrNtsYpo/B7eA==" + "integrity": "sha512-nfwyX+G1dsx2R1DMMKWLpNxuHMOCL7JIRBUw0fl7Z4nZ1YZK0apZuGY8MDexn0HDZzgbERgj/CrNtsYpo/B7eA==", + "license": "MIT" }, "node_modules/socket.io-client": { "version": "4.8.3", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz", "integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==", + "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", @@ -7785,6 +9728,7 @@ "version": "4.2.5", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.5.tgz", "integrity": "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ==", + "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1" @@ -7797,6 +9741,7 @@ "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -7805,15 +9750,31 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" @@ -7826,12 +9787,37 @@ "version": "2.14.4", "resolved": "https://registry.npmjs.org/store2/-/store2-2.14.4.tgz", "integrity": "sha512-srTItn1GOvyvOycgxjAnPA63FZNwy0PTyUBFMHRM+hVFltAeoh0LmNBz9SZqUS9mMqGk8rfyWyXn3GH5ReJ8Zw==", + "license": "MIT", "peer": true }, + "node_modules/storybook-addon-whats-new": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/storybook-addon-whats-new/-/storybook-addon-whats-new-1.0.3.tgz", + "integrity": "sha512-5je0qJh2ahPbbS2pwOkgKKmUDJAT0aAn3cSmap0Hp3hzFlu4uaDPiY00T4mPF3BsavBrkPNmy7kKIHTXnmRu5A==", + "license": "MIT", + "peerDependencies": { + "@storybook/addons": "^6.4.0", + "@storybook/api": "^6.4.0", + "@storybook/components": "^6.4.0", + "@storybook/core-events": "^6.4.0", + "@storybook/theming": "^6.4.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -7849,6 +9835,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -7861,12 +9848,14 @@ "node_modules/string-width-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" }, "node_modules/string-width/node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -7878,6 +9867,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -7893,6 +9883,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -7920,6 +9911,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", "dev": true, + "license": "MIT", "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" @@ -7930,6 +9922,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", @@ -7951,6 +9944,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", @@ -7969,6 +9963,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -7985,6 +9980,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -7997,6 +9993,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -8008,6 +10005,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, "license": "MIT", "dependencies": { "min-indent": "^1.0.0" @@ -8021,6 +10019,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -8032,6 +10031,7 @@ "version": "1.1.8", "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.8.tgz", "integrity": "sha512-bPSspCXkkhETLXnEgDbaoWRWyv3lF2bj32YIc8IElok2IIMHUlZtQUrxYmAkKUNxpluhH0qnKWrmuoXUyTY12g==", + "license": "MIT", "dependencies": { "style-to-object": "1.0.3" } @@ -8040,6 +10040,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.3.tgz", "integrity": "sha512-xOpx7S53E0V3DpVsvt7ySvoiumRpfXiC99PUXLqGB3wiAnN9ybEIpuzlZ8LAZg+h1sl9JkEUwtSQXxcCgFqbbg==", + "license": "MIT", "dependencies": { "inline-style-parser": "0.2.2" } @@ -8047,12 +10048,14 @@ "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" }, "node_modules/substyle": { "version": "9.4.1", "resolved": "https://registry.npmjs.org/substyle/-/substyle-9.4.1.tgz", "integrity": "sha512-VOngeq/W1/UkxiGzeqVvDbGDPM8XgUyJVWjrqeh+GgKqspEPiLYndK+XRcsKUHM5Muz/++1ctJ1QCF/OqRiKWA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.3.4", "invariant": "^2.2.4" @@ -8066,6 +10069,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -8077,6 +10081,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -8084,15 +10089,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, "node_modules/systemjs": { "version": "6.15.1", "resolved": "https://registry.npmjs.org/systemjs/-/systemjs-6.15.1.tgz", - "integrity": "sha512-Nk8c4lXvMB98MtbmjX7JwJRgJOL8fluecYCfCeYBznwmpOs8Bf15hLM6z4z71EDAhQVrQrI+wt1aLWSXZq+hXA==" + "integrity": "sha512-Nk8c4lXvMB98MtbmjX7JwJRgJOL8fluecYCfCeYBznwmpOs8Bf15hLM6z4z71EDAhQVrQrI+wt1aLWSXZq+hXA==", + "license": "MIT" }, "node_modules/telejson": { "version": "6.0.8", "resolved": "https://registry.npmjs.org/telejson/-/telejson-6.0.8.tgz", "integrity": "sha512-nerNXi+j8NK1QEfBHtZUN/aLdDcyupA//9kAboYLrtzZlPLpUfqbVGWb9zz91f/mIjRbAYhbgtnJHY8I1b5MBg==", + "license": "MIT", "peer": true, "dependencies": { "@types/is-function": "^1.0.0", @@ -8109,12 +10123,14 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/throttle-debounce": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.3.0.tgz", "integrity": "sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -8122,27 +10138,49 @@ "node_modules/tiny-case": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", - "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==" + "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==", + "license": "MIT" }, "node_modules/tiny-invariant": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" }, "node_modules/tiny-warning": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" }, "node_modules/tinycolor2": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", - "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==" + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" @@ -8154,43 +10192,129 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tippy.js": { "version": "6.3.7", "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==", + "license": "MIT", "dependencies": { "@popperjs/core": "^2.9.0" } }, + "node_modules/tldts": { + "version": "7.0.23", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.23.tgz", + "integrity": "sha512-ASdhgQIBSay0R/eXggAkQ53G4nTJqTXqC2kbaBbdDwM7SkjyZyO0OaaN1/FH7U/yCeqOHDwFO5j8+Os/IS1dXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.23" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.23", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.23.tgz", + "integrity": "sha512-0g9vrtDQLrNIiCj22HSe9d4mLVG3g5ph5DZ8zCKBr4OtrspmNB6ss7hVyzArAeE88ceZocIEGkyW1Ime7fxPtQ==", + "dev": true, + "license": "MIT" + }, "node_modules/toposort": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", - "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==", + "license": "MIT" + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz", + "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } }, "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } }, "node_modules/ts-dedent": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "license": "MIT", "peer": true, "engines": { "node": ">=6.10" } }, + "node_modules/tsconfck": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", + "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", + "license": "MIT", + "bin": { + "tsconfck": "bin/tsconfck.js" + }, + "engines": { + "node": "^18 || >=20" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -8203,6 +10327,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -8215,6 +10340,7 @@ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -8229,6 +10355,7 @@ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", @@ -8248,6 +10375,7 @@ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", @@ -8269,6 +10397,7 @@ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", @@ -8288,6 +10417,7 @@ "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8301,6 +10431,7 @@ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", @@ -8314,11 +10445,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici": { + "version": "7.22.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz", + "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -8327,6 +10469,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==", + "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } @@ -8335,6 +10478,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } @@ -8349,12 +10493,14 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT", "peer": true }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } @@ -8362,12 +10508,55 @@ "node_modules/velocity-animate": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/velocity-animate/-/velocity-animate-1.5.2.tgz", - "integrity": "sha512-m6EXlCAMetKztO1ppBhGU1/1MR3IiEevO6ESq6rcrSQ3Q77xYSW13jkfXW88o4xMrkXJhy/U7j4wFR/twMB0Eg==" + "integrity": "sha512-m6EXlCAMetKztO1ppBhGU1/1MR3IiEevO6ESq6rcrSQ3Q77xYSW13jkfXW88o4xMrkXJhy/U7j4wFR/twMB0Eg==", + "license": "MIT" + }, + "node_modules/velocity-react": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/velocity-react/-/velocity-react-1.4.3.tgz", + "integrity": "sha512-zvefGm85A88S3KdF9/dz5vqyFLAiwKYlXGYkHH2EbXl+CZUD1OT0a0aS1tkX/WXWTa/FUYqjBaAzAEFYuSobBQ==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.5", + "prop-types": "^15.5.8", + "react-transition-group": "^2.0.0", + "velocity-animate": "^1.4.0" + }, + "peerDependencies": { + "react": "^15.3.0 || ^16.0.0", + "react-dom": "^15.3.0 || ^16.0.0" + } + }, + "node_modules/velocity-react/node_modules/dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.1.2" + } + }, + "node_modules/velocity-react/node_modules/react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "license": "BSD-3-Clause", + "dependencies": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0", + "react-dom": ">=15.0.0" + } }, "node_modules/victory-vendor": { "version": "36.9.2", "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "license": "MIT AND ISC", "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", @@ -8389,6 +10578,7 @@ "version": "7.3.1", "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "license": "MIT", "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -8462,6 +10652,7 @@ "version": "6.1.1", "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-6.1.1.tgz", "integrity": "sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg==", + "license": "MIT", "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", @@ -8471,73 +10662,155 @@ "vite": "*" } }, - "node_modules/vite-tsconfig-paths/node_modules/tsconfck": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", - "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", + "node_modules/vitest": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz", + "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.0.18", + "@vitest/mocker": "4.0.18", + "@vitest/pretty-format": "4.0.18", + "@vitest/runner": "4.0.18", + "@vitest/snapshot": "4.0.18", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, "bin": { - "tsconfck": "bin/tsconfck.js" + "vitest": "vitest.mjs" }, "engines": { - "node": "^18 || >=20" + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "typescript": "^5.0.0" + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.18", + "@vitest/browser-preview": "4.0.18", + "@vitest/browser-webdriverio": "4.0.18", + "@vitest/ui": "4.0.18", + "happy-dom": "*", + "jsdom": "*" }, "peerDependenciesMeta": { - "typescript": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { "optional": true } } }, - "node_modules/vite-tsconfig-paths/node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "optional": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/void-elements": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/warning": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", "dependencies": { "loose-envify": "^1.0.0" } }, - "node_modules/whatwg-url": { + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-mimetype": { "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" + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" } }, - "node_modules/whatwg-url/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": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -8553,6 +10826,7 @@ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, + "license": "MIT", "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", @@ -8572,6 +10846,7 @@ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", @@ -8599,6 +10874,7 @@ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, + "license": "MIT", "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", @@ -8613,10 +10889,11 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", - "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", "dev": true, + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", @@ -8633,11 +10910,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -8646,6 +10941,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -8663,6 +10959,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -8678,12 +10975,14 @@ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -8697,6 +10996,7 @@ "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -8708,6 +11008,7 @@ "version": "6.2.3", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -8719,6 +11020,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -8733,7 +11035,46 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" }, "node_modules/xmlhttprequest-ssl": { "version": "2.1.2", @@ -8747,6 +11088,7 @@ "version": "2.8.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "license": "ISC", "optional": true, "peer": true, "bin": { @@ -8764,6 +11106,7 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -8775,6 +11118,7 @@ "version": "1.7.1", "resolved": "https://registry.npmjs.org/yup/-/yup-1.7.1.tgz", "integrity": "sha512-GKHFX2nXul2/4Dtfxhozv701jLQHdf6J34YDh2cEkpqoo8le5Mg6/LrdseVLrFarmFygZTlfIhHx/QKfb/QWXw==", + "license": "MIT", "dependencies": { "property-expr": "^2.0.5", "tiny-case": "^1.0.3", @@ -8786,6 +11130,7 @@ "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=12.20" }, diff --git a/ui/package.json b/ui/package.json index 96186721d..94fce1147 100644 --- a/ui/package.json +++ b/ui/package.json @@ -6,7 +6,6 @@ "@contentstack/json-rte-serializer": "^3.0.5", "@contentstack/venus-components": "^3.0.3", "@reduxjs/toolkit": "^2.8.2", - "@testing-library/jest-dom": "^6.0.0", "@types/react": "^18.3.21", "@types/react-dom": "^18.2.13", "@types/react-redux": "^7.1.33", @@ -36,13 +35,30 @@ "lint": "eslint .", "lint:fix": "eslint --fix --ext .js,.jsx,.ts,.tsx . --ignore-pattern './node_modules/' --ignore-pattern './build/' --ignore-pattern '.eslintrc.js'", "precommit": "npm run prettify && npm run lint:fix", - "format": "prettier --write './**/*.{js,jsx,ts,tsx,css,md,json}' --config ./.prettierrc" + "format": "prettier --write './**/*.{js,jsx,ts,tsx,css,md,json}' --config ./.prettierrc", + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage", + "test:coverage:open": "vitest run --coverage && open coverage/index.html", + "coverage:ui": "npx serve coverage -l 3939" }, "devDependencies": { + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", + "@testing-library/user-event": "^14.6.1", + "@vitest/coverage-v8": "^4.0.18", + "@vitest/ui": "^4.0.18", "eslint": "^8.51.0", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", - "prettier": "^3.3.3" + "jsdom": "^28.1.0", + "prettier": "^3.3.3", + "vitest": "^4.0.18" + }, + "overrides": { + "minimatch": ">=10.2.3", + "rollup": ">=4.59.0", + "@babel/runtime": ">=7.26.10" }, "eslintConfig": { "extends": [ diff --git a/ui/tests/fixtures/user.fixture.ts b/ui/tests/fixtures/user.fixture.ts new file mode 100644 index 000000000..b7cd8889f --- /dev/null +++ b/ui/tests/fixtures/user.fixture.ts @@ -0,0 +1,72 @@ +export const createMockUser = (overrides = {}) => ({ + email: 'test@example.com', + username: 'testuser', + first_name: 'Test', + last_name: 'User', + mobile_number: '1234567890', + country_code: '+1', + organizations: [], + region: 'NA', + ...overrides +}); + +export const createMockOrganisation = (overrides = {}) => ({ + uid: 'org-123', + value: 'org-123', + label: 'Test Org', + master_locale: 'en-us', + locales: [], + created_at: '2024-01-01', + ...overrides +}); + +export const createMockProject = (overrides = {}) => ({ + _id: 'proj-123', + name: 'Test Project', + description: 'A test project', + status: 0, + org_id: 'org-123', + org_name: 'Test Org', + region: 'NA', + owner: 'user-123', + created_by: 'user-123', + created_at: '2024-01-01T00:00:00.000Z', + updated_at: '2024-01-01T00:00:00.000Z', + legacy_cms: { + cms_id: 'wordpress', + allowed_file_formats: ['json'], + affix: 'cs', + file_format: 'json', + file_path: '/path/to/file', + is_localPath: true, + is_fileValid: true, + awsDetails: { + awsRegion: 'us-east-1', + bucketName: 'test-bucket', + bucketKey: 'test-key' + } + }, + destination_stack_id: 'stack-123', + destination_stack_name: 'Test Stack', + destination_stack_master_locale: 'en-us', + destination_stack_created_at: '2024-01-01', + content_mapping: {}, + stackDetails: { label: 'Test Stack', value: 'stack-123' }, + mapperKeys: {}, + ...overrides +}); + +export const createMockLoginUser = (overrides = {}) => ({ + email: 'test@example.com', + password: 'password123', + ...overrides +}); + +export const createMockAxiosResponse = (overrides = {}) => ({ + status: 200, + statusText: 'OK', + data: {}, + headers: {}, + config: {}, + ...overrides +}); diff --git a/ui/tests/setup.ts b/ui/tests/setup.ts new file mode 100644 index 000000000..090074d50 --- /dev/null +++ b/ui/tests/setup.ts @@ -0,0 +1,20 @@ +import { vi, beforeAll, afterAll, afterEach } from 'vitest'; +import '@testing-library/jest-dom/vitest'; + +beforeAll(() => { + vi.stubEnv('VITE_BASE_API_URL', 'http://localhost:5001/'); + vi.stubEnv('VITE_WEBSITE_BASE_URL', 'https://test.contentstack.com'); + vi.stubEnv('VITE_API_VERSION', 'v2'); + vi.stubEnv('VITE_UPLOAD_SERVER', 'http://localhost:5002/'); + vi.stubEnv('VITE_OFFLINE_CMS', 'true'); +}); + +afterEach(() => { + vi.restoreAllMocks(); + localStorage.clear(); + sessionStorage.clear(); +}); + +afterAll(() => { + vi.unstubAllEnvs(); +}); diff --git a/ui/tests/unit/cmsData/cmsSelector.test.ts b/ui/tests/unit/cmsData/cmsSelector.test.ts new file mode 100644 index 000000000..5acdb9e90 --- /dev/null +++ b/ui/tests/unit/cmsData/cmsSelector.test.ts @@ -0,0 +1,114 @@ +import { describe, it, expect, vi } from 'vitest'; + +vi.mock('../../../src/utilities/constants', () => ({ + CS_ENTRIES: { + HEADER: 'header', + MAIN_HEADER: 'main_header', + HOME_PAGE: 'homepage', + REGIONS: 'region_login', + LOGIN: 'login', + PROJECTS: 'projects', + MIGRATION_FLOW: 'migration_steps', + LEGACY_CMS: 'legacy_cms', + DESTINATION_STACK: 'destination_stack', + CONTENT_MAPPING: 'content_mapping', + TEST_MIGRATION: 'test_migration', + MIGRATION_EXECUTION: 'migration_execution', + SETTING: 'settings', + NOT_FOUND_ERROR: { type: 'error_handler', url: '404' }, + INTERNAL_SERVER_ERROR: { type: 'error_handler', url: '500' }, + ERROR_HANDLER: 'error_handler', + ADD_STACK: 'add_stack', + UNMAPPED_LOCALE_KEY: 'undefined' + }, + BASE_API_URL: 'http://localhost:5001/', + TOKEN_KEY: 'access_token', + TOKEN: null, + HEADERS: {} +})); + +import { getCMSDataFromFile } from '../../../src/cmsData/cmsSelector'; + +describe('cmsData/cmsSelector', () => { + describe('getCMSDataFromFile', () => { + it('should return homepage data for HOME_PAGE content type', async () => { + const result = await getCMSDataFromFile('homepage'); + expect(result).toBeDefined(); + }); + + it('should return login data for LOGIN content type', async () => { + const result = await getCMSDataFromFile('login'); + expect(result).toBeDefined(); + }); + + it('should return region login data for REGIONS content type', async () => { + const result = await getCMSDataFromFile('region_login'); + expect(result).toBeDefined(); + }); + + it('should return main header data for MAIN_HEADER content type', async () => { + const result = await getCMSDataFromFile('main_header'); + expect(result).toBeDefined(); + }); + + it('should return projects data for PROJECTS content type', async () => { + const result = await getCMSDataFromFile('projects'); + expect(result).toBeDefined(); + }); + + it('should return legacy CMS data for LEGACY_CMS content type', async () => { + const result = await getCMSDataFromFile('legacy_cms'); + expect(result).toBeDefined(); + }); + + it('should return destination stack data for DESTINATION_STACK content type', async () => { + const result = await getCMSDataFromFile('destination_stack'); + expect(result).toBeDefined(); + }); + + it('should return add stack data for ADD_STACK content type', async () => { + const result = await getCMSDataFromFile('add_stack'); + expect(result).toBeDefined(); + }); + + it('should return migration steps data for MIGRATION_FLOW content type', async () => { + const result = await getCMSDataFromFile('migration_steps'); + expect(result).toBeDefined(); + }); + + it('should return content mapping data for CONTENT_MAPPING content type', async () => { + const result = await getCMSDataFromFile('content_mapping'); + expect(result).toBeDefined(); + }); + + it('should return test migration data for TEST_MIGRATION content type', async () => { + const result = await getCMSDataFromFile('test_migration'); + expect(result).toBeDefined(); + }); + + it('should return migration execution data for MIGRATION_EXECUTION content type', async () => { + const result = await getCMSDataFromFile('migration_execution'); + expect(result).toBeDefined(); + }); + + it('should return settings data for SETTING content type', async () => { + const result = await getCMSDataFromFile('settings'); + expect(result).toBeDefined(); + }); + + it('should return 500 error data for ERROR_HANDLER with 500 url', async () => { + const result = await getCMSDataFromFile('error_handler', '500'); + expect(result).toBeDefined(); + }); + + it('should return 404 error data for ERROR_HANDLER with 404 url', async () => { + const result = await getCMSDataFromFile('error_handler', '404'); + expect(result).toBeDefined(); + }); + + it('should return undefined for unknown content type', async () => { + const result = await getCMSDataFromFile('unknown_type'); + expect(result).toBeUndefined(); + }); + }); +}); diff --git a/ui/tests/unit/hooks/useAuthCheck.test.ts b/ui/tests/unit/hooks/useAuthCheck.test.ts new file mode 100644 index 000000000..8b50491b8 --- /dev/null +++ b/ui/tests/unit/hooks/useAuthCheck.test.ts @@ -0,0 +1,104 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { renderHook } from '@testing-library/react'; +import React from 'react'; + +const mockNavigate = vi.fn(); +const mockDispatch = vi.fn(); +const mockSelector = vi.fn(); + +vi.mock('react-redux', () => ({ + useSelector: (fn: any) => mockSelector(fn), + useDispatch: () => mockDispatch +})); + +vi.mock('react-router-dom', () => ({ + useNavigate: () => mockNavigate +})); + +vi.mock('jwt-decode', () => ({ + jwtDecode: vi.fn() +})); + +vi.mock('../../../src/store/slice/authSlice', () => ({ + reInitiliseState: vi.fn(() => ({ type: 'auth/reInitiliseState' })) +})); + +import useAuthCheck from '../../../src/hooks/authentication'; +import { jwtDecode } from 'jwt-decode'; +import { reInitiliseState } from '../../../src/store/slice/authSlice'; + +describe('hooks/useAuthCheck', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockNavigate.mockClear(); + mockDispatch.mockClear(); + }); + + it('should not dispatch or navigate when authToken is falsy', () => { + let callCount = 0; + mockSelector.mockImplementation(() => { + callCount++; + if (callCount % 2 === 1) return ''; // authToken + return {}; // selectedOrganisation + }); + + renderHook(() => useAuthCheck()); + + expect(mockDispatch).not.toHaveBeenCalled(); + expect(mockNavigate).not.toHaveBeenCalled(); + }); + + it('should not dispatch if token is valid (not expired)', () => { + let callCount = 0; + mockSelector.mockImplementation(() => { + callCount++; + if (callCount % 2 === 1) return 'valid-token'; + return {}; + }); + + const futureExp = (Date.now() / 1000) + 3600; + vi.mocked(jwtDecode).mockReturnValue({ exp: futureExp }); + + renderHook(() => useAuthCheck()); + + expect(mockDispatch).not.toHaveBeenCalled(); + expect(mockNavigate).not.toHaveBeenCalled(); + }); + + it('should dispatch reInitiliseState and navigate to "/" when token is expired', () => { + let callCount = 0; + mockSelector.mockImplementation(() => { + callCount++; + if (callCount % 2 === 1) return 'expired-token'; + return {}; + }); + + const pastExp = (Date.now() / 1000) - 3600; + vi.mocked(jwtDecode).mockReturnValue({ exp: pastExp }); + + renderHook(() => useAuthCheck()); + + expect(mockDispatch).toHaveBeenCalledWith(reInitiliseState()); + expect(mockNavigate).toHaveBeenCalledWith('/'); + }); + + it('should dispatch reInitiliseState and navigate on jwtDecode error', () => { + let callCount = 0; + mockSelector.mockImplementation(() => { + callCount++; + if (callCount % 2 === 1) return 'bad-token'; + return {}; + }); + + vi.mocked(jwtDecode).mockImplementation(() => { + throw new Error('Invalid token'); + }); + + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); + renderHook(() => useAuthCheck()); + + expect(mockDispatch).toHaveBeenCalledWith(reInitiliseState()); + expect(mockNavigate).toHaveBeenCalledWith('/'); + consoleSpy.mockRestore(); + }); +}); diff --git a/ui/tests/unit/hooks/useBlockNavigation.test.ts b/ui/tests/unit/hooks/useBlockNavigation.test.ts new file mode 100644 index 000000000..cdd77d317 --- /dev/null +++ b/ui/tests/unit/hooks/useBlockNavigation.test.ts @@ -0,0 +1,85 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { renderHook } from '@testing-library/react'; + +const mockLocation = { pathname: '/migration', search: '', hash: '' }; + +vi.mock('react-router-dom', () => ({ + useLocation: () => mockLocation +})); + +vi.mock('../../../src/utilities/constants', () => ({ + WEBSITE_BASE_URL: 'https://test.contentstack.com' +})); + +import useBlockNavigation from '../../../src/hooks/userNavigation'; + +describe('hooks/useBlockNavigation', () => { + let pushStateSpy: ReturnType; + let addEventSpy: ReturnType; + let removeEventSpy: ReturnType; + + beforeEach(() => { + vi.clearAllMocks(); + pushStateSpy = vi.spyOn(window.history, 'pushState').mockImplementation(() => {}); + addEventSpy = vi.spyOn(window, 'addEventListener'); + removeEventSpy = vi.spyOn(window, 'removeEventListener'); + }); + + it('should not push state or add listener when modal is closed', () => { + renderHook(() => useBlockNavigation(false)); + expect(pushStateSpy).not.toHaveBeenCalled(); + expect(addEventSpy).not.toHaveBeenCalledWith('popstate', expect.any(Function)); + }); + + it('should push state and add popstate listener when modal is open', () => { + renderHook(() => useBlockNavigation(true)); + + expect(pushStateSpy).toHaveBeenCalledWith( + { blockNav: true }, + '', + '/migration' + ); + expect(addEventSpy).toHaveBeenCalledWith('popstate', expect.any(Function)); + }); + + it('should remove popstate listener on unmount', () => { + const { unmount } = renderHook(() => useBlockNavigation(true)); + unmount(); + expect(removeEventSpy).toHaveBeenCalledWith('popstate', expect.any(Function)); + }); + + it('should re-push state on popstate when modal is open', () => { + renderHook(() => useBlockNavigation(true)); + + const handler = addEventSpy.mock.calls.find( + (call) => call[0] === 'popstate' + )?.[1] as EventListener; + + pushStateSpy.mockClear(); + handler(new PopStateEvent('popstate')); + + expect(pushStateSpy).toHaveBeenCalledWith( + { blockNav: true }, + '', + '/migration' + ); + }); + + it('should update stored pathname when modal closes', () => { + const { rerender } = renderHook( + ({ isOpen }) => useBlockNavigation(isOpen), + { initialProps: { isOpen: true } } + ); + + rerender({ isOpen: false }); + + pushStateSpy.mockClear(); + rerender({ isOpen: true }); + + expect(pushStateSpy).toHaveBeenCalledWith( + { blockNav: true }, + '', + '/migration' + ); + }); +}); diff --git a/ui/tests/unit/hooks/useDebouncer.test.ts b/ui/tests/unit/hooks/useDebouncer.test.ts new file mode 100644 index 000000000..f21d724d2 --- /dev/null +++ b/ui/tests/unit/hooks/useDebouncer.test.ts @@ -0,0 +1,80 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { useDebouncer } from '../../../src/hooks/index'; + +describe('hooks/useDebouncer', () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it('should debounce the callback', () => { + const callback = vi.fn(); + const debounced = useDebouncer(callback, 300); + + debounced('arg1'); + debounced('arg2'); + debounced('arg3'); + + expect(callback).not.toHaveBeenCalled(); + + vi.advanceTimersByTime(300); + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenCalledWith('arg3'); + }); + + it('should use 250ms as default wait time', () => { + const callback = vi.fn(); + const debounced = useDebouncer(callback); + + debounced(); + vi.advanceTimersByTime(249); + expect(callback).not.toHaveBeenCalled(); + + vi.advanceTimersByTime(1); + expect(callback).toHaveBeenCalledTimes(1); + }); + + it('should reset the timer on each call', () => { + const callback = vi.fn(); + const debounced = useDebouncer(callback, 100); + + debounced('first'); + vi.advanceTimersByTime(80); + + debounced('second'); + vi.advanceTimersByTime(80); + + expect(callback).not.toHaveBeenCalled(); + + vi.advanceTimersByTime(20); + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenCalledWith('second'); + }); + + it('should pass multiple arguments to the callback', () => { + const callback = vi.fn(); + const debounced = useDebouncer(callback, 100); + + debounced('arg1', 'arg2', 42); + vi.advanceTimersByTime(100); + + expect(callback).toHaveBeenCalledWith('arg1', 'arg2', 42); + }); + + it('should allow separate invocations after wait period', () => { + const callback = vi.fn(); + const debounced = useDebouncer(callback, 100); + + debounced('first'); + vi.advanceTimersByTime(100); + expect(callback).toHaveBeenCalledWith('first'); + + debounced('second'); + vi.advanceTimersByTime(100); + expect(callback).toHaveBeenCalledWith('second'); + expect(callback).toHaveBeenCalledTimes(2); + }); +}); diff --git a/ui/tests/unit/hooks/usePreventBackNavigation.test.ts b/ui/tests/unit/hooks/usePreventBackNavigation.test.ts new file mode 100644 index 000000000..4765cf843 --- /dev/null +++ b/ui/tests/unit/hooks/usePreventBackNavigation.test.ts @@ -0,0 +1,68 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { renderHook } from '@testing-library/react'; + +const mockNavigate = vi.fn(); +const mockLocation = { pathname: '/projects', search: '?id=1', hash: '#top' }; + +vi.mock('react-router-dom', () => ({ + useNavigate: () => mockNavigate, + useLocation: () => mockLocation +})); + +vi.mock('../../../src/utilities/constants', () => ({ + WEBSITE_BASE_URL: 'https://test.contentstack.com' +})); + +import usePreventBackNavigation from '../../../src/hooks/usePreventBackNavigation'; + +describe('hooks/usePreventBackNavigation', () => { + let pushStateSpy: ReturnType; + let addEventSpy: ReturnType; + let removeEventSpy: ReturnType; + + beforeEach(() => { + vi.clearAllMocks(); + pushStateSpy = vi.spyOn(window.history, 'pushState').mockImplementation(() => {}); + addEventSpy = vi.spyOn(window, 'addEventListener'); + removeEventSpy = vi.spyOn(window, 'removeEventListener'); + }); + + it('should push history state on mount', () => { + renderHook(() => usePreventBackNavigation()); + + expect(pushStateSpy).toHaveBeenCalledWith( + { preventBack: true }, + '', + '/projects?id=1#top' + ); + }); + + it('should add popstate event listener', () => { + renderHook(() => usePreventBackNavigation()); + expect(addEventSpy).toHaveBeenCalledWith('popstate', expect.any(Function)); + }); + + it('should remove popstate event listener on unmount', () => { + const { unmount } = renderHook(() => usePreventBackNavigation()); + unmount(); + expect(removeEventSpy).toHaveBeenCalledWith('popstate', expect.any(Function)); + }); + + it('should push state again on popstate (back navigation)', () => { + renderHook(() => usePreventBackNavigation()); + + const handler = addEventSpy.mock.calls.find( + (call) => call[0] === 'popstate' + )?.[1] as EventListener; + + pushStateSpy.mockClear(); + const event = new PopStateEvent('popstate'); + handler(event); + + expect(pushStateSpy).toHaveBeenCalledWith( + { preventBack: true }, + '', + '/projects?id=1#top' + ); + }); +}); diff --git a/ui/tests/unit/hooks/useWarnOnRefresh.test.ts b/ui/tests/unit/hooks/useWarnOnRefresh.test.ts new file mode 100644 index 000000000..376534e60 --- /dev/null +++ b/ui/tests/unit/hooks/useWarnOnRefresh.test.ts @@ -0,0 +1,52 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { renderHook } from '@testing-library/react'; +import { useWarnOnRefresh } from '../../../src/hooks/useWarnOnrefresh'; + +describe('hooks/useWarnOnRefresh', () => { + let addSpy: ReturnType; + let removeSpy: ReturnType; + + beforeEach(() => { + addSpy = vi.spyOn(window, 'addEventListener'); + removeSpy = vi.spyOn(window, 'removeEventListener'); + }); + + it('should add beforeunload event listener on mount', () => { + renderHook(() => useWarnOnRefresh(true)); + expect(addSpy).toHaveBeenCalledWith('beforeunload', expect.any(Function)); + }); + + it('should remove beforeunload event listener on unmount', () => { + const { unmount } = renderHook(() => useWarnOnRefresh(true)); + unmount(); + expect(removeSpy).toHaveBeenCalledWith('beforeunload', expect.any(Function)); + }); + + it('should call preventDefault when isUnsaved is true', () => { + renderHook(() => useWarnOnRefresh(true)); + + const handler = addSpy.mock.calls.find( + (call) => call[0] === 'beforeunload' + )?.[1] as EventListener; + + const event = new Event('beforeunload') as BeforeUnloadEvent; + const preventDefaultSpy = vi.spyOn(event, 'preventDefault'); + handler(event); + + expect(preventDefaultSpy).toHaveBeenCalled(); + }); + + it('should not call preventDefault when isUnsaved is false', () => { + renderHook(() => useWarnOnRefresh(false)); + + const handler = addSpy.mock.calls.find( + (call) => call[0] === 'beforeunload' + )?.[1] as EventListener; + + const event = new Event('beforeunload') as BeforeUnloadEvent; + const preventDefaultSpy = vi.spyOn(event, 'preventDefault'); + handler(event); + + expect(preventDefaultSpy).not.toHaveBeenCalled(); + }); +}); diff --git a/ui/tests/unit/services/login.service.test.ts b/ui/tests/unit/services/login.service.test.ts new file mode 100644 index 000000000..a6634efa0 --- /dev/null +++ b/ui/tests/unit/services/login.service.test.ts @@ -0,0 +1,90 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockPostCall } = vi.hoisted(() => ({ + mockPostCall: vi.fn() +})); + +vi.mock('../../../src/services/api/service', () => ({ + postCall: mockPostCall +})); + +vi.mock('../../../src/utilities/constants', () => ({ + AUTH_ROUTES: 'v2/auth', + API_VERSION: 'v2', + BASE_API_URL: 'http://localhost:5001/', + TOKEN_KEY: 'access_token', + TOKEN: null, + HEADERS: {} +})); + +import { userSession, requestSMSToken } from '../../../src/services/api/login.service'; + +describe('services/api/login.service', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('userSession', () => { + it('should call postCall with user-session endpoint and data', async () => { + const mockResponse = { status: 200, data: { notice: 'Login successful' } }; + mockPostCall.mockResolvedValue(mockResponse); + + const userData = { email: 'test@example.com', password: 'pass123' }; + const result = await userSession(userData); + + expect(mockPostCall).toHaveBeenCalledWith('v2/auth/user-session', userData); + expect(result).toEqual(mockResponse); + }); + + it('should propagate the response from postCall', async () => { + const mockResponse = { status: 401, data: { error_message: 'Invalid credentials' } }; + mockPostCall.mockResolvedValue(mockResponse); + + const result = await userSession({ email: 'test@example.com', password: 'wrong' }); + expect(result).toEqual(mockResponse); + }); + }); + + describe('requestSMSToken', () => { + it('should call postCall with request-token-sms endpoint', async () => { + const mockResponse = { status: 200, data: { notice: 'SMS sent' } }; + mockPostCall.mockResolvedValue(mockResponse); + + const smsData = { email: 'test@example.com', password: 'pass123', region: 'NA' }; + const result = await requestSMSToken(smsData); + + expect(mockPostCall).toHaveBeenCalledWith('v2/auth/request-token-sms', smsData); + expect(result).toEqual(mockResponse); + }); + + it('should throw with error message when postCall throws an Error', () => { + mockPostCall.mockImplementation(() => { throw new Error('Network failure'); }); + + expect(() => requestSMSToken({ email: 'a@b.com', password: 'p', region: 'NA' })) + .toThrow('Error in requestSMSToken: Network failure'); + }); + + it('should throw generic message when postCall throws a non-Error', () => { + mockPostCall.mockImplementation(() => { throw 'something'; }); + + expect(() => requestSMSToken({ email: 'a@b.com', password: 'p', region: 'NA' })) + .toThrow('Unknown error in requestSMSToken'); + }); + }); + + describe('userSession - error handling', () => { + it('should throw with error message when postCall throws an Error', () => { + mockPostCall.mockImplementation(() => { throw new Error('Connection refused'); }); + + expect(() => userSession({ email: 'a@b.com', password: 'p' })) + .toThrow('Error in userSession: Connection refused'); + }); + + it('should throw generic message when postCall throws a non-Error', () => { + mockPostCall.mockImplementation(() => { throw 42; }); + + expect(() => userSession({ email: 'a@b.com', password: 'p' })) + .toThrow('Unknown error in userSession'); + }); + }); +}); diff --git a/ui/tests/unit/services/migration.service.test.ts b/ui/tests/unit/services/migration.service.test.ts new file mode 100644 index 000000000..f1098b61e --- /dev/null +++ b/ui/tests/unit/services/migration.service.test.ts @@ -0,0 +1,565 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockGetCall, mockPostCall, mockPutCall, mockPatchCall } = vi.hoisted(() => ({ + mockGetCall: vi.fn(), + mockPostCall: vi.fn(), + mockPutCall: vi.fn(), + mockPatchCall: vi.fn() +})); + +vi.mock('../../../src/services/api/service', () => ({ + getCall: mockGetCall, + postCall: mockPostCall, + putCall: mockPutCall, + patchCall: mockPatchCall +})); + +vi.mock('../../../src/utilities/constants', () => ({ + API_VERSION: 'v2', + BASE_API_URL: 'http://localhost:5001/', + TOKEN_KEY: 'access_token', + TOKEN: null, + HEADERS: {}, + EXECUTION_LOGS_ERROR_TEXT: { ERROR: 'Error in Getting Migration Logs' } +})); + +vi.mock('../../../src/utilities/functions', () => ({ + getDataFromLocalStorage: vi.fn(() => 'mock-app-token') +})); + +import { + getMigrationData, + updateLegacyCMSData, + updateAffixData, + updateFileFormatData, + updateDestinationStack, + updateCurrentStepData, + affixConfirmation, + fileformatConfirmation, + getContentTypes, + getFieldMapping, + updateContentType, + resetToInitialMapping, + getExistingContentTypes, + getExistingGlobalFields, + removeContentMapper, + updateContentMapper, + updateStackDetails, + getOrgDetails, + createTestStack, + createTestMigration, + startMigration, + updateMigrationKey, + updateLocaleMapper, + getExistingTaxonomies, + getMigrationLogs +} from '../../../src/services/api/migration.service'; + +describe('services/api/migration.service', () => { + const orgId = 'org-123'; + const projectId = 'proj-456'; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('getMigrationData', () => { + it('should call getCall with org and project ID', () => { + const mockResponse = { status: 200, data: {} }; + mockGetCall.mockResolvedValue(mockResponse); + + getMigrationData(orgId, projectId); + expect(mockGetCall).toHaveBeenCalledWith( + `v2/org/${orgId}/project/${projectId}`, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('updateLegacyCMSData', () => { + it('should call putCall with legacy-cms endpoint', () => { + const data = { cms: 'wordpress' }; + mockPutCall.mockResolvedValue({ status: 200 }); + + updateLegacyCMSData(orgId, projectId, data); + expect(mockPutCall).toHaveBeenCalledWith( + `v2/org/${orgId}/project/${projectId}/legacy-cms`, + data, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('updateAffixData', () => { + it('should call putCall with affix endpoint', () => { + const data = { affix: 'cs' }; + mockPutCall.mockResolvedValue({ status: 200 }); + + updateAffixData(orgId, projectId, data); + expect(mockPutCall).toHaveBeenCalledWith( + `v2/org/${orgId}/project/${projectId}/affix`, + data, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('updateFileFormatData', () => { + it('should call putCall with file-format endpoint', () => { + const data = { file_format: 'json' }; + mockPutCall.mockResolvedValue({ status: 200 }); + + updateFileFormatData(orgId, projectId, data); + expect(mockPutCall).toHaveBeenCalledWith( + `v2/org/${orgId}/project/${projectId}/file-format`, + data, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('updateDestinationStack', () => { + it('should call putCall with destination-stack endpoint', () => { + const data = { stack_id: 'stack-1' }; + mockPutCall.mockResolvedValue({ status: 200 }); + + updateDestinationStack(orgId, projectId, data); + expect(mockPutCall).toHaveBeenCalledWith( + `v2/org/${orgId}/project/${projectId}/destination-stack`, + data, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('updateCurrentStepData', () => { + it('should call putCall with current-step endpoint', () => { + const data = { step: 2 }; + mockPutCall.mockResolvedValue({ status: 200 }); + + updateCurrentStepData(orgId, projectId, data); + expect(mockPutCall).toHaveBeenCalledWith( + `v2/org/${orgId}/project/${projectId}/current-step`, + data, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + + it('should use empty object as default data', () => { + mockPutCall.mockResolvedValue({ status: 200 }); + + updateCurrentStepData(orgId, projectId); + expect(mockPutCall).toHaveBeenCalledWith( + `v2/org/${orgId}/project/${projectId}/current-step`, + {}, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('affixConfirmation', () => { + it('should call putCall with affix_confirmation endpoint', () => { + mockPutCall.mockResolvedValue({ status: 200 }); + + affixConfirmation(orgId, projectId, { confirmed: true }); + expect(mockPutCall).toHaveBeenCalledWith( + `v2/org/${orgId}/project/${projectId}/affix_confirmation`, + { confirmed: true }, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('fileformatConfirmation', () => { + it('should call putCall with fileformat_confirmation endpoint', () => { + mockPutCall.mockResolvedValue({ status: 200 }); + + fileformatConfirmation(orgId, projectId, { confirmed: true }); + expect(mockPutCall).toHaveBeenCalledWith( + `v2/org/${orgId}/project/${projectId}/fileformat_confirmation`, + { confirmed: true }, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('getContentTypes', () => { + it('should call getCall with encoded search text', () => { + mockGetCall.mockResolvedValue({ status: 200, data: {} }); + + getContentTypes('proj-1', 0, 10, 'search text'); + expect(mockGetCall).toHaveBeenCalledWith( + 'v2/mapper/contentTypes/proj-1/0/10/search%20text?', + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('getFieldMapping', () => { + it('should call getCall with content type and pagination', async () => { + mockGetCall.mockResolvedValue({ status: 200, data: {} }); + + await getFieldMapping('ct-1', 0, 10, 'field', 'proj-1'); + expect(mockGetCall).toHaveBeenCalledWith( + 'v2/mapper/fieldMapping/proj-1/ct-1/0/10/field?', + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('updateContentType', () => { + it('should call putCall with content type update data', async () => { + mockPutCall.mockResolvedValue({ status: 200 }); + + await updateContentType(orgId, projectId, 'ct-1', { mapping: {} }); + expect(mockPutCall).toHaveBeenCalledWith( + `v2/mapper/contentTypes/${orgId}/${projectId}/ct-1`, + { mapping: {} }, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('resetToInitialMapping', () => { + it('should call putCall with reset endpoint', async () => { + mockPutCall.mockResolvedValue({ status: 200 }); + + await resetToInitialMapping(orgId, projectId, 'ct-1', {}); + expect(mockPutCall).toHaveBeenCalledWith( + `v2/mapper/resetFields/${orgId}/${projectId}/ct-1`, + {}, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('getExistingContentTypes', () => { + it('should call getCall with content type uid', async () => { + mockGetCall.mockResolvedValue({ status: 200, data: [] }); + + await getExistingContentTypes('proj-1', 'ct-1'); + expect(mockGetCall).toHaveBeenCalledWith( + 'v2/mapper/proj-1/contentTypes/ct-1', + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + + it('should call getCall without content type uid when not provided', async () => { + mockGetCall.mockResolvedValue({ status: 200, data: [] }); + + await getExistingContentTypes('proj-1'); + expect(mockGetCall).toHaveBeenCalledWith( + 'v2/mapper/proj-1/contentTypes/', + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('getExistingGlobalFields', () => { + it('should call getCall with global field uid', async () => { + mockGetCall.mockResolvedValue({ status: 200, data: [] }); + + await getExistingGlobalFields('proj-1', 'gf-1'); + expect(mockGetCall).toHaveBeenCalledWith( + 'v2/mapper/proj-1/globalFields/gf-1', + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('removeContentMapper', () => { + it('should call getCall for content-mapper endpoint', async () => { + mockGetCall.mockResolvedValue({ status: 200 }); + + await removeContentMapper(orgId, projectId); + expect(mockGetCall).toHaveBeenCalledWith( + `v2/mapper/${orgId}/${projectId}/content-mapper`, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('updateContentMapper', () => { + it('should call patchCall with content_mapper wrapped data', async () => { + mockPatchCall.mockResolvedValue({ status: 200 }); + const data = { key: 'value' }; + + await updateContentMapper(orgId, projectId, data); + expect(mockPatchCall).toHaveBeenCalledWith( + `v2/mapper/${orgId}/${projectId}/mapper_keys`, + { content_mapper: data }, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('updateStackDetails', () => { + it('should call patchCall with stack_details wrapped data', async () => { + mockPatchCall.mockResolvedValue({ status: 200 }); + const data = { locale: 'en-us' }; + + await updateStackDetails(orgId, projectId, data); + expect(mockPatchCall).toHaveBeenCalledWith( + `v2/org/${orgId}/project/${projectId}/stack-details`, + { stack_details: data }, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('getOrgDetails', () => { + it('should call getCall with org details endpoint', async () => { + mockGetCall.mockResolvedValue({ status: 200, data: {} }); + + await getOrgDetails(orgId); + expect(mockGetCall).toHaveBeenCalledWith( + `v2/org/${orgId}/get_org_details`, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('createTestStack', () => { + it('should call postCall with test stack data', async () => { + mockPostCall.mockResolvedValue({ status: 201 }); + + await createTestStack(orgId, projectId, { name: 'test-stack' }); + expect(mockPostCall).toHaveBeenCalledWith( + `v2/migration/create-test-stack/${orgId}/${projectId}`, + { name: 'test-stack' }, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('createTestMigration', () => { + it('should call postCall with empty body', async () => { + mockPostCall.mockResolvedValue({ status: 200 }); + + await createTestMigration(orgId, projectId); + expect(mockPostCall).toHaveBeenCalledWith( + `v2/migration/test-stack/${orgId}/${projectId}`, + {}, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('startMigration', () => { + it('should call postCall with migration start endpoint', async () => { + mockPostCall.mockResolvedValue({ status: 200 }); + + await startMigration(orgId, projectId); + expect(mockPostCall).toHaveBeenCalledWith( + `v2/migration/start/${orgId}/${projectId}`, + {}, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('updateMigrationKey', () => { + it('should call putCall with migration-execution endpoint', async () => { + mockPutCall.mockResolvedValue({ status: 200 }); + + await updateMigrationKey(orgId, projectId); + expect(mockPutCall).toHaveBeenCalledWith( + `v2/org/${orgId}/project/${projectId}/migration-excution`, + {}, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('updateLocaleMapper', () => { + it('should call postCall with locale data', async () => { + mockPostCall.mockResolvedValue({ status: 200 }); + const data = { locales: { 'en-us': 'en-us' } }; + + await updateLocaleMapper(projectId, data); + expect(mockPostCall).toHaveBeenCalledWith( + `v2/migration/updateLocales/${projectId}`, + data, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('getExistingTaxonomies', () => { + it('should call getCall with taxonomies endpoint', async () => { + mockGetCall.mockResolvedValue({ status: 200, data: [] }); + + await getExistingTaxonomies(projectId); + expect(mockGetCall).toHaveBeenCalledWith( + `v2/mapper/${projectId}/taxonomies`, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + }); + + describe('getMigrationLogs', () => { + it('should call getCall with all parameters in the URL', async () => { + mockGetCall.mockResolvedValue({ status: 200, data: { logs: [] } }); + + await getMigrationLogs(orgId, projectId, 'stack-1', 0, 10, 0, 10, 'search', 'all'); + expect(mockGetCall).toHaveBeenCalledWith( + `v2/migration/get_migration_logs/${orgId}/${projectId}/stack-1/0/10/0/10/search/all`, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + }); + + it('should throw with formatted message on Error', async () => { + mockGetCall.mockImplementation(() => { throw new Error('timeout'); }); + await expect(getMigrationLogs(orgId, projectId, 's', 0, 10, 0, 10, '', 'all')) + .rejects.toThrow('Error in Getting Migration Logs: timeout'); + }); + + it('should throw generic message on non-Error', async () => { + mockGetCall.mockImplementation(() => { throw null; }); + await expect(getMigrationLogs(orgId, projectId, 's', 0, 10, 0, 10, '', 'all')) + .rejects.toThrow('Unknown Error in Getting Migration Logs'); + }); + }); + + describe('error handling for migration service functions', () => { + it('updateLegacyCMSData should throw on Error', () => { + mockPutCall.mockImplementation(() => { throw new Error('err'); }); + expect(() => updateLegacyCMSData(orgId, projectId, {})).toThrow('err'); + }); + + it('updateLegacyCMSData should throw generic on non-Error', () => { + mockPutCall.mockImplementation(() => { throw 0; }); + expect(() => updateLegacyCMSData(orgId, projectId, {})).toThrow('Unknown error'); + }); + + it('updateAffixData should throw on Error', () => { + mockPutCall.mockImplementation(() => { throw new Error('err'); }); + expect(() => updateAffixData(orgId, projectId, {})).toThrow('err'); + }); + + it('updateAffixData should throw generic on non-Error', () => { + mockPutCall.mockImplementation(() => { throw 0; }); + expect(() => updateAffixData(orgId, projectId, {})).toThrow('Unknown error'); + }); + + it('updateFileFormatData should throw on Error', () => { + mockPutCall.mockImplementation(() => { throw new Error('err'); }); + expect(() => updateFileFormatData(orgId, projectId, {})).toThrow('err'); + }); + + it('updateFileFormatData should throw generic on non-Error', () => { + mockPutCall.mockImplementation(() => { throw 0; }); + expect(() => updateFileFormatData(orgId, projectId, {})).toThrow('Unknown error'); + }); + + it('updateDestinationStack should throw on Error', () => { + mockPutCall.mockImplementation(() => { throw new Error('err'); }); + expect(() => updateDestinationStack(orgId, projectId, {})).toThrow('err'); + }); + + it('updateDestinationStack should throw generic on non-Error', () => { + mockPutCall.mockImplementation(() => { throw 0; }); + expect(() => updateDestinationStack(orgId, projectId, {})).toThrow('Unknown error'); + }); + + it('updateCurrentStepData should throw on Error', () => { + mockPutCall.mockImplementation(() => { throw new Error('err'); }); + expect(() => updateCurrentStepData(orgId, projectId, {})).toThrow('err'); + }); + + it('updateCurrentStepData should throw generic on non-Error', () => { + mockPutCall.mockImplementation(() => { throw 0; }); + expect(() => updateCurrentStepData(orgId, projectId, {})).toThrow('Unknown error'); + }); + + it('getMigrationData should throw on Error', () => { + mockGetCall.mockImplementation(() => { throw new Error('err'); }); + expect(() => getMigrationData(orgId, projectId)).toThrow('Error in getting migrationData: err'); + }); + + it('getMigrationData should throw generic on non-Error', () => { + mockGetCall.mockImplementation(() => { throw 0; }); + expect(() => getMigrationData(orgId, projectId)).toThrow('Unknown error in getting migrationData'); + }); + + it('getContentTypes should throw on Error', () => { + mockGetCall.mockImplementation(() => { throw new Error('err'); }); + expect(() => getContentTypes('p', 0, 10, '')).toThrow('err'); + }); + + it('getContentTypes should throw generic on non-Error', () => { + mockGetCall.mockImplementation(() => { throw 0; }); + expect(() => getContentTypes('p', 0, 10, '')).toThrow('Unknown error'); + }); + + it('getFieldMapping should throw on Error', async () => { + mockGetCall.mockImplementation(() => { throw new Error('err'); }); + await expect(getFieldMapping('ct', 0, 10, '', 'p')).rejects.toThrow('err'); + }); + + it('getFieldMapping should throw generic on non-Error', async () => { + mockGetCall.mockImplementation(() => { throw 0; }); + await expect(getFieldMapping('ct', 0, 10, '', 'p')).rejects.toThrow('Unknown error'); + }); + + it('updateContentType should throw on Error', async () => { + mockPutCall.mockImplementation(() => { throw new Error('err'); }); + await expect(updateContentType(orgId, projectId, 'ct', {})).rejects.toThrow('err'); + }); + + it('updateContentType should throw generic on non-Error', async () => { + mockPutCall.mockImplementation(() => { throw 0; }); + await expect(updateContentType(orgId, projectId, 'ct', {})).rejects.toThrow('Unknown error'); + }); + + it('resetToInitialMapping should throw on Error', async () => { + mockPutCall.mockImplementation(() => { throw new Error('err'); }); + await expect(resetToInitialMapping(orgId, projectId, 'ct', {})).rejects.toThrow('err'); + }); + + it('resetToInitialMapping should throw generic on non-Error', async () => { + mockPutCall.mockImplementation(() => { throw 0; }); + await expect(resetToInitialMapping(orgId, projectId, 'ct', {})).rejects.toThrow('Unknown error'); + }); + + it('getExistingContentTypes should throw on Error', async () => { + mockGetCall.mockImplementation(() => { throw new Error('err'); }); + await expect(getExistingContentTypes('p')).rejects.toThrow('err'); + }); + + it('getExistingContentTypes should throw generic on non-Error', async () => { + mockGetCall.mockImplementation(() => { throw 0; }); + await expect(getExistingContentTypes('p')).rejects.toThrow('Unknown error'); + }); + + it('getExistingGlobalFields should throw on Error', async () => { + mockGetCall.mockImplementation(() => { throw new Error('err'); }); + await expect(getExistingGlobalFields('p')).rejects.toThrow('err'); + }); + + it('getExistingGlobalFields should throw generic on non-Error', async () => { + mockGetCall.mockImplementation(() => { throw 0; }); + await expect(getExistingGlobalFields('p')).rejects.toThrow('Unknown error'); + }); + + it('updateContentMapper should throw on Error', async () => { + mockPatchCall.mockImplementation(() => { throw new Error('err'); }); + await expect(updateContentMapper(orgId, projectId, {})).rejects.toThrow('err'); + }); + + it('updateContentMapper should throw generic on non-Error', async () => { + mockPatchCall.mockImplementation(() => { throw 0; }); + await expect(updateContentMapper(orgId, projectId, {})).rejects.toThrow('Unknown error'); + }); + + it('getExistingTaxonomies should throw on Error', async () => { + mockGetCall.mockImplementation(() => { throw new Error('err'); }); + await expect(getExistingTaxonomies(projectId)).rejects.toThrow('err'); + }); + + it('getExistingTaxonomies should throw generic on non-Error', async () => { + mockGetCall.mockImplementation(() => { throw 0; }); + await expect(getExistingTaxonomies(projectId)).rejects.toThrow('Unknown error'); + }); + }); +}); diff --git a/ui/tests/unit/services/project.service.test.ts b/ui/tests/unit/services/project.service.test.ts new file mode 100644 index 000000000..32a182563 --- /dev/null +++ b/ui/tests/unit/services/project.service.test.ts @@ -0,0 +1,222 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockGetCall, mockPostCall, mockPutCall, mockDeleteCall } = vi.hoisted(() => ({ + mockGetCall: vi.fn(), + mockPostCall: vi.fn(), + mockPutCall: vi.fn(), + mockDeleteCall: vi.fn() +})); + +vi.mock('../../../src/services/api/service', () => ({ + getCall: mockGetCall, + postCall: mockPostCall, + putCall: mockPutCall, + deleteCall: mockDeleteCall +})); + +vi.mock('../../../src/utilities/constants', () => ({ + API_VERSION: 'v2', + BASE_API_URL: 'http://localhost:5001/', + TOKEN_KEY: 'access_token', + TOKEN: null, + HEADERS: {} +})); + +vi.mock('../../../src/utilities/functions', () => ({ + getDataFromLocalStorage: vi.fn(() => 'mock-app-token') +})); + +import { + getAllProjects, + getProject, + createProject, + updateProject, + deleteProject, + getMigratedStacks, + getAuditData +} from '../../../src/services/api/project.service'; + +describe('services/api/project.service', () => { + const orgId = 'org-123'; + const projectId = 'proj-456'; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('getAllProjects', () => { + it('should call getCall with the correct URL and options', async () => { + const mockResponse = { status: 200, data: { projects: [] } }; + mockGetCall.mockResolvedValue(mockResponse); + + const result = await getAllProjects(orgId); + expect(mockGetCall).toHaveBeenCalledWith( + `v2/org/${orgId}/project`, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + expect(result).toEqual(mockResponse); + }); + }); + + describe('getProject', () => { + it('should call getCall with org and project ID', async () => { + const mockResponse = { status: 200, data: { project: {} } }; + mockGetCall.mockResolvedValue(mockResponse); + + const result = await getProject(orgId, projectId); + expect(mockGetCall).toHaveBeenCalledWith( + `v2/org/${orgId}/project/${projectId}`, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + expect(result).toEqual(mockResponse); + }); + }); + + describe('createProject', () => { + it('should call postCall with project data', async () => { + const data = { name: 'New Project' }; + const mockResponse = { status: 201, data: { project: data } }; + mockPostCall.mockResolvedValue(mockResponse); + + const result = await createProject(orgId, data); + expect(mockPostCall).toHaveBeenCalledWith( + `v2/org/${orgId}/project/`, + data, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + expect(result).toEqual(mockResponse); + }); + }); + + describe('updateProject', () => { + it('should call putCall with updated data', async () => { + const data = { name: 'Updated Project' }; + const mockResponse = { status: 200, data: {} }; + mockPutCall.mockResolvedValue(mockResponse); + + const result = await updateProject(orgId, projectId, data); + expect(mockPutCall).toHaveBeenCalledWith( + `v2/org/${orgId}/project/${projectId}`, + data, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + expect(result).toEqual(mockResponse); + }); + }); + + describe('deleteProject', () => { + it('should call deleteCall with the correct URL', async () => { + const mockResponse = { status: 204, data: {} }; + mockDeleteCall.mockResolvedValue(mockResponse); + + const result = await deleteProject(orgId, projectId); + expect(mockDeleteCall).toHaveBeenCalledWith( + `v2/org/${orgId}/project/${projectId}`, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + expect(result).toEqual(mockResponse); + }); + }); + + describe('getMigratedStacks', () => { + it('should call getCall for migrated stacks', async () => { + const mockResponse = { status: 200, data: { stacks: [] } }; + mockGetCall.mockResolvedValue(mockResponse); + + const result = await getMigratedStacks(orgId, projectId); + expect(mockGetCall).toHaveBeenCalledWith( + `v2/org/${orgId}/project/${projectId}/get-migrated-stacks`, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + expect(result).toEqual(mockResponse); + }); + }); + + describe('getAuditData', () => { + it('should call getCall with all parameters in the URL', async () => { + const mockResponse = { status: 200, data: { logs: [] } }; + mockGetCall.mockResolvedValue(mockResponse); + + const result = await getAuditData(orgId, projectId, 'stack-1', 'content_types', 0, 10, 0, 10, 'search', 'all'); + expect(mockGetCall).toHaveBeenCalledWith( + `v2/migration/get_audit_data/${orgId}/${projectId}/stack-1/content_types/0/10/0/10/search/all`, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + expect(result).toEqual(mockResponse); + }); + + it('should throw with message when getCall throws an Error', async () => { + mockGetCall.mockImplementation(() => { throw new Error('fail'); }); + await expect(getAuditData(orgId, projectId, 's', 'm', 0, 10, 0, 10, '', 'all')) + .rejects.toThrow('Error in fetching audit data: fail'); + }); + + it('should throw generic message when getCall throws a non-Error', async () => { + mockGetCall.mockImplementation(() => { throw null; }); + await expect(getAuditData(orgId, projectId, 's', 'm', 0, 10, 0, 10, '', 'all')) + .rejects.toThrow('Unknown error in fetching audit data'); + }); + }); + + describe('error handling for all project service functions', () => { + it('getAllProjects should throw on Error', async () => { + mockGetCall.mockImplementation(() => { throw new Error('err'); }); + await expect(getAllProjects(orgId)).rejects.toThrow('Error in userSession: err'); + }); + + it('getAllProjects should throw generic on non-Error', async () => { + mockGetCall.mockImplementation(() => { throw 0; }); + await expect(getAllProjects(orgId)).rejects.toThrow('Unknown error in userSession'); + }); + + it('getProject should throw on Error', async () => { + mockGetCall.mockImplementation(() => { throw new Error('err'); }); + await expect(getProject(orgId, projectId)).rejects.toThrow('Error in userSession: err'); + }); + + it('getProject should throw generic on non-Error', async () => { + mockGetCall.mockImplementation(() => { throw 0; }); + await expect(getProject(orgId, projectId)).rejects.toThrow('Unknown error in userSession'); + }); + + it('createProject should throw on Error', async () => { + mockPostCall.mockImplementation(() => { throw new Error('err'); }); + await expect(createProject(orgId, {})).rejects.toThrow('Error in userSession: err'); + }); + + it('createProject should throw generic on non-Error', async () => { + mockPostCall.mockImplementation(() => { throw 0; }); + await expect(createProject(orgId, {})).rejects.toThrow('Unknown error in userSession'); + }); + + it('updateProject should throw on Error', async () => { + mockPutCall.mockImplementation(() => { throw new Error('err'); }); + await expect(updateProject(orgId, projectId, {})).rejects.toThrow('Error in userSession: err'); + }); + + it('updateProject should throw generic on non-Error', async () => { + mockPutCall.mockImplementation(() => { throw 0; }); + await expect(updateProject(orgId, projectId, {})).rejects.toThrow('Unknown error in userSession'); + }); + + it('deleteProject should throw on Error', async () => { + mockDeleteCall.mockImplementation(() => { throw new Error('err'); }); + await expect(deleteProject(orgId, projectId)).rejects.toThrow('Error in userSession: err'); + }); + + it('deleteProject should throw generic on non-Error', async () => { + mockDeleteCall.mockImplementation(() => { throw 0; }); + await expect(deleteProject(orgId, projectId)).rejects.toThrow('Unknown error in userSession'); + }); + + it('getMigratedStacks should throw on Error', async () => { + mockGetCall.mockImplementation(() => { throw new Error('err'); }); + await expect(getMigratedStacks(orgId, projectId)).rejects.toThrow('Error in userSession: err'); + }); + + it('getMigratedStacks should throw generic on non-Error', async () => { + mockGetCall.mockImplementation(() => { throw 0; }); + await expect(getMigratedStacks(orgId, projectId)).rejects.toThrow('Unknown error in userSession'); + }); + }); +}); diff --git a/ui/tests/unit/services/service.test.ts b/ui/tests/unit/services/service.test.ts new file mode 100644 index 000000000..dd147d8f3 --- /dev/null +++ b/ui/tests/unit/services/service.test.ts @@ -0,0 +1,141 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import axios from 'axios'; +import { getCall, postCall, putCall, deleteCall, patchCall } from '../../../src/services/api/service'; + +vi.mock('axios'); +vi.mock('../../../src/utilities/constants', () => ({ + BASE_API_URL: 'http://localhost:5001/' +})); + +const mockedAxios = vi.mocked(axios); + +describe('services/api/service', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('getCall', () => { + it('should make a GET request with the correct URL', async () => { + const mockResponse = { data: { message: 'ok' }, status: 200 }; + mockedAxios.get.mockResolvedValue(mockResponse); + + const result = await getCall('v2/test'); + expect(mockedAxios.get).toHaveBeenCalledWith('http://localhost:5001/v2/test', {}); + expect(result).toEqual(mockResponse); + }); + + it('should pass options to axios', async () => { + const mockResponse = { data: {}, status: 200 }; + mockedAxios.get.mockResolvedValue(mockResponse); + const options = { headers: { Authorization: 'Bearer token' } }; + + await getCall('v2/test', options); + expect(mockedAxios.get).toHaveBeenCalledWith('http://localhost:5001/v2/test', options); + }); + + it('should return error response on failure', async () => { + const errorResponse = { status: 500, data: { message: 'Server Error' } }; + mockedAxios.get.mockRejectedValue({ response: errorResponse }); + + const result = await getCall('v2/test'); + expect(result).toEqual(errorResponse); + }); + }); + + describe('postCall', () => { + it('should make a POST request with data', async () => { + const mockResponse = { data: { id: 1 }, status: 201 }; + mockedAxios.post.mockResolvedValue(mockResponse); + const data = { name: 'test' }; + + const result = await postCall('v2/test', data); + expect(mockedAxios.post).toHaveBeenCalledWith('http://localhost:5001/v2/test', data, undefined); + expect(result).toEqual(mockResponse); + }); + + it('should pass options to axios', async () => { + mockedAxios.post.mockResolvedValue({ data: {}, status: 200 }); + const options = { headers: { 'Content-Type': 'application/json' } }; + + await postCall('v2/test', { key: 'val' }, options); + expect(mockedAxios.post).toHaveBeenCalledWith( + 'http://localhost:5001/v2/test', + { key: 'val' }, + options + ); + }); + + it('should return error response on failure', async () => { + const errorResponse = { status: 400, data: { message: 'Bad Request' } }; + mockedAxios.post.mockRejectedValue({ response: errorResponse }); + + const result = await postCall('v2/test', {}); + expect(result).toEqual(errorResponse); + }); + }); + + describe('putCall', () => { + it('should make a PUT request with data', async () => { + const mockResponse = { data: {}, status: 200 }; + mockedAxios.put.mockResolvedValue(mockResponse); + + const result = await putCall('v2/test', { updated: true }); + expect(mockedAxios.put).toHaveBeenCalledWith( + 'http://localhost:5001/v2/test', + { updated: true }, + undefined + ); + expect(result).toEqual(mockResponse); + }); + + it('should return error response on failure', async () => { + const errorResponse = { status: 500, data: {} }; + mockedAxios.put.mockRejectedValue({ response: errorResponse }); + + const result = await putCall('v2/test', {}); + expect(result).toEqual(errorResponse); + }); + }); + + describe('deleteCall', () => { + it('should make a DELETE request', async () => { + const mockResponse = { data: {}, status: 204 }; + mockedAxios.delete.mockResolvedValue(mockResponse); + + const result = await deleteCall('v2/test'); + expect(mockedAxios.delete).toHaveBeenCalledWith('http://localhost:5001/v2/test', undefined); + expect(result).toEqual(mockResponse); + }); + + it('should return error response on failure', async () => { + const errorResponse = { status: 404, data: {} }; + mockedAxios.delete.mockRejectedValue({ response: errorResponse }); + + const result = await deleteCall('v2/test'); + expect(result).toEqual(errorResponse); + }); + }); + + describe('patchCall', () => { + it('should make a PATCH request with data', async () => { + const mockResponse = { data: {}, status: 200 }; + mockedAxios.patch.mockResolvedValue(mockResponse); + + const result = await patchCall('v2/test', { patched: true }); + expect(mockedAxios.patch).toHaveBeenCalledWith( + 'http://localhost:5001/v2/test', + { patched: true }, + undefined + ); + expect(result).toEqual(mockResponse); + }); + + it('should return error response on failure', async () => { + const errorResponse = { status: 422, data: {} }; + mockedAxios.patch.mockRejectedValue({ response: errorResponse }); + + const result = await patchCall('v2/test', {}); + expect(result).toEqual(errorResponse); + }); + }); +}); diff --git a/ui/tests/unit/services/stacks.service.test.ts b/ui/tests/unit/services/stacks.service.test.ts new file mode 100644 index 000000000..1504552c1 --- /dev/null +++ b/ui/tests/unit/services/stacks.service.test.ts @@ -0,0 +1,143 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockGetCall, mockPostCall } = vi.hoisted(() => ({ + mockGetCall: vi.fn(), + mockPostCall: vi.fn() +})); + +vi.mock('../../../src/services/api/service', () => ({ + getCall: mockGetCall, + postCall: mockPostCall +})); + +vi.mock('../../../src/utilities/constants', () => ({ + API_VERSION: 'v2', + BASE_API_URL: 'http://localhost:5001/', + TOKEN_KEY: 'access_token', + TOKEN: null, + HEADERS: {} +})); + +vi.mock('../../../src/utilities/functions', () => ({ + getDataFromLocalStorage: vi.fn(() => 'mock-app-token') +})); + +import { + getAllStacksInOrg, + createStacksInOrg, + getStackStatus, + getStackLocales +} from '../../../src/services/api/stacks.service'; + +describe('services/api/stacks.service', () => { + const orgId = 'org-123'; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('getAllStacksInOrg', () => { + it('should call getCall with org and search text', async () => { + const mockResponse = { status: 200, data: { stacks: [] } }; + mockGetCall.mockResolvedValue(mockResponse); + + const result = await getAllStacksInOrg(orgId, 'test'); + expect(mockGetCall).toHaveBeenCalledWith( + 'v2/org/org-123/stacks/test?', + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + expect(result).toEqual(mockResponse); + }); + + it('should return error on failure', async () => { + const error = new Error('Network error'); + mockGetCall.mockRejectedValue(error); + + const result = await getAllStacksInOrg(orgId, ''); + expect(result).toEqual(error); + }); + }); + + describe('createStacksInOrg', () => { + it('should call postCall with stack data', async () => { + const stackData = { name: 'New Stack', master_locale: 'en-us' }; + const mockResponse = { status: 201, data: { stack: stackData } }; + mockPostCall.mockResolvedValue(mockResponse); + + const result = await createStacksInOrg(orgId, stackData as any); + expect(mockPostCall).toHaveBeenCalledWith( + 'v2/org/org-123/stacks', + stackData, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + expect(result).toEqual(mockResponse); + }); + }); + + describe('getStackStatus', () => { + it('should call postCall with stack_api_key', async () => { + const mockResponse = { status: 200, data: { status: 'active' } }; + mockPostCall.mockResolvedValue(mockResponse); + + const result = await getStackStatus(orgId, 'stack-api-key-123'); + expect(mockPostCall).toHaveBeenCalledWith( + 'v2/org/org-123/stack_status', + { stack_api_key: 'stack-api-key-123' }, + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + expect(result).toEqual(mockResponse); + }); + }); + + describe('getStackLocales', () => { + it('should call getCall with locales endpoint', async () => { + const mockResponse = { status: 200, data: { locales: ['en-us'] } }; + mockGetCall.mockResolvedValue(mockResponse); + + const result = await getStackLocales(orgId); + expect(mockGetCall).toHaveBeenCalledWith( + 'v2/org/org-123/locales', + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + expect(result).toEqual(mockResponse); + }); + + it('should throw with message on Error', async () => { + mockGetCall.mockImplementation(() => { throw new Error('Locale fetch failed'); }); + await expect(getStackLocales(orgId)).rejects.toThrow('Locale fetch failed'); + }); + + it('should throw generic message on non-Error', async () => { + mockGetCall.mockImplementation(() => { throw null; }); + await expect(getStackLocales(orgId)).rejects.toThrow('Unknown error'); + }); + }); + + describe('createStacksInOrg - error handling', () => { + it('should throw with message on Error', async () => { + mockPostCall.mockImplementation(() => { throw new Error('Create failed'); }); + await expect(createStacksInOrg(orgId, { name: 'x' } as any)) + .rejects.toThrow('Error in userSession: Create failed'); + }); + + it('should throw generic message on non-Error', async () => { + mockPostCall.mockImplementation(() => { throw 0; }); + await expect(createStacksInOrg(orgId, { name: 'x' } as any)) + .rejects.toThrow('Unknown error in userSession'); + }); + }); + + describe('getStackStatus - error handling', () => { + it('should throw with message on Error', async () => { + mockPostCall.mockImplementation(() => { throw new Error('Status failed'); }); + await expect(getStackStatus(orgId, 'key')) + .rejects.toThrow('Error in userSession: Status failed'); + }); + + it('should throw generic message on non-Error', async () => { + mockPostCall.mockImplementation(() => { throw 0; }); + await expect(getStackStatus(orgId, 'key')) + .rejects.toThrow('Unknown error in userSession'); + }); + }); +}); diff --git a/ui/tests/unit/services/upload.service.test.ts b/ui/tests/unit/services/upload.service.test.ts new file mode 100644 index 000000000..723d34a01 --- /dev/null +++ b/ui/tests/unit/services/upload.service.test.ts @@ -0,0 +1,221 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import axios from 'axios'; + +vi.mock('axios'); + +vi.mock('../../../src/utilities/constants', () => ({ + UPLOAD_FILE_RELATIVE_URL: 'http://localhost:5002/', + BASE_API_URL: 'http://localhost:5001/', + TOKEN_KEY: 'access_token', + TOKEN: null, + HEADERS: {} +})); + +vi.mock('../../../src/utilities/functions', () => ({ + getDataFromLocalStorage: vi.fn(() => 'mock-app-token') +})); + +import { + getCall, + postCall, + putCall, + uploadFilePath, + fileValidation, + getConfig, + getRestrictedKeywords, + getLocales +} from '../../../src/services/api/upload.service'; + +const mockedAxios = vi.mocked(axios); + +describe('services/api/upload.service', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('getCall', () => { + it('should make a GET request and return response', async () => { + const mockResponse = { data: { result: true }, status: 200 }; + mockedAxios.get.mockResolvedValue(mockResponse); + + const result = await getCall('http://localhost:5002/config'); + expect(mockedAxios.get).toHaveBeenCalledWith('http://localhost:5002/config', {}); + expect(result).toEqual(mockResponse); + }); + + it('should return error response on failure', async () => { + const errorResponse = { status: 500, data: {} }; + mockedAxios.get.mockRejectedValue({ response: errorResponse }); + + const result = await getCall('http://localhost:5002/config'); + expect(result).toEqual(errorResponse); + }); + }); + + describe('postCall', () => { + it('should make a POST request and return response', async () => { + const mockResponse = { data: {}, status: 200 }; + mockedAxios.post.mockResolvedValue(mockResponse); + + const result = await postCall('http://localhost:5002/upload', { file: 'data' } as any); + expect(mockedAxios.post).toHaveBeenCalledWith( + 'http://localhost:5002/upload', + { file: 'data' }, + undefined + ); + expect(result).toEqual(mockResponse); + }); + + it('should throw error on failure', async () => { + mockedAxios.post.mockRejectedValue(new Error('Upload failed')); + + await expect(postCall('http://localhost:5002/upload', {} as any)).rejects.toThrow( + 'Upload failed' + ); + }); + }); + + describe('putCall', () => { + it('should make a PUT request and return response', async () => { + const mockResponse = { data: {}, status: 200 }; + mockedAxios.put.mockResolvedValue(mockResponse); + + const result = await putCall('http://localhost:5002/update', { data: 'test' } as any); + expect(mockedAxios.put).toHaveBeenCalledWith( + 'http://localhost:5002/update', + { data: 'test' }, + undefined + ); + expect(result).toEqual(mockResponse); + }); + + it('should return error response on failure', async () => { + const errorResponse = { status: 400, data: {} }; + mockedAxios.put.mockRejectedValue({ response: errorResponse }); + + const result = await putCall('http://localhost:5002/update', {} as any); + expect(result).toEqual(errorResponse); + }); + }); + + describe('uploadFilePath', () => { + it('should return the upload URL', () => { + expect(uploadFilePath()).toBe('http://localhost:5002/upload'); + }); + }); + + describe('fileValidation', () => { + it('should call getCall with validator endpoint and correct headers', async () => { + const mockResponse = { data: { valid: true }, status: 200 }; + mockedAxios.get.mockResolvedValue(mockResponse); + + const result = await fileValidation('proj-1', 'cs'); + expect(mockedAxios.get).toHaveBeenCalledWith( + 'http://localhost:5002/validator', + expect.objectContaining({ + headers: { + app_token: 'mock-app-token', + projectId: 'proj-1', + affix: 'cs' + } + }) + ); + expect(result).toEqual(mockResponse); + }); + + it('should use "cs" as default affix', async () => { + mockedAxios.get.mockResolvedValue({ data: {}, status: 200 }); + + await fileValidation('proj-1'); + expect(mockedAxios.get).toHaveBeenCalledWith( + 'http://localhost:5002/validator', + expect.objectContaining({ + headers: expect.objectContaining({ affix: 'cs' }) + }) + ); + }); + }); + + describe('getConfig', () => { + it('should call getCall with config endpoint', async () => { + const mockResponse = { data: { maxFileSize: 100 }, status: 200 }; + mockedAxios.get.mockResolvedValue(mockResponse); + + const result = await getConfig(); + expect(mockedAxios.get).toHaveBeenCalledWith('http://localhost:5002/config', {}); + expect(result).toEqual(mockResponse); + }); + }); + + describe('getRestrictedKeywords', () => { + it('should call getCall with contentstack API URL', async () => { + const mockResponse = { data: { keywords: [] }, status: 200 }; + mockedAxios.get.mockResolvedValue(mockResponse); + + const result = await getRestrictedKeywords(); + expect(mockedAxios.get).toHaveBeenCalledWith( + 'https://api.contentstack.io/v3/restricted_uids', + {} + ); + expect(result).toEqual(mockResponse); + }); + }); + + describe('getLocales', () => { + it('should call getCall with locales endpoint and auth headers', async () => { + const mockResponse = { data: { locales: [] }, status: 200 }; + mockedAxios.get.mockResolvedValue(mockResponse); + + const result = await getLocales('api-key-123'); + expect(mockedAxios.get).toHaveBeenCalledWith( + 'https://api.contentstack.io/v3/locales', + expect.objectContaining({ + headers: { + authtoken: 'mock-app-token', + api_key: 'api-key-123' + } + }) + ); + expect(result).toEqual(mockResponse); + }); + + it('should handle axios failure and return error response', async () => { + const errorResponse = { status: 500, data: { message: 'fail' } }; + mockedAxios.get.mockRejectedValue({ response: errorResponse }); + + const result = await getLocales('api-key-123'); + expect(result).toEqual(errorResponse); + }); + }); + + describe('error handling for upload service functions', () => { + it('postCall should throw generic on non-Error', async () => { + mockedAxios.post.mockRejectedValue('string-error'); + await expect(postCall('http://test', {} as any)).rejects.toThrow('Unknown error in userSession'); + }); + + it('fileValidation should return response on axios failure', async () => { + const errorResponse = { status: 400, data: { valid: false } }; + mockedAxios.get.mockRejectedValue({ response: errorResponse }); + + const result = await fileValidation('p'); + expect(result).toEqual(errorResponse); + }); + + it('getConfig should return error response on axios failure', async () => { + const errorResponse = { status: 500, data: {} }; + mockedAxios.get.mockRejectedValue({ response: errorResponse }); + + const result = await getConfig(); + expect(result).toEqual(errorResponse); + }); + + it('getRestrictedKeywords should return error response on axios failure', async () => { + const errorResponse = { status: 403, data: {} }; + mockedAxios.get.mockRejectedValue({ response: errorResponse }); + + const result = await getRestrictedKeywords(); + expect(result).toEqual(errorResponse); + }); + }); +}); diff --git a/ui/tests/unit/services/user.service.test.ts b/ui/tests/unit/services/user.service.test.ts new file mode 100644 index 000000000..e8d6919eb --- /dev/null +++ b/ui/tests/unit/services/user.service.test.ts @@ -0,0 +1,90 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockGetCall } = vi.hoisted(() => ({ + mockGetCall: vi.fn() +})); + +vi.mock('../../../src/services/api/service', () => ({ + getCall: mockGetCall +})); + +vi.mock('../../../src/utilities/constants', () => ({ + API_VERSION: 'v2', + BASE_API_URL: 'http://localhost:5001/', + TOKEN_KEY: 'access_token', + TOKEN: null, + HEADERS: {} +})); + +vi.mock('../../../src/utilities/functions', () => ({ + getDataFromLocalStorage: vi.fn(() => 'mock-app-token') +})); + +import { getUser, getAllLocales } from '../../../src/services/api/user.service'; + +describe('services/api/user.service', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('getUser', () => { + it('should call getCall with user profile endpoint', async () => { + const mockResponse = { + status: 200, + data: { user: { email: 'test@example.com', first_name: 'Test' } } + }; + mockGetCall.mockResolvedValue(mockResponse); + + const result = await getUser(); + expect(mockGetCall).toHaveBeenCalledWith( + 'v2/user/profile', + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + expect(result).toEqual(mockResponse); + }); + + it('should return error response on failure', async () => { + const errorResponse = { status: 401, data: { message: 'Unauthorized' } }; + mockGetCall.mockResolvedValue(errorResponse); + + const result = await getUser(); + expect(result).toEqual(errorResponse); + }); + }); + + describe('getAllLocales', () => { + it('should call getCall with locales endpoint for the given org', async () => { + const mockResponse = { status: 200, data: { locales: ['en-us', 'fr-fr'] } }; + mockGetCall.mockResolvedValue(mockResponse); + + const result = await getAllLocales('org-123'); + expect(mockGetCall).toHaveBeenCalledWith( + 'v2/org/org-123/locales', + expect.objectContaining({ headers: { app_token: 'mock-app-token' } }) + ); + expect(result).toEqual(mockResponse); + }); + + it('should throw with message when getCall throws an Error', async () => { + mockGetCall.mockImplementation(() => { throw new Error('Timeout'); }); + await expect(getAllLocales('org-123')).rejects.toThrow('Error in userSession: Timeout'); + }); + + it('should throw generic message when getCall throws a non-Error', async () => { + mockGetCall.mockImplementation(() => { throw null; }); + await expect(getAllLocales('org-123')).rejects.toThrow('Unknown error in userSession'); + }); + }); + + describe('getUser - error handling', () => { + it('should throw with message when getCall throws an Error', async () => { + mockGetCall.mockImplementation(() => { throw new Error('Auth error'); }); + await expect(getUser()).rejects.toThrow('Error in userSession: Auth error'); + }); + + it('should throw generic message when getCall throws a non-Error', async () => { + mockGetCall.mockImplementation(() => { throw undefined; }); + await expect(getUser()).rejects.toThrow('Unknown error in userSession'); + }); + }); +}); diff --git a/ui/tests/unit/store/authMiddleware.test.ts b/ui/tests/unit/store/authMiddleware.test.ts new file mode 100644 index 000000000..39cfbf3a8 --- /dev/null +++ b/ui/tests/unit/store/authMiddleware.test.ts @@ -0,0 +1,53 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockGetUserDetails } = vi.hoisted(() => ({ + mockGetUserDetails: vi.fn() +})); + +vi.mock('../../../src/store/slice/authSlice', () => ({ + getUserDetails: mockGetUserDetails, + default: (state = {}) => state +})); + +import authMiddleware from '../../../src/store/middleware/authMiddleware'; + +describe('store/middleware/authMiddleware', () => { + let dispatch: ReturnType; + let next: ReturnType; + + beforeEach(() => { + dispatch = vi.fn(); + next = vi.fn(); + vi.clearAllMocks(); + }); + + it('should dispatch getUserDetails on @@INIT action', () => { + const middleware = authMiddleware({ dispatch, getState: vi.fn() } as any)(next); + middleware({ type: '@@INIT' }); + + expect(dispatch).toHaveBeenCalledWith(mockGetUserDetails()); + }); + + it('should call next for any action', () => { + const middleware = authMiddleware({ dispatch, getState: vi.fn() } as any)(next); + const action = { type: 'SOME_ACTION' }; + + middleware(action); + expect(next).toHaveBeenCalledWith(action); + }); + + it('should not dispatch getUserDetails for non-INIT actions', () => { + const middleware = authMiddleware({ dispatch, getState: vi.fn() } as any)(next); + middleware({ type: 'OTHER_ACTION' }); + + expect(dispatch).not.toHaveBeenCalled(); + }); + + it('should always pass action to next', () => { + const middleware = authMiddleware({ dispatch, getState: vi.fn() } as any)(next); + const action = { type: '@@INIT' }; + + const result = middleware(action); + expect(next).toHaveBeenCalledWith(action); + }); +}); diff --git a/ui/tests/unit/store/authSlice.test.ts b/ui/tests/unit/store/authSlice.test.ts new file mode 100644 index 000000000..1c908c45c --- /dev/null +++ b/ui/tests/unit/store/authSlice.test.ts @@ -0,0 +1,187 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { configureStore } from '@reduxjs/toolkit'; + +vi.mock('../../../src/utilities/functions', () => ({ + getDataFromLocalStorage: vi.fn(() => null), + clearLocalStorage: vi.fn(() => true), + isEmptyString: vi.fn((str: string | undefined) => !str || str.trim().length < 1), + validateArray: vi.fn((arr: any[]) => Array.isArray(arr) && arr.length > 0) +})); + +vi.mock('../../../src/services/api/user.service', () => ({ + getUser: vi.fn() +})); + +import authReducer, { + setAuthToken, + reInitiliseState, + setOrganisationsList, + setSelectedOrganisation, + setUser, + clearOrganisationData, + clearAuthToken, + getUserDetails +} from '../../../src/store/slice/authSlice'; +import { getUser } from '../../../src/services/api/user.service'; + +const createTestStore = (preloadedState?: any) => + configureStore({ + reducer: { authentication: authReducer }, + preloadedState: preloadedState + ? { authentication: preloadedState } + : undefined + }); + +describe('store/slice/authSlice', () => { + describe('reducers', () => { + it('should return the initial state', () => { + const store = createTestStore(); + const state = store.getState().authentication; + + expect(state).toHaveProperty('authToken'); + expect(state).toHaveProperty('user'); + expect(state).toHaveProperty('isAuthenticated'); + expect(state).toHaveProperty('organisationsList'); + expect(state).toHaveProperty('selectedOrganisation'); + }); + + describe('setAuthToken', () => { + it('should set auth token and authentication flag', () => { + const store = createTestStore(); + store.dispatch(setAuthToken({ authToken: 'new-token', isAuthenticated: true })); + + const state = store.getState().authentication; + expect(state.authToken).toBe('new-token'); + expect(state.isAuthenticated).toBe(true); + }); + }); + + describe('setUser', () => { + it('should merge user data with existing state', () => { + const store = createTestStore(); + store.dispatch(setUser({ first_name: 'John', last_name: 'Doe' })); + + const state = store.getState().authentication; + expect(state.user.first_name).toBe('John'); + expect(state.user.last_name).toBe('Doe'); + }); + }); + + describe('reInitiliseState', () => { + it('should reset state to initial values', () => { + const store = createTestStore(); + store.dispatch(setAuthToken({ authToken: 'token', isAuthenticated: true })); + store.dispatch(reInitiliseState()); + + const state = store.getState().authentication; + expect(state.authToken).toBe(''); + expect(state.isAuthenticated).toBe(false); + expect(state.organisationsList).toEqual([]); + }); + }); + + describe('setOrganisationsList', () => { + it('should set the organisations list', () => { + const store = createTestStore(); + const orgs = [ + { uid: 'org-1', value: 'org-1', label: 'Org 1' }, + { uid: 'org-2', value: 'org-2', label: 'Org 2' } + ]; + store.dispatch(setOrganisationsList(orgs)); + + const state = store.getState().authentication; + expect(state.organisationsList).toEqual(orgs); + }); + }); + + describe('setSelectedOrganisation', () => { + it('should set the selected organisation', () => { + const store = createTestStore(); + const org = { uid: 'org-1', value: 'org-1', label: 'Org 1' }; + store.dispatch(setSelectedOrganisation(org)); + + const state = store.getState().authentication; + expect(state.selectedOrganisation).toEqual(org); + }); + }); + + describe('clearOrganisationData', () => { + it('should clear organisations list and selected organisation', () => { + const store = createTestStore(); + store.dispatch( + setOrganisationsList([{ uid: 'org-1', value: 'org-1', label: 'Org 1' }]) + ); + store.dispatch(clearOrganisationData()); + + const state = store.getState().authentication; + expect(state.organisationsList).toEqual([]); + expect(state.selectedOrganisation.value).toBe(''); + }); + }); + + describe('clearAuthToken', () => { + it('should clear auth token and set isAuthenticated to false', () => { + const store = createTestStore(); + store.dispatch(setAuthToken({ authToken: 'token', isAuthenticated: true })); + store.dispatch(clearAuthToken()); + + const state = store.getState().authentication; + expect(state.authToken).toBe(''); + expect(state.isAuthenticated).toBe(false); + }); + }); + }); + + describe('getUserDetails thunk', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should set user data on successful response', async () => { + const mockUser = { + email: 'test@example.com', + first_name: 'Test', + orgs: [ + { org_id: 'org-1', org_name: 'Test Org' } + ] + }; + vi.mocked(getUser).mockResolvedValue({ + status: 200, + data: { user: mockUser } + }); + + const store = createTestStore(); + await store.dispatch(getUserDetails()); + + const state = store.getState().authentication; + expect(state.user.email).toBe('test@example.com'); + expect(state.organisationsList).toHaveLength(1); + expect(state.organisationsList[0]).toEqual( + expect.objectContaining({ uid: 'org-1', label: 'Test Org' }) + ); + }); + + it('should reset state on 401 response', async () => { + vi.mocked(getUser).mockResolvedValue({ status: 401 }); + + const store = createTestStore(); + store.dispatch(setAuthToken({ authToken: 'old-token', isAuthenticated: true })); + await store.dispatch(getUserDetails()); + + const state = store.getState().authentication; + expect(state.authToken).toBe(''); + expect(state.isAuthenticated).toBe(false); + }); + + it('should reset state on error', async () => { + vi.mocked(getUser).mockRejectedValue(new Error('Network error')); + + const store = createTestStore(); + store.dispatch(setAuthToken({ authToken: 'old-token', isAuthenticated: true })); + await store.dispatch(getUserDetails()); + + const state = store.getState().authentication; + expect(state.authToken).toBe(''); + }); + }); +}); diff --git a/ui/tests/unit/store/networkSlice.test.ts b/ui/tests/unit/store/networkSlice.test.ts new file mode 100644 index 000000000..65019d73b --- /dev/null +++ b/ui/tests/unit/store/networkSlice.test.ts @@ -0,0 +1,61 @@ +import { describe, it, expect } from 'vitest'; +import { configureStore } from '@reduxjs/toolkit'; +import networkReducer, { + setOnline, + setOffline, + selectNetworkStatus +} from '../../../src/store/slice/networkSlice'; + +const createTestStore = () => + configureStore({ + reducer: { network: networkReducer } + }); + +describe('store/slice/networkSlice', () => { + describe('reducers', () => { + it('should return initial state with navigator.onLine', () => { + const store = createTestStore(); + const state = store.getState().network; + expect(state).toHaveProperty('isOnline'); + }); + + describe('setOnline', () => { + it('should set isOnline to true', () => { + const store = createTestStore(); + store.dispatch(setOffline()); + store.dispatch(setOnline()); + + expect(store.getState().network.isOnline).toBe(true); + }); + }); + + describe('setOffline', () => { + it('should set isOnline to false', () => { + const store = createTestStore(); + store.dispatch(setOffline()); + + expect(store.getState().network.isOnline).toBe(false); + }); + }); + }); + + describe('selectors', () => { + describe('selectNetworkStatus', () => { + it('should return the network online status', () => { + const store = createTestStore(); + store.dispatch(setOnline()); + + const state = store.getState(); + expect(selectNetworkStatus(state as any)).toBe(true); + }); + + it('should return false when offline', () => { + const store = createTestStore(); + store.dispatch(setOffline()); + + const state = store.getState(); + expect(selectNetworkStatus(state as any)).toBe(false); + }); + }); + }); +}); diff --git a/ui/tests/unit/store/storeIndex.test.ts b/ui/tests/unit/store/storeIndex.test.ts new file mode 100644 index 000000000..a58269d81 --- /dev/null +++ b/ui/tests/unit/store/storeIndex.test.ts @@ -0,0 +1,59 @@ +import { describe, it, expect, vi } from 'vitest'; + +vi.mock('../../../src/utilities/functions', () => ({ + getDataFromLocalStorage: vi.fn(() => null), + clearLocalStorage: vi.fn(() => true), + isEmptyString: vi.fn((str: string | undefined) => !str || str.trim().length < 1), + validateArray: vi.fn((arr: any[]) => Array.isArray(arr) && arr.length > 0) +})); + +vi.mock('../../../src/services/api/user.service', () => ({ + getUser: vi.fn() +})); + +import { store, persistor } from '../../../src/store/index'; +import type { RootState, AppDispatch } from '../../../src/store/index'; + +describe('store/index', () => { + it('should export a configured store', () => { + expect(store).toBeDefined(); + expect(store.getState).toBeDefined(); + expect(store.dispatch).toBeDefined(); + }); + + it('should export a persistor', () => { + expect(persistor).toBeDefined(); + }); + + it('should have all required reducer slices in state', () => { + const state = store.getState(); + expect(state).toHaveProperty('migration'); + expect(state).toHaveProperty('authentication'); + expect(state).toHaveProperty('network'); + }); + + it('should have the correct initial authentication state shape', () => { + const state = store.getState(); + expect(state.authentication).toHaveProperty('authToken'); + expect(state.authentication).toHaveProperty('user'); + expect(state.authentication).toHaveProperty('isAuthenticated'); + expect(state.authentication).toHaveProperty('organisationsList'); + expect(state.authentication).toHaveProperty('selectedOrganisation'); + }); + + it('should have the correct initial migration state shape', () => { + const state = store.getState(); + expect(state.migration).toHaveProperty('migrationData'); + expect(state.migration).toHaveProperty('newMigrationData'); + }); + + it('should have the correct initial network state shape', () => { + const state = store.getState(); + expect(state.network).toHaveProperty('isOnline'); + }); + + it('should allow dispatching actions', () => { + const dispatch: AppDispatch = store.dispatch; + expect(typeof dispatch).toBe('function'); + }); +}); diff --git a/ui/tests/unit/utilities/constants.test.ts b/ui/tests/unit/utilities/constants.test.ts new file mode 100644 index 000000000..ee2c057f0 --- /dev/null +++ b/ui/tests/unit/utilities/constants.test.ts @@ -0,0 +1,156 @@ +import { describe, it, expect } from 'vitest'; +import { + assetsRelativeUrl, + TOKEN_KEY, + API_VERSION, + AUTH_ROUTES, + LOGIN_SUCCESSFUL_MESSAGE, + TFA_MESSAGE, + TFA_VIA_SMS_MESSAGE, + API_METHOD, + REGIONS, + CS_URL, + HEADERS, + CS_ENTRIES, + PROJECT_STATUS, + NEW_PROJECT_STATUS, + CONTENT_MAPPING_STATUS, + STATUS_ICON_Mapping, + VALIDATION_DOCUMENTATION_URL, + auditLogsConstants, + HTTP_CODES, + EXECUTION_LOGS_UI_TEXT, + EXECUTION_LOGS_ERROR_TEXT +} from '../../../src/utilities/constants'; + +describe('utilities/constants', () => { + it('should export assetsRelativeUrl', () => { + expect(assetsRelativeUrl).toBe('v3/assets'); + }); + + it('should export TOKEN_KEY', () => { + expect(TOKEN_KEY).toBe('access_token'); + }); + + it('should export API_VERSION from env or default', () => { + expect(API_VERSION).toBeDefined(); + }); + + it('should export AUTH_ROUTES based on API_VERSION', () => { + expect(AUTH_ROUTES).toBe(`${API_VERSION}/auth`); + }); + + it('should export login messages', () => { + expect(LOGIN_SUCCESSFUL_MESSAGE).toBe('Login Successful.'); + expect(TFA_MESSAGE).toBe('Please login using the Two-Factor verification Token'); + expect(TFA_VIA_SMS_MESSAGE).toBe('Two-Factor Authentication Token sent via SMS.'); + }); + + it('should export API_METHOD with all HTTP methods', () => { + expect(API_METHOD).toEqual({ + GET: 'GET', + POST: 'POST', + PATCH: 'PATCH', + PUT: 'PUT', + DELETE: 'DELETE' + }); + }); + + it('should export all REGIONS', () => { + expect(REGIONS).toHaveProperty('NA'); + expect(REGIONS).toHaveProperty('EU'); + expect(REGIONS).toHaveProperty('AZURE_NA'); + expect(REGIONS).toHaveProperty('AZURE_EU'); + expect(REGIONS).toHaveProperty('GCP_NA'); + expect(REGIONS).toHaveProperty('GCP_EU'); + expect(REGIONS).toHaveProperty('AU'); + }); + + it('should export CS_URL with URLs for all regions', () => { + expect(CS_URL.NA).toContain('app.contentstack.com'); + expect(CS_URL.EU).toContain('eu-app.contentstack.com'); + expect(CS_URL.AU).toContain('au-app.contentstack.com'); + expect(Object.keys(CS_URL)).toHaveLength(7); + }); + + it('should export HEADERS with Content-Type', () => { + expect(HEADERS['Content-Type']).toBe('application/json'); + expect(HEADERS).toHaveProperty('Authorization'); + }); + + it('should export CS_ENTRIES with all content type keys', () => { + expect(CS_ENTRIES.HOME_PAGE).toBe('homepage'); + expect(CS_ENTRIES.LOGIN).toBe('login'); + expect(CS_ENTRIES.PROJECTS).toBe('projects'); + expect(CS_ENTRIES.LEGACY_CMS).toBe('legacy_cms'); + expect(CS_ENTRIES.DESTINATION_STACK).toBe('destination_stack'); + expect(CS_ENTRIES.CONTENT_MAPPING).toBe('content_mapping'); + expect(CS_ENTRIES.TEST_MIGRATION).toBe('test_migration'); + expect(CS_ENTRIES.MIGRATION_EXECUTION).toBe('migration_execution'); + expect(CS_ENTRIES.SETTING).toBe('settings'); + expect(CS_ENTRIES.ERROR_HANDLER).toBe('error_handler'); + expect(CS_ENTRIES.NOT_FOUND_ERROR).toEqual({ type: 'error_handler', url: '404' }); + expect(CS_ENTRIES.INTERNAL_SERVER_ERROR).toEqual({ type: 'error_handler', url: '500' }); + expect(CS_ENTRIES.ADD_STACK).toBe('add_stack'); + expect(CS_ENTRIES.UNMAPPED_LOCALE_KEY).toBe('undefined'); + }); + + it('should export PROJECT_STATUS with all statuses', () => { + expect(PROJECT_STATUS['0']).toBe('Draft'); + expect(PROJECT_STATUS['5']).toBe('Migration successful'); + expect(PROJECT_STATUS['6']).toBe('Migration terminated'); + }); + + it('should export NEW_PROJECT_STATUS with all statuses', () => { + expect(NEW_PROJECT_STATUS['0']).toBe('Draft'); + expect(NEW_PROJECT_STATUS['5']).toBe('Completed'); + expect(NEW_PROJECT_STATUS['6']).toBe('Failed'); + }); + + it('should export CONTENT_MAPPING_STATUS', () => { + expect(CONTENT_MAPPING_STATUS['1']).toBe('Mapped'); + expect(CONTENT_MAPPING_STATUS['2']).toBe('Updated'); + expect(CONTENT_MAPPING_STATUS['3']).toBe('Failed'); + expect(CONTENT_MAPPING_STATUS['4']).toBe('All'); + }); + + it('should export STATUS_ICON_Mapping', () => { + expect(STATUS_ICON_Mapping['1']).toBe('CheckedCircle'); + expect(STATUS_ICON_Mapping['2']).toBe('SuccessInverted'); + expect(STATUS_ICON_Mapping['3']).toBe('ErrorInverted'); + }); + + it('should export VALIDATION_DOCUMENTATION_URL', () => { + expect(VALIDATION_DOCUMENTATION_URL.sitecore).toContain('sitecore.pdf'); + expect(VALIDATION_DOCUMENTATION_URL.contentful).toContain('contentful.pdf'); + expect(VALIDATION_DOCUMENTATION_URL.wordpress).toBe(''); + expect(VALIDATION_DOCUMENTATION_URL.drupal).toContain('Drupal.pdf'); + expect(VALIDATION_DOCUMENTATION_URL.aem).toContain('AEM'); + }); + + it('should export auditLogsConstants', () => { + expect(auditLogsConstants.noLogs).toBe('No logs'); + expect(auditLogsConstants.noResult).toBe('No matching result found'); + expect(auditLogsConstants.placeholders.selectStack).toBe('Select Stack'); + expect(auditLogsConstants.filterModal.apply).toBe('Apply'); + }); + + it('should export HTTP_CODES', () => { + expect(HTTP_CODES.OK).toBe(200); + expect(HTTP_CODES.UNAUTHORIZED).toBe(401); + expect(HTTP_CODES.FORBIDDEN).toBe(403); + expect(HTTP_CODES.NOT_FOUND).toBe(404); + expect(HTTP_CODES.SERVER_ERROR).toBe(500); + expect(HTTP_CODES.UNPROCESSABLE_CONTENT).toBe(422); + }); + + it('should export EXECUTION_LOGS_UI_TEXT', () => { + expect(EXECUTION_LOGS_UI_TEXT.SEARCH_PLACEHOLDER).toBe('Search Execution Logs'); + expect(EXECUTION_LOGS_UI_TEXT.EMPTY_STATE_HEADING.NO_LOGS).toBe('No logs'); + expect(EXECUTION_LOGS_UI_TEXT.VIEW_LOG.VIEW_TEXT).toBe('View Log'); + }); + + it('should export EXECUTION_LOGS_ERROR_TEXT', () => { + expect(EXECUTION_LOGS_ERROR_TEXT.ERROR).toBe('Error in Getting Migration Logs'); + }); +}); diff --git a/ui/tests/unit/utilities/functions.test.ts b/ui/tests/unit/utilities/functions.test.ts new file mode 100644 index 000000000..28263c68d --- /dev/null +++ b/ui/tests/unit/utilities/functions.test.ts @@ -0,0 +1,431 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { + Locales, + getLocaleCode, + validateObject, + validateArray, + validateImage, + validateLink, + imageWithSiteDomainUrl, + addDomainInPath, + failtureNotification, + clearMarks, + clearMeasures, + extractWindowObj, + clearLocalStorage, + getDataFromLocalStorage, + setDataInLocalStorage, + getStateFromLocalStorage, + saveStateToLocalStorage, + getDays, + isEmptyString, + shortName, + returnFileSize, + isValidPrefix, + getFileExtension, + getSafeRouterPath +} from '../../../src/utilities/functions'; + +vi.mock('../../../src/utilities/constants', () => ({ + WEBSITE_BASE_URL: 'https://test.contentstack.com' +})); + +vi.mock('@contentstack/venus-components', () => ({ + Notification: vi.fn() +})); + +describe('utilities/functions', () => { + describe('Locales', () => { + it('should contain all supported locale mappings', () => { + expect(Locales.en).toBe('en-us'); + expect(Locales.fr).toBe('fr-fr'); + expect(Locales.de).toBe('de-de'); + expect(Locales.jp).toBe('ja-jp'); + expect(Locales.kr).toBe('ko-kr'); + expect(Locales.cn).toBe('zh-cn'); + expect(Locales.es).toBe('es-mx'); + expect(Locales.pt).toBe('pt-br'); + }); + }); + + describe('getLocaleCode', () => { + it('should return the locale code for a valid key', () => { + expect(getLocaleCode('en')).toBe('en-us'); + expect(getLocaleCode('fr')).toBe('fr-fr'); + }); + + it('should default to "en" when no argument is provided', () => { + expect(getLocaleCode()).toBe('en-us'); + }); + + it('should return undefined for an invalid key', () => { + expect(getLocaleCode('invalid')).toBeUndefined(); + }); + }); + + describe('validateObject', () => { + it('should return true for a non-empty object', () => { + expect(validateObject({ key: 'value' })).toBe(true); + }); + + it('should return false for an empty object', () => { + expect(validateObject({})).toBe(false); + }); + + it('should return false for an array', () => { + expect(validateObject([] as any)).toBe(false); + }); + }); + + describe('validateArray', () => { + it('should return true for a non-empty array', () => { + expect(validateArray([1, 2, 3])).toBe(true); + }); + + it('should return false for an empty array', () => { + expect(validateArray([])).toBe(false); + }); + + it('should return false for a non-array value', () => { + expect(validateArray('string' as any)).toBe(false); + }); + }); + + describe('validateImage', () => { + it('should return a truthy value when image has a url', () => { + expect(validateImage({ url: 'https://example.com/img.png' })).toBeTruthy(); + }); + + it('should return a falsy value when image has no url', () => { + expect(validateImage({ url: '' })).toBeFalsy(); + }); + + it('should return a falsy value for null/undefined', () => { + expect(validateImage(null as any)).toBeFalsy(); + expect(validateImage(undefined as any)).toBeFalsy(); + }); + }); + + describe('validateLink', () => { + it('should return a truthy value when link has a url', () => { + expect(validateLink({ url: 'https://example.com' })).toBeTruthy(); + }); + + it('should return a falsy value when link has no url', () => { + expect(validateLink({ url: '' })).toBeFalsy(); + }); + }); + + describe('imageWithSiteDomainUrl', () => { + it('should replace images.contentstack.io domain', () => { + const url = 'https://images.contentstack.io/v3/assets/image.png'; + const result = imageWithSiteDomainUrl(url); + expect(result).toBe('https://test.contentstack.com/v3/assets/image.png'); + }); + + it('should replace assets.contentstack.io domain', () => { + const url = 'https://assets.contentstack.io/v3/file.pdf'; + const result = imageWithSiteDomainUrl(url); + expect(result).toBe('https://test.contentstack.com/v3/file.pdf'); + }); + + it('should return the original url if no match', () => { + const url = 'https://other-domain.com/image.png'; + expect(imageWithSiteDomainUrl(url)).toBe(url); + }); + }); + + describe('failtureNotification', () => { + it('should call Notification with error type and message', async () => { + const { Notification } = await import('@contentstack/venus-components'); + failtureNotification('Something went wrong'); + + expect(Notification).toHaveBeenCalledWith( + expect.objectContaining({ + text: 'Something went wrong', + type: 'error' + }) + ); + }); + }); + + describe('addDomainInPath', () => { + it('should prepend WEBSITE_BASE_URL to the path', () => { + expect(addDomainInPath('/v3/assets/img.png')).toBe( + 'https://test.contentstack.com/v3/assets/img.png' + ); + }); + }); + + describe('clearMarks', () => { + it('should call performance.clearMarks with a specific name', () => { + const spy = vi.spyOn(performance, 'clearMarks'); + clearMarks('test-mark'); + expect(spy).toHaveBeenCalledWith('test-mark'); + }); + + it('should call performance.clearMarks without arguments when clearAll is true', () => { + const spy = vi.spyOn(performance, 'clearMarks'); + clearMarks('test-mark', true); + expect(spy).toHaveBeenCalledWith(); + }); + }); + + describe('clearMeasures', () => { + it('should call performance.clearMeasures with a specific name', () => { + const spy = vi.spyOn(performance, 'clearMeasures'); + clearMeasures('test-measure'); + expect(spy).toHaveBeenCalledWith('test-measure'); + }); + + it('should call performance.clearMeasures without arguments when clearAll is true', () => { + const spy = vi.spyOn(performance, 'clearMeasures'); + clearMeasures('test-measure', true); + expect(spy).toHaveBeenCalledWith(); + }); + }); + + describe('extractWindowObj', () => { + it('should extract script content containing window.sso', () => { + const html = ''; + expect(extractWindowObj(html)).toBe('window.sso = { token: "abc" };'); + }); + + it('should return null when no script tags are found', () => { + expect(extractWindowObj('no scripts here')).toBeNull(); + }); + + it('should return null when no window.sso script is found', () => { + const html = ''; + expect(extractWindowObj(html)).toBeNull(); + }); + + it('should handle multiple script tags and find the correct one', () => { + const html = + ''; + expect(extractWindowObj(html)).toBe('window.sso = { id: 123 };'); + }); + }); + + describe('localStorage helpers', () => { + describe('clearLocalStorage', () => { + it('should clear localStorage and return true', () => { + localStorage.setItem('key', 'value'); + expect(clearLocalStorage()).toBe(true); + expect(localStorage.length).toBe(0); + }); + }); + + describe('getDataFromLocalStorage', () => { + it('should return the value for an existing key', () => { + localStorage.setItem('testKey', 'testValue'); + expect(getDataFromLocalStorage('testKey')).toBe('testValue'); + }); + + it('should return null for a non-existent key', () => { + expect(getDataFromLocalStorage('nonExistent')).toBeNull(); + }); + }); + + describe('setDataInLocalStorage', () => { + it('should store data and return true', () => { + expect(setDataInLocalStorage('key', 'value')).toBe(true); + expect(localStorage.getItem('key')).toBe('value'); + }); + + it('should return false when localStorage.getItem returns null after setItem', () => { + const getItemSpy = vi.spyOn(Storage.prototype, 'getItem'); + const setItemSpy = vi.spyOn(Storage.prototype, 'setItem'); + + setItemSpy.mockImplementation(() => {}); + getItemSpy.mockReturnValue(null); + + expect(setDataInLocalStorage('key', 'value')).toBe(false); + + setItemSpy.mockRestore(); + getItemSpy.mockRestore(); + }); + }); + }); + + describe('sessionStorage helpers', () => { + describe('getStateFromLocalStorage', () => { + it('should parse and return stored JSON', () => { + sessionStorage.setItem('state', JSON.stringify({ active: true })); + expect(getStateFromLocalStorage('state')).toEqual({ active: true }); + }); + + it('should return null when key does not exist', () => { + expect(getStateFromLocalStorage('missing')).toBeNull(); + }); + }); + + describe('saveStateToLocalStorage', () => { + it('should save state as JSON string', () => { + saveStateToLocalStorage('state', { step1: true }); + expect(sessionStorage.getItem('state')).toBe(JSON.stringify({ step1: true })); + }); + }); + }); + + describe('getDays', () => { + it('should return "X seconds ago" for a recent date', () => { + const recent = new Date(Date.now() - 30 * 1000); + expect(getDays(recent)).toMatch(/seconds? ago/); + }); + + it('should return "X minutes ago" for dates within the last hour', () => { + const minsAgo = new Date(Date.now() - 10 * 60 * 1000); + expect(getDays(minsAgo)).toMatch(/minutes? ago/); + }); + + it('should return "X hours ago" for dates within the last day', () => { + const hoursAgo = new Date(Date.now() - 5 * 3600 * 1000); + expect(getDays(hoursAgo)).toMatch(/hours? ago/); + }); + + it('should return "X days ago" for dates within the last week', () => { + const daysAgo = new Date(Date.now() - 3 * 24 * 3600 * 1000); + expect(getDays(daysAgo)).toMatch(/days? ago/); + }); + + it('should return a formatted date for dates older than a week', () => { + const oldDate = new Date('2023-01-15'); + const result = getDays(oldDate); + expect(result).toMatch(/Jan/); + expect(result).toMatch(/2023/); + }); + }); + + describe('isEmptyString', () => { + it('should return true for undefined', () => { + expect(isEmptyString(undefined)).toBe(true); + }); + + it('should return true for an empty string', () => { + expect(isEmptyString('')).toBe(true); + }); + + it('should return true for a whitespace-only string', () => { + expect(isEmptyString(' ')).toBe(true); + }); + + it('should return false for a non-empty string', () => { + expect(isEmptyString('hello')).toBe(false); + }); + }); + + describe('shortName', () => { + it('should return the name unchanged if <= 25 characters', () => { + expect(shortName('short name')).toBe('short name'); + }); + + it('should truncate and add ellipsis for names > 25 characters', () => { + const longName = 'this-is-a-very-long-project-name-that-exceeds'; + const result = shortName(longName); + expect(result).toContain('...'); + expect(result.length).toBeLessThan(longName.length); + }); + + it('should return falsy for falsy input', () => { + expect(shortName('')).toBe(''); + }); + }); + + describe('returnFileSize', () => { + it('should return bytes for small files', () => { + expect(returnFileSize(500)).toBe('500bytes'); + }); + + it('should return KB for files >= 1024 bytes', () => { + expect(returnFileSize(2048)).toBe('2.0KB'); + }); + + it('should return MB for files >= 1MB', () => { + expect(returnFileSize(2097152)).toBe('2.0MB'); + }); + }); + + describe('isValidPrefix', () => { + it('should return true for valid prefixes (2-5 alpha chars)', () => { + expect(isValidPrefix('cs')).toBe(true); + expect(isValidPrefix('abc')).toBe(true); + expect(isValidPrefix('ABCDE')).toBe(true); + }); + + it('should return false for too short prefix', () => { + expect(isValidPrefix('a')).toBe(false); + }); + + it('should return false for too long prefix', () => { + expect(isValidPrefix('abcdef')).toBe(false); + }); + + it('should return false for prefix with numbers', () => { + expect(isValidPrefix('ab1')).toBe(false); + }); + + it('should return false for prefix with special characters', () => { + expect(isValidPrefix('ab!')).toBe(false); + }); + }); + + describe('getFileExtension', () => { + it('should extract json extension', () => { + expect(getFileExtension('/path/to/file.json')).toBe('json'); + }); + + it('should extract pdf extension', () => { + expect(getFileExtension('file.pdf')).toBe('pdf'); + }); + + it('should extract zip extension', () => { + expect(getFileExtension('archive.zip')).toBe('zip'); + }); + + it('should extract xml extension', () => { + expect(getFileExtension('data.xml')).toBe('xml'); + }); + + it('should return empty string for unsupported extensions', () => { + expect(getFileExtension('file.txt')).toBe(''); + }); + + it('should return empty string for files without extension', () => { + expect(getFileExtension('noextension')).toBe(''); + }); + + it('should handle backslash paths', () => { + expect(getFileExtension('C:\\path\\to\\file.json')).toBe('json'); + }); + + it('should be case insensitive', () => { + expect(getFileExtension('file.JSON')).toBe('json'); + }); + }); + + describe('getSafeRouterPath', () => { + it('should return pathname only by default', () => { + const location = { pathname: '/projects', search: '?id=1', hash: '#top' }; + expect(getSafeRouterPath(location)).toBe('/projects'); + }); + + it('should return full path when includeSearchAndHash is true', () => { + const location = { pathname: '/projects', search: '?id=1', hash: '#section' }; + expect(getSafeRouterPath(location, true)).toBe('/projects?id=1#section'); + }); + + it('should default pathname to "/" when missing', () => { + expect(getSafeRouterPath({} as any)).toBe('/'); + }); + + it('should handle missing search and hash gracefully', () => { + const location = { pathname: '/home' }; + expect(getSafeRouterPath(location, true)).toBe('/home'); + }); + + it('should handle null location', () => { + expect(getSafeRouterPath(null as any)).toBe('/'); + }); + }); +}); diff --git a/ui/tests/unit/utilities/projectDBMapper.test.ts b/ui/tests/unit/utilities/projectDBMapper.test.ts new file mode 100644 index 000000000..01c824590 --- /dev/null +++ b/ui/tests/unit/utilities/projectDBMapper.test.ts @@ -0,0 +1,86 @@ +import { describe, it, expect } from 'vitest'; +import { createObject } from '../../../src/utilities/projectDBMapper'; +import { createMockProject } from '../../fixtures/user.fixture'; + +describe('utilities/projectDBMapper', () => { + describe('createObject', () => { + it('should map project data to the correct structure', () => { + const projectData = createMockProject(); + const result = createObject(projectData); + + expect(result).toHaveProperty('legacy_cms'); + expect(result).toHaveProperty('destination_stack'); + expect(result).toHaveProperty('content_mapping'); + expect(result).toHaveProperty('stackDetails'); + expect(result).toHaveProperty('mapperKeys'); + }); + + it('should correctly map legacy_cms fields', () => { + const projectData = createMockProject(); + const result = createObject(projectData); + + expect(result.legacy_cms.selectedCms.cms_id).toBe('wordpress'); + expect(result.legacy_cms.selectedCms.allowed_file_formats).toEqual(['json']); + expect(result.legacy_cms.affix).toBe('cs'); + expect(result.legacy_cms.file_format).toBe('json'); + }); + + it('should correctly map uploadedFile details', () => { + const projectData = createMockProject(); + const result = createObject(projectData); + + expect(result.legacy_cms.uploadedFile.file_details.localPath).toBe('/path/to/file'); + expect(result.legacy_cms.uploadedFile.file_details.isLocalPath).toBe(true); + expect(result.legacy_cms.uploadedFile.isValidated).toBe(true); + }); + + it('should correctly map AWS details', () => { + const projectData = createMockProject(); + const result = createObject(projectData); + + const awsData = result.legacy_cms.uploadedFile.file_details.awsData; + expect(awsData.awsRegion).toBe('us-east-1'); + expect(awsData.bucketName).toBe('test-bucket'); + expect(awsData.bucketKey).toBe('test-key'); + }); + + it('should correctly map destination_stack fields', () => { + const projectData = createMockProject(); + const result = createObject(projectData); + + expect(result.destination_stack.selectedOrg.value).toBe('org-123'); + expect(result.destination_stack.selectedOrg.label).toBe('Test Org'); + expect(result.destination_stack.selectedStack.value).toBe('stack-123'); + expect(result.destination_stack.selectedStack.label).toBe('Test Stack'); + expect(result.destination_stack.selectedStack.master_locale).toBe('en-us'); + }); + + it('should correctly map stackArray', () => { + const projectData = createMockProject(); + const result = createObject(projectData); + + expect(result.destination_stack.stackArray.value).toBe('stack-123'); + expect(result.destination_stack.stackArray.created_at).toBe('2024-01-01'); + }); + + it('should handle missing/undefined project data gracefully', () => { + const result = createObject({}); + + expect(result.legacy_cms.selectedCms.cms_id).toBeUndefined(); + expect(result.legacy_cms.affix).toBeUndefined(); + expect(result.destination_stack.selectedOrg.value).toBeUndefined(); + expect(result.content_mapping).toBeUndefined(); + }); + + it('should pass through content_mapping and mapperKeys directly', () => { + const projectData = createMockProject({ + content_mapping: { ct1: 'mapped1' }, + mapperKeys: { key1: 'val1' } + }); + const result = createObject(projectData); + + expect(result.content_mapping).toEqual({ ct1: 'mapped1' }); + expect(result.mapperKeys).toEqual({ key1: 'val1' }); + }); + }); +}); diff --git a/ui/vitest.config.ts b/ui/vitest.config.ts new file mode 100644 index 000000000..c841b02fa --- /dev/null +++ b/ui/vitest.config.ts @@ -0,0 +1,40 @@ +import { defineConfig } from 'vitest/config'; +import react from '@vitejs/plugin-react-swc'; +import tsconfigPaths from 'vite-tsconfig-paths'; + +export default defineConfig({ + plugins: [react(), tsconfigPaths()], + test: { + globals: true, + environment: 'jsdom', + setupFiles: ['./tests/setup.ts'], + coverage: { + provider: 'v8', + reporter: ['text', 'lcov', 'html'], + include: ['src/**/*.ts', 'src/**/*.tsx'], + exclude: [ + '**/node_modules/**', + '**/tests/**', + 'src/index.tsx', + 'src/App.tsx', + 'src/vite-env.d.ts', + 'src/setupTests.js', + 'src/**/*.interface.ts', + 'src/**/*.d.ts', + 'src/types/**', + 'src/scss/**', + 'src/common/assets/**', + 'src/pages/**', + 'src/components/**', + 'src/context/app/app.context.tsx', + 'src/context/app/app.provider.tsx', + ], + thresholds: { + lines: 80, + functions: 80, + branches: 60, + statements: 80, + }, + }, + }, +}); diff --git a/upload-api/package-lock.json b/upload-api/package-lock.json index b92c93ee4..da1a7e6b4 100644 --- a/upload-api/package-lock.json +++ b/upload-api/package-lock.json @@ -24,7 +24,7 @@ "fs-readdir-recursive": "^1.1.0", "generate-schema": "^2.6.0", "helmet": "^7.1.0", - "jsdom": "^19.0.0", + "jsdom": "^23.0.0", "jszip": "^3.10.1", "lodash": "^4.17.21", "lodash.isempty": "^4.4.0", @@ -34,7 +34,7 @@ "migration-sitecore": "file:migration-sitecore", "migration-wordpress": "file:migration-wordpress", "mkdirp": "^1.0.4", - "multer": "^2.0.1", + "multer": "^2.1.0", "mysql2": "^3.16.2", "php-serialize": "^5.1.3", "prettier": "^3.3.3", @@ -54,13 +54,15 @@ "@types/wordpress__blocks": "^12.5.18", "@types/xml2js": "^0.4.14", "@typescript-eslint/eslint-plugin": "^8.0.0", + "@vitest/coverage-v8": "^4.0.18", "eslint": "^9.0.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", "nodemon": "^3.1.10", "rimraf": "^6.0.1", "ts-node": "^10.9.2", - "typescript": "^5.3.3" + "typescript": "^5.3.3", + "vitest": "^4.0.18" } }, "migration-aem": { @@ -80,25 +82,6 @@ "typescript": "^5.8.3" } }, - "migration-aem/node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "migration-aem/node_modules/@isaacs/brace-expansion": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, "migration-aem/node_modules/@isaacs/cliui": { "version": "8.0.2", "dev": true, @@ -142,11 +125,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "migration-aem/node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, "migration-aem/node_modules/brace-expansion": { "version": "1.1.12", "dev": true, @@ -156,51 +134,6 @@ "concat-map": "0.0.1" } }, - "migration-aem/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "migration-aem/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "migration-aem/node_modules/cross-spawn": { - "version": "7.0.6", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "migration-aem/node_modules/debug": { - "version": "4.4.3", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "migration-aem/node_modules/eastasianwidth": { "version": "0.2.0", "dev": true, @@ -226,22 +159,21 @@ "url": "https://github.com/sponsors/isaacs" } }, - "migration-aem/node_modules/fs-readdir-recursive": { - "version": "1.1.0", - "license": "MIT" - }, "migration-aem/node_modules/genson-js": { "version": "0.0.8", "license": "Apache-2.0" }, "migration-aem/node_modules/glob": { - "version": "11.0.3", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", - "minimatch": "^10.0.3", + "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" @@ -256,15 +188,40 @@ "url": "https://github.com/sponsors/isaacs" } }, + "migration-aem/node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "migration-aem/node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "migration-aem/node_modules/glob/node_modules/minimatch": { - "version": "10.0.3", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" + "brace-expansion": "^5.0.2" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -278,19 +235,6 @@ "node": ">=4" } }, - "migration-aem/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "migration-aem/node_modules/isexe": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, "migration-aem/node_modules/jackspeak": { "version": "4.1.1", "dev": true, @@ -314,7 +258,9 @@ } }, "migration-aem/node_modules/minimatch": { - "version": "3.1.2", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -324,11 +270,6 @@ "node": "*" } }, - "migration-aem/node_modules/ms": { - "version": "2.1.3", - "dev": true, - "license": "MIT" - }, "migration-aem/node_modules/nodemon": { "version": "3.1.10", "dev": true, @@ -356,14 +297,6 @@ "url": "https://opencollective.com/nodemon" } }, - "migration-aem/node_modules/path-key": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "migration-aem/node_modules/path-scurry": { "version": "2.0.0", "dev": true, @@ -397,36 +330,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "migration-aem/node_modules/semver": { - "version": "7.7.3", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "migration-aem/node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "migration-aem/node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "migration-aem/node_modules/signal-exit": { "version": "4.1.0", "dev": true, @@ -537,43 +440,6 @@ "node": ">=4" } }, - "migration-aem/node_modules/typescript": { - "version": "5.9.3", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "migration-aem/node_modules/uuid": { - "version": "9.0.1", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "migration-aem/node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "migration-aem/node_modules/wrap-ansi": { "version": "8.1.0", "dev": true, @@ -704,6 +570,36 @@ "react-dom": "^17.0.0 || ^18.0.0" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-2.0.2.tgz", + "integrity": "sha512-x1KXOatwofR6ZAYzXRBL5wrdV0vwNxlTCK9NCuLqAzQYARqGcvFwiJA6A1ERuh+dgeA4Dxm3JBYictIes+SqUQ==", + "license": "MIT", + "dependencies": { + "bidi-js": "^1.0.3", + "css-tree": "^2.3.1", + "is-potential-custom-element-name": "^1.0.1" + } + }, "node_modules/@aws-crypto/crc32": { "version": "5.2.0", "license": "Apache-2.0", @@ -1681,6 +1577,16 @@ } } }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@colors/colors": { "version": "1.6.0", "license": "MIT", @@ -1787,20 +1693,6 @@ "node": ">=8.0.0" } }, - "node_modules/@contentstack/management/node_modules/qs": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", - "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/@contentstack/marketplace-sdk": { "version": "1.4.0", "license": "MIT", @@ -1834,44 +1726,154 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.8", - "license": "MIT", - "dependencies": { - "@so-ric/colorspace": "^1.1.6", - "enabled": "2.0.x", - "kuler": "^2.0.0" + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" } }, - "node_modules/@date-fns/tz": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.4.1.tgz", - "integrity": "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==" - }, - "node_modules/@date-fns/utc": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@date-fns/utc/-/utc-2.1.1.tgz", - "integrity": "sha512-SlJDfG6RPeEX8wEVv6ZB3kak4MmbtyiI2qX/5zuKdordbrhB/iaJ58GVMZgJ6P1sJaM1gMgENFYYeg1JWrCFrA==" - }, - "node_modules/@emotion/babel-plugin": { - "version": "11.13.5", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", - "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", - "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.2", - "@emotion/memoize": "^0.9.0", - "@emotion/serialize": "^1.3.3", - "babel-plugin-macros": "^3.1.0", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "4.2.0" + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, - "node_modules/@emotion/cache": { + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.8", + "license": "MIT", + "dependencies": { + "@so-ric/colorspace": "^1.1.6", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@date-fns/tz": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.4.1.tgz", + "integrity": "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==" + }, + "node_modules/@date-fns/utc": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@date-fns/utc/-/utc-2.1.1.tgz", + "integrity": "sha512-SlJDfG6RPeEX8wEVv6ZB3kak4MmbtyiI2qX/5zuKdordbrhB/iaJ58GVMZgJ6P1sJaM1gMgENFYYeg1JWrCFrA==" + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache": { "version": "11.14.0", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", @@ -1988,15 +1990,457 @@ "react": ">=16.8.0" } }, - "node_modules/@emotion/utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", - "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==" + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@emotion/weak-memoize": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", - "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", @@ -2035,28 +2479,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@eslint/config-helpers": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", @@ -2104,32 +2526,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/@eslint/eslintrc/node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2139,24 +2535,6 @@ "node": ">= 4" } }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@eslint/js": { "version": "9.39.2", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", @@ -2284,15 +2662,6 @@ } } }, - "node_modules/@isaacs/cliui": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", - "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", - "dev": true, - "engines": { - "node": ">=18" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -2908,20 +3277,370 @@ "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.5.tgz", "integrity": "sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g==" }, - "node_modules/@react-spring/web": { - "version": "9.7.5", - "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.5.tgz", - "integrity": "sha512-lmvqGwpe+CSttsWNZVr+Dg62adtKhauGwLyGE/RRyZ8AAMLgb9x3NDMA5RMElXo+IMyTkPp7nxTB8ZQlmhb6JQ==", - "dependencies": { - "@react-spring/animated": "~9.7.5", - "@react-spring/core": "~9.7.5", - "@react-spring/shared": "~9.7.5", - "@react-spring/types": "~9.7.5" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } + "node_modules/@react-spring/web": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.5.tgz", + "integrity": "sha512-lmvqGwpe+CSttsWNZVr+Dg62adtKhauGwLyGE/RRyZ8AAMLgb9x3NDMA5RMElXo+IMyTkPp7nxTB8ZQlmhb6JQ==", + "dependencies": { + "@react-spring/animated": "~9.7.5", + "@react-spring/core": "~9.7.5", + "@react-spring/shared": "~9.7.5", + "@react-spring/types": "~9.7.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] }, "node_modules/@smithy/abort-controller": { "version": "4.2.8", @@ -3561,6 +4280,13 @@ "text-hex": "1.0.x" } }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, "node_modules/@tannin/compile": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@tannin/compile/-/compile-1.1.0.tgz", @@ -3593,14 +4319,6 @@ "resolved": "https://registry.npmjs.org/@tannin/sprintf/-/sprintf-1.3.3.tgz", "integrity": "sha512-RwARl+hFwhzy0tg9atWcchLFvoQiOh4rrP7uG2N5E4W80BPCUX0ElcUR9St43fxB9EfjsW2df9Qp+UsTbvQDjA==" }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "engines": { - "node": ">= 10" - } - }, "node_modules/@tsconfig/node10": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", @@ -3634,6 +4352,17 @@ "@types/node": "*" } }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "dev": true, @@ -3650,10 +4379,15 @@ "@types/node": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "license": "MIT" }, "node_modules/@types/express": { @@ -4092,6 +4826,148 @@ "react": ">= 16.8.0" } }, + "node_modules/@vitest/coverage-v8": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.18.tgz", + "integrity": "sha512-7i+N2i0+ME+2JFZhfuz7Tg/FqKtilHjGyGvoHYQ6iLV0zahbsJ9sljC9OcFcPDbhYKCet+sG8SsVqlyGvPflZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.0.18", + "ast-v8-to-istanbul": "^0.3.10", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.1", + "obug": "^2.1.1", + "std-env": "^3.10.0", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.0.18", + "vitest": "4.0.18" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", + "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz", + "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.18", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", + "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz", + "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.18", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", + "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.18", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz", + "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz", + "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.18", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/@wordpress/a11y": { "version": "4.39.0", "resolved": "https://registry.npmjs.org/@wordpress/a11y/-/a11y-4.39.0.tgz", @@ -7671,14 +8547,6 @@ "npm": ">=8.19.2" } }, - "node_modules/@wordpress/sync/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/@wordpress/theme": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@wordpress/theme/-/theme-0.6.0.tgz", @@ -7993,12 +8861,6 @@ "npm": ">=8.19.2" } }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "deprecated": "Use your platform's native atob() and btoa() methods instead" - }, "node_modules/accepts": { "version": "2.0.0", "license": "MIT", @@ -8020,26 +8882,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dependencies": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - } - }, - "node_modules/acorn-globals/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -8049,27 +8891,19 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "engines": { - "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" - }, + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", "engines": { - "node": ">= 6.0.0" + "node": ">= 14" } }, "node_modules/ajv": { - "version": "8.17.1", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -8150,6 +8984,19 @@ "node": ">= 8" } }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/append-field": { "version": "1.0.0", "license": "MIT" @@ -8235,6 +9082,35 @@ "util": "^0.12.5" } }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-v8-to-istanbul": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.11.tgz", + "integrity": "sha512-Qya9fkoofMjCBNVdWINMjB5KZvkYfaO9/anwkWnjxibpWUxo5iHl2sOdP7/uAqaRuUYuoo8rDwnbaaKVFxoUvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, "node_modules/async": { "version": "3.2.6", "license": "MIT" @@ -8307,6 +9183,7 @@ }, "node_modules/balanced-match": { "version": "1.0.2", + "dev": true, "license": "MIT" }, "node_modules/base64-js": { @@ -8327,6 +9204,15 @@ ], "license": "MIT" }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -8413,10 +9299,24 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "2.0.2", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/brace-expansion/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/braces": { @@ -8438,11 +9338,6 @@ "wcwidth": "^1.0.1" } }, - "node_modules/browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" - }, "node_modules/buffer": { "version": "6.0.3", "funding": [ @@ -8560,6 +9455,16 @@ "upper-case-first": "^2.0.2" } }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "license": "MIT", @@ -8916,7 +9821,8 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "node_modules/concat-stream": { "version": "2.0.0", @@ -9041,6 +9947,15 @@ "node": ">=10" } }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -9080,6 +9995,19 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, "node_modules/css-what": { "version": "6.2.2", "license": "BSD-2-Clause", @@ -9090,26 +10018,24 @@ "url": "https://github.com/sponsors/fb55" } }, - "node_modules/cssom": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", - "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==" - }, "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "license": "MIT", "dependencies": { - "cssom": "~0.3.6" + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + "node_modules/cssstyle/node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "license": "MIT" }, "node_modules/csstype": { "version": "3.2.3", @@ -9142,36 +10068,16 @@ "license": "MIT" }, "node_modules/data-urls": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", - "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", - "dependencies": { - "abab": "^2.0.6", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/data-urls/node_modules/whatwg-mimetype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", - "engines": { - "node": ">=12" - } - }, - "node_modules/data-urls/node_modules/whatwg-url": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", - "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "license": "MIT", "dependencies": { - "tr46": "^3.0.0", - "webidl-conversions": "^7.0.0" + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/data-view-buffer": { @@ -9372,9 +10278,10 @@ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" }, "node_modules/diff": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", - "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", + "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -9401,18 +10308,6 @@ ], "license": "BSD-2-Clause" }, - "node_modules/domexception": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", - "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", - "deprecated": "Use your platform's native DOMException instead", - "dependencies": { - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/domhandler": { "version": "5.0.3", "license": "BSD-2-Clause", @@ -9739,6 +10634,48 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, "node_modules/escalade": { "version": "3.2.0", "license": "MIT", @@ -9760,35 +10697,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/escodegen/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/eslint": { "version": "9.39.2", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", @@ -9914,30 +10822,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/eslint/node_modules/eslint-visitor-keys": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", @@ -9957,22 +10841,6 @@ "node": ">= 4" } }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "license": "MIT" - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", @@ -10002,18 +10870,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/esquery": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", @@ -10044,6 +10900,16 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "license": "BSD-2-Clause", @@ -10058,6 +10924,16 @@ "node": ">= 0.6" } }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/express": { "version": "5.2.1", "license": "MIT", @@ -10129,10 +11005,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "license": "MIT" - }, "node_modules/fast-levenshtein": { "version": "2.0.6", "license": "MIT" @@ -10151,8 +11023,22 @@ ], "license": "BSD-3-Clause" }, + "node_modules/fast-xml-builder": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.0.0.tgz", + "integrity": "sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/fast-xml-parser": { - "version": "5.3.4", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.4.2.tgz", + "integrity": "sha512-pw/6pIl4k0CSpElPEJhDppLzaixDEuWui2CUQQBH/ECDf7+y6YwA4Gf7Tyb0Rfe4DIMuZipYj4AEL0nACKglvQ==", "funding": [ { "type": "github", @@ -10161,12 +11047,30 @@ ], "license": "MIT", "dependencies": { - "strnum": "^2.1.0" + "fast-xml-builder": "^1.0.0", + "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/fecha": { "version": "4.2.3", "license": "MIT" @@ -10208,16 +11112,6 @@ "minimatch": "^5.0.1" } }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -10573,45 +11467,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/balanced-match": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.2.tgz", - "integrity": "sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg==", - "dev": true, - "dependencies": { - "jackspeak": "^4.2.3" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz", - "integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==", - "dev": true, - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.0.tgz", - "integrity": "sha512-ugkC31VaVg9cF0DFVoADH12k6061zNZkZON+aX8AWsR9GhPcErkcMBceb6znR8wLERM2AkkOxy2nWRLpT9Jq5w==", - "dev": true, - "dependencies": { - "brace-expansion": "^5.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -10820,38 +11675,23 @@ } }, "node_modules/html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "dependencies": { - "whatwg-encoding": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/html-encoding-sniffer/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "whatwg-encoding": "^3.1.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=18" } }, - "node_modules/html-encoding-sniffer/node_modules/whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=12" - } + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" }, "node_modules/html-react-parser": { "version": "5.2.11", @@ -10922,28 +11762,29 @@ } }, "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "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==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", "dependencies": { - "agent-base": "6", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/husky": { @@ -11963,19 +12804,56 @@ "url": "https://github.com/sponsors/dmonad" } }, - "node_modules/jackspeak": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", - "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@isaacs/cliui": "^9.0.0" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "20 || >=22" + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/jake": { @@ -12009,43 +12887,38 @@ } }, "node_modules/jsdom": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-19.0.0.tgz", - "integrity": "sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==", - "dependencies": { - "abab": "^2.0.5", - "acorn": "^8.5.0", - "acorn-globals": "^6.0.0", - "cssom": "^0.5.0", - "cssstyle": "^2.3.0", - "data-urls": "^3.0.1", - "decimal.js": "^10.3.1", - "domexception": "^4.0.0", - "escodegen": "^2.0.0", + "version": "23.2.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-23.2.0.tgz", + "integrity": "sha512-L88oL7D/8ufIES+Zjz7v0aes+oBMh2Xnh3ygWvL0OaICOomKEPKuPnIfBJekiXr+BHbbMjrWn/xqrDQuxFTeyA==", + "license": "MIT", + "dependencies": { + "@asamuzakjp/dom-selector": "^2.0.1", + "cssstyle": "^4.0.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", "form-data": "^4.0.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.6.0", + "saxes": "^6.0.0", "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^3.0.0", + "tough-cookie": "^4.1.3", + "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^2.0.0", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^10.0.0", - "ws": "^8.2.3", - "xml-name-validator": "^4.0.0" + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.16.0", + "xml-name-validator": "^5.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "peerDependencies": { - "canvas": "^2.5.0" + "canvas": "^2.11.2" }, "peerDependenciesMeta": { "canvas": { @@ -12053,42 +12926,6 @@ } } }, - "node_modules/jsdom/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jsdom/node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" - }, - "node_modules/jsdom/node_modules/whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/jsdom/node_modules/whatwg-mimetype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", - "engines": { - "node": ">=12" - } - }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -12320,6 +13157,44 @@ "url": "https://github.com/sponsors/wellwelwel" } }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", + "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -12333,6 +13208,12 @@ "node": ">= 0.4" } }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "license": "CC0-1.0" + }, "node_modules/media-typer": { "version": "1.1.0", "license": "MIT", @@ -12400,23 +13281,18 @@ } }, "node_modules/minimatch": { - "version": "9.0.5", - "license": "ISC", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "license": "MIT", + "node": "18 || 20 || >=22" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minipass": { @@ -12486,19 +13362,22 @@ "license": "MIT" }, "node_modules/multer": { - "version": "2.0.2", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz", + "integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==", "license": "MIT", "dependencies": { "append-field": "^1.0.0", "busboy": "^1.6.0", "concat-stream": "^2.0.0", - "mkdirp": "^0.5.6", - "object-assign": "^4.1.1", - "type-is": "^1.6.18", - "xtend": "^4.0.2" + "type-is": "^1.6.18" }, "engines": { "node": ">= 10.16.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/multer/node_modules/media-typer": { @@ -12525,16 +13404,6 @@ "node": ">= 0.6" } }, - "node_modules/multer/node_modules/mkdirp": { - "version": "0.5.6", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/multer/node_modules/type-is": { "version": "1.6.18", "license": "MIT", @@ -12657,16 +13526,6 @@ "url": "https://opencollective.com/nodemon" } }, - "node_modules/nodemon/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/nodemon/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -12676,18 +13535,6 @@ "node": ">=4" } }, - "node_modules/nodemon/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/nodemon/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -12724,11 +13571,6 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/nwsapi": { - "version": "2.2.23", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz", - "integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==" - }, "node_modules/object-assign": { "version": "4.1.1", "license": "MIT", @@ -12785,6 +13627,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, "node_modules/on-finished": { "version": "2.4.1", "license": "MIT", @@ -13120,6 +13973,13 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/php-serialize": { "version": "5.1.3", "license": "MIT", @@ -13132,12 +13992,12 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -13601,6 +14461,19 @@ "node": ">=8.10.0" } }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/recheck": { "version": "4.4.5", "license": "MIT", @@ -13770,6 +14643,51 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "fsevents": "~2.3.2" + } + }, "node_modules/router": { "version": "2.2.0", "license": "MIT", @@ -13784,6 +14702,12 @@ "node": ">= 18" } }, + "node_modules/rrweb-cssom": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", + "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", + "license": "MIT" + }, "node_modules/run-async": { "version": "2.4.1", "license": "MIT", @@ -13893,14 +14817,15 @@ "license": "ISC" }, "node_modules/saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "license": "ISC", "dependencies": { "xmlchars": "^2.2.0" }, "engines": { - "node": ">=10" + "node": ">=v12.22.7" } }, "node_modules/scheduler": { @@ -14296,6 +15221,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/simple-html-tokenizer": { "version": "0.5.11", "resolved": "https://registry.npmjs.org/simple-html-tokenizer/-/simple-html-tokenizer-0.5.11.tgz", @@ -14464,6 +15396,13 @@ "node": "*" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, "node_modules/statuses": { "version": "2.0.2", "license": "MIT", @@ -14471,6 +15410,13 @@ "node": ">= 0.8" } }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "license": "MIT", @@ -14724,6 +15670,23 @@ "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "license": "MIT", @@ -14738,29 +15701,14 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", + "node_modules/tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">=14.0.0" } }, "node_modules/tmp": { @@ -14813,14 +15761,15 @@ } }, "node_modules/tr46": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", - "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "license": "MIT", "dependencies": { - "punycode": "^2.1.1" + "punycode": "^2.3.1" }, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/traverse": { @@ -15149,13 +16098,6 @@ "tslib": "^2.0.3" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/url-parse": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", @@ -15274,24 +16216,169 @@ "node": ">= 0.8" } }, - "node_modules/w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz", + "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", + "dev": true, + "license": "MIT", "dependencies": { - "browser-process-hrtime": "^1.0.0" + "@vitest/expect": "4.0.18", + "@vitest/mocker": "4.0.18", + "@vitest/pretty-format": "4.0.18", + "@vitest/runner": "4.0.18", + "@vitest/snapshot": "4.0.18", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.18", + "@vitest/browser-preview": "4.0.18", + "@vitest/browser-webdriverio": "4.0.18", + "@vitest/ui": "4.0.18", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } } }, "node_modules/w3c-xmlserializer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz", - "integrity": "sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "license": "MIT", "dependencies": { - "xml-name-validator": "^4.0.0" + "xml-name-validator": "^5.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/wcwidth": { @@ -15305,6 +16392,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" } @@ -15337,15 +16425,16 @@ } }, "node_modules/whatwg-url": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-10.0.0.tgz", - "integrity": "sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", "dependencies": { - "tr46": "^3.0.0", + "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/which": { @@ -15446,6 +16535,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/widest-line": { "version": "3.1.0", "license": "MIT", @@ -15567,11 +16673,12 @@ } }, "node_modules/xml-name-validator": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", - "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "license": "Apache-2.0", "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/xml2js": { @@ -15595,14 +16702,8 @@ "node_modules/xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" - }, - "node_modules/xtend": { - "version": "4.0.2", - "license": "MIT", - "engines": { - "node": ">=0.4" - } + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "license": "MIT" }, "node_modules/y-protocols": { "version": "1.0.7", @@ -15631,11 +16732,21 @@ } }, "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, "engines": { - "node": ">= 6" + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, "node_modules/yargs": { diff --git a/upload-api/package.json b/upload-api/package.json index 3432c547f..620c4facf 100644 --- a/upload-api/package.json +++ b/upload-api/package.json @@ -5,7 +5,10 @@ "main": "index.js", "scripts": { "build": "for d in migration-*; do [ -f \"$d/package.json\" ] && npm --prefix $d run build; done && tsc", - "test": "echo \"Error: no test specified\" && exit 1", + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage", + "coverage:ui": "npx serve coverage -l 3939", "start": "npm run build &&tsc && node build/index.js", "startNew": "tsc && node build/index.js", "validation": "tsc && node build/main.js", @@ -31,13 +34,15 @@ "@types/wordpress__blocks": "^12.5.18", "@types/xml2js": "^0.4.14", "@typescript-eslint/eslint-plugin": "^8.0.0", + "@vitest/coverage-v8": "^4.0.18", "eslint": "^9.0.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", "nodemon": "^3.1.10", "rimraf": "^6.0.1", "ts-node": "^10.9.2", - "typescript": "^5.3.3" + "typescript": "^5.3.3", + "vitest": "^4.0.18" }, "dependencies": { "@aws-sdk/client-s3": "^3.985.0", @@ -55,7 +60,7 @@ "fs-readdir-recursive": "^1.1.0", "generate-schema": "^2.6.0", "helmet": "^7.1.0", - "jsdom": "^19.0.0", + "jsdom": "^23.0.0", "jszip": "^3.10.1", "lodash": "^4.17.21", "lodash.isempty": "^4.4.0", @@ -65,7 +70,7 @@ "migration-sitecore": "file:migration-sitecore", "migration-wordpress": "file:migration-wordpress", "mkdirp": "^1.0.4", - "multer": "^2.0.1", + "multer": "^2.1.0", "mysql2": "^3.16.2", "php-serialize": "^5.1.3", "prettier": "^3.3.3", @@ -73,6 +78,13 @@ "xml2js": "^0.6.2" }, "overrides": { - "tmp": ">=0.2.4" + "tmp": ">=0.2.4", + "minimatch": ">=10.2.3", + "ajv": ">=8.18.0", + "rollup": ">=4.59.0", + "@tootallnate/once": ">=3.0.1", + "fast-xml-parser": ">=5.3.8", + "diff": ">=5.2.2", + "qs": ">=6.14.2" } } diff --git a/upload-api/tests/__mocks__/migration-aem.ts b/upload-api/tests/__mocks__/migration-aem.ts new file mode 100644 index 000000000..97068b06d --- /dev/null +++ b/upload-api/tests/__mocks__/migration-aem.ts @@ -0,0 +1,9 @@ +import { vi } from 'vitest'; + +export const validator = vi.fn().mockResolvedValue([true]); +export const contentTypes = vi.fn().mockReturnValue({ + convertAndCreate: vi.fn().mockResolvedValue([]), +}); +export const locales = vi.fn().mockReturnValue({ + processAndSave: vi.fn().mockResolvedValue([]), +}); diff --git a/upload-api/tests/__mocks__/migration-contentful.ts b/upload-api/tests/__mocks__/migration-contentful.ts new file mode 100644 index 000000000..21764d913 --- /dev/null +++ b/upload-api/tests/__mocks__/migration-contentful.ts @@ -0,0 +1,5 @@ +import { vi } from 'vitest'; + +export const extractLocale = vi.fn().mockResolvedValue([]); +export const extractContentTypes = vi.fn().mockResolvedValue(undefined); +export const createInitialMapper = vi.fn().mockResolvedValue({ contentTypes: [] }); diff --git a/upload-api/tests/__mocks__/migration-drupal.ts b/upload-api/tests/__mocks__/migration-drupal.ts new file mode 100644 index 000000000..386318efd --- /dev/null +++ b/upload-api/tests/__mocks__/migration-drupal.ts @@ -0,0 +1,5 @@ +import { vi } from 'vitest'; + +export const extractLocale = vi.fn().mockResolvedValue(new Set()); +export const extractTaxonomy = vi.fn().mockResolvedValue(undefined); +export const createInitialMapper = vi.fn().mockResolvedValue({ contentTypes: [] }); diff --git a/upload-api/tests/__mocks__/migration-sitecore.ts b/upload-api/tests/__mocks__/migration-sitecore.ts new file mode 100644 index 000000000..cbd04d9a5 --- /dev/null +++ b/upload-api/tests/__mocks__/migration-sitecore.ts @@ -0,0 +1,7 @@ +import { vi } from 'vitest'; + +export const ExtractFiles = vi.fn().mockResolvedValue(undefined); +export const extractLocales = vi.fn().mockResolvedValue([]); +export const ExtractConfiguration = vi.fn().mockResolvedValue(undefined); +export const contentTypes = vi.fn().mockResolvedValue(undefined); +export const reference = vi.fn().mockResolvedValue({ contentTypeUids: [], path: '' }); diff --git a/upload-api/tests/__mocks__/migration-wordpress.ts b/upload-api/tests/__mocks__/migration-wordpress.ts new file mode 100644 index 000000000..7c925b6cc --- /dev/null +++ b/upload-api/tests/__mocks__/migration-wordpress.ts @@ -0,0 +1,4 @@ +import { vi } from 'vitest'; + +export const extractLocale = vi.fn().mockResolvedValue([]); +export const extractContentTypes = vi.fn().mockResolvedValue(null); diff --git a/upload-api/tests/fixtures/config.fixture.ts b/upload-api/tests/fixtures/config.fixture.ts new file mode 100644 index 000000000..42078f068 --- /dev/null +++ b/upload-api/tests/fixtures/config.fixture.ts @@ -0,0 +1,59 @@ +import { vi } from 'vitest'; + +export const createMockConfig = (overrides: Record = {}) => ({ + plan: { dropdown: { optionLimit: 100 } }, + cmsType: 'wordpress', + isLocalPath: true, + awsData: { + awsRegion: 'us-east-2', + awsAccessKeyId: 'test-key', + awsSecretAccessKey: 'test-secret', + awsSessionToken: 'test-token', + bucketName: 'test-bucket', + bucketKey: 'test-key.zip', + }, + mysql: { + host: 'localhost', + user: 'root', + password: 'password', + database: 'drupal_db', + port: '3306', + }, + assetsConfig: { + base_url: 'http://localhost:8080', + public_path: '/sites/default/files', + }, + localPath: '/tmp/test-uploads', + ...overrides, +}); + +export const createMockRequest = (overrides: Record = {}) => ({ + headers: { + projectid: 'project-123', + app_token: 'mock-token', + affix: 'csm', + ...overrides.headers, + }, + file: overrides.file || undefined, + body: overrides.body || {}, + ...overrides, +}); + +export const createMockResponse = () => { + const res: any = { + headersSent: false, + status: vi.fn().mockReturnThis(), + json: vi.fn().mockReturnThis(), + send: vi.fn().mockReturnThis(), + }; + return res; +}; + +export const createMockMySQLData = (overrides: Record = {}) => ({ + host: 'localhost', + user: 'root', + password: 'password', + database: 'drupal_db', + port: 3306, + ...overrides, +}); diff --git a/upload-api/tests/setup.ts b/upload-api/tests/setup.ts new file mode 100644 index 000000000..d730764e4 --- /dev/null +++ b/upload-api/tests/setup.ts @@ -0,0 +1,18 @@ +import { vi, beforeAll, afterAll, afterEach } from 'vitest'; + +beforeAll(() => { + vi.stubEnv('PORT', '5002'); + vi.stubEnv('CMS_TYPE', 'wordpress'); + vi.stubEnv('CONTAINER_PATH', '/tmp/test-uploads'); + vi.stubEnv('NODE_BACKEND_API', 'http://localhost:5001'); + vi.stubEnv('DRUPAL_ASSETS_BASE_URL', 'http://localhost:8080'); + vi.stubEnv('DRUPAL_ASSETS_PUBLIC_PATH', '/sites/default/files'); +}); + +afterEach(() => { + vi.restoreAllMocks(); +}); + +afterAll(() => { + vi.unstubAllEnvs(); +}); diff --git a/upload-api/tests/unit/config/index.config.test.ts b/upload-api/tests/unit/config/index.config.test.ts new file mode 100644 index 000000000..73f05093f --- /dev/null +++ b/upload-api/tests/unit/config/index.config.test.ts @@ -0,0 +1,72 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +describe('config/index', () => { + beforeEach(() => { + vi.resetModules(); + }); + + it('should export default configuration object', async () => { + const config = (await import('../../../src/config/index')).default; + + expect(config).toHaveProperty('plan'); + expect(config).toHaveProperty('cmsType'); + expect(config).toHaveProperty('isLocalPath'); + expect(config).toHaveProperty('awsData'); + expect(config).toHaveProperty('mysql'); + expect(config).toHaveProperty('assetsConfig'); + expect(config).toHaveProperty('localPath'); + }); + + it('should have plan with dropdown optionLimit', async () => { + const config = (await import('../../../src/config/index')).default; + expect(config.plan.dropdown.optionLimit).toBe(100); + }); + + it('should have isLocalPath as true', async () => { + const config = (await import('../../../src/config/index')).default; + expect(config.isLocalPath).toBe(true); + }); + + it('should use CMS_TYPE env var when set', async () => { + vi.stubEnv('CMS_TYPE', 'sitecore'); + const config = (await import('../../../src/config/index')).default; + expect(config.cmsType).toBe('sitecore'); + }); + + it('should use CONTAINER_PATH env var when set', async () => { + vi.stubEnv('CONTAINER_PATH', '/custom/path'); + const config = (await import('../../../src/config/index')).default; + expect(config.localPath).toBe('/custom/path'); + }); + + it('should use DRUPAL_ASSETS_BASE_URL env var when set', async () => { + vi.stubEnv('DRUPAL_ASSETS_BASE_URL', 'https://example.com'); + const config = (await import('../../../src/config/index')).default; + expect(config.assetsConfig.base_url).toBe('https://example.com'); + }); + + it('should use DRUPAL_ASSETS_PUBLIC_PATH env var when set', async () => { + vi.stubEnv('DRUPAL_ASSETS_PUBLIC_PATH', '/custom/files'); + const config = (await import('../../../src/config/index')).default; + expect(config.assetsConfig.public_path).toBe('/custom/files'); + }); + + it('should have default AWS data', async () => { + const config = (await import('../../../src/config/index')).default; + expect(config.awsData).toEqual({ + awsRegion: 'us-east-2', + awsAccessKeyId: '', + awsSecretAccessKey: '', + awsSessionToken: '', + bucketName: '', + bucketKey: '', + }); + }); + + it('should have default MySQL configuration', async () => { + const config = (await import('../../../src/config/index')).default; + expect(config.mysql.host).toBe('host_name'); + expect(config.mysql.user).toBe('user_name'); + expect(config.mysql.database).toBe('database_name'); + }); +}); diff --git a/upload-api/tests/unit/constants/index.test.ts b/upload-api/tests/unit/constants/index.test.ts new file mode 100644 index 000000000..4475cf4c7 --- /dev/null +++ b/upload-api/tests/unit/constants/index.test.ts @@ -0,0 +1,91 @@ +import { describe, it, expect } from 'vitest'; +import { HTTP_CODES, HTTP_TEXTS, HTTP_RESPONSE_HEADERS, MIGRATION_DATA_CONFIG, MACOSX_FOLDER } from '../../../src/constants/index'; + +describe('constants', () => { + describe('HTTP_CODES', () => { + it('should have standard HTTP status codes', () => { + expect(HTTP_CODES.OK).toBe(200); + expect(HTTP_CODES.BAD_REQUEST).toBe(400); + expect(HTTP_CODES.UNAUTHORIZED).toBe(401); + expect(HTTP_CODES.FORBIDDEN).toBe(403); + expect(HTTP_CODES.NOT_FOUND).toBe(404); + expect(HTTP_CODES.SERVER_ERROR).toBe(500); + }); + + it('should have additional status codes', () => { + expect(HTTP_CODES.TOO_MANY_REQS).toBe(429); + expect(HTTP_CODES.SOMETHING_WRONG).toBe(501); + expect(HTTP_CODES.MOVED_PERMANENTLY).toBe(301); + expect(HTTP_CODES.SUPPORT_DOC).toBe(294); + expect(HTTP_CODES.UNPROCESSABLE_CONTENT).toBe(422); + }); + }); + + describe('HTTP_TEXTS', () => { + it('should have error message strings', () => { + expect(HTTP_TEXTS.UNAUTHORIZED).toContain('unauthorized'); + expect(HTTP_TEXTS.INTERNAL_ERROR).toContain('Internal server error'); + expect(HTTP_TEXTS.ROUTE_ERROR).toContain('not available'); + expect(HTTP_TEXTS.SOMETHING_WENT_WRONG).toContain('Something went wrong'); + }); + + it('should have validation messages', () => { + expect(HTTP_TEXTS.VALIDATION_ERROR).toContain('validation failed'); + expect(HTTP_TEXTS.VALIDATION_SUCCESSFULL).toContain('validated successfully'); + }); + + it('should have file operation messages', () => { + expect(HTTP_TEXTS.ZIP_FILE_SAVE).toBeDefined(); + expect(HTTP_TEXTS.XML_FILE_SAVE).toBeDefined(); + expect(HTTP_TEXTS.S3_ERROR).toBeDefined(); + }); + + it('should have mapper and locale messages', () => { + expect(HTTP_TEXTS.MAPPER_SAVED).toContain('completed'); + expect(HTTP_TEXTS.LOCALE_SAVED).toContain('locales'); + expect(HTTP_TEXTS.LOCALE_FAILED).toContain('Unable'); + }); + }); + + describe('HTTP_RESPONSE_HEADERS', () => { + it('should have CORS and content-type headers', () => { + expect(HTTP_RESPONSE_HEADERS['Access-Control-Allow-Origin']).toBe('*'); + expect(HTTP_RESPONSE_HEADERS['Content-Type']).toBe('application/json'); + expect(HTTP_RESPONSE_HEADERS.Connection).toBe('close'); + }); + }); + + describe('MIGRATION_DATA_CONFIG', () => { + it('should have data folder configurations', () => { + expect(MIGRATION_DATA_CONFIG.DATA).toBe('cmsMigrationData'); + expect(MIGRATION_DATA_CONFIG.BACKUP_DATA).toBe('migration-data'); + }); + + it('should have locale configurations', () => { + expect(MIGRATION_DATA_CONFIG.LOCALE_DIR_NAME).toBe('locale'); + expect(MIGRATION_DATA_CONFIG.LOCALE_FILE_NAME).toBe('locales.json'); + expect(MIGRATION_DATA_CONFIG.LOCALE_MASTER_LOCALE).toBe('master-locale.json'); + }); + + it('should have content type configurations', () => { + expect(MIGRATION_DATA_CONFIG.CONTENT_TYPES_DIR_NAME).toBe('content_types'); + expect(MIGRATION_DATA_CONFIG.CONTENT_TYPES_FILE_NAME).toBe('contenttype.json'); + }); + + it('should have asset configurations', () => { + expect(MIGRATION_DATA_CONFIG.ASSETS_DIR_NAME).toBe('assets'); + expect(MIGRATION_DATA_CONFIG.ASSETS_FILE_NAME).toBe('assets.json'); + }); + + it('should have entries and global field configurations', () => { + expect(MIGRATION_DATA_CONFIG.ENTRIES_DIR_NAME).toBe('entries'); + expect(MIGRATION_DATA_CONFIG.GLOBAL_FIELDS_DIR_NAME).toBe('global_fields'); + }); + }); + + describe('MACOSX_FOLDER', () => { + it('should be __MACOSX', () => { + expect(MACOSX_FOLDER).toBe('__MACOSX'); + }); + }); +}); diff --git a/upload-api/tests/unit/controllers/aem.controller.test.ts b/upload-api/tests/unit/controllers/aem.controller.test.ts new file mode 100644 index 000000000..578797d1b --- /dev/null +++ b/upload-api/tests/unit/controllers/aem.controller.test.ts @@ -0,0 +1,55 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockAxiosRequest, mockContentTypes, mockLocales } = vi.hoisted(() => ({ + mockAxiosRequest: vi.fn(), + mockContentTypes: vi.fn(), + mockLocales: vi.fn(), +})); + +vi.mock('migration-aem', () => ({ + contentTypes: mockContentTypes, + locales: mockLocales, + validator: vi.fn(), +})); + +vi.mock('axios', () => ({ default: { request: mockAxiosRequest } })); +vi.mock('../../../src/utils/logger', () => ({ default: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn() } })); + +import { createAemMapper } from '../../../src/controllers/aem/index'; + +describe('createAemMapper', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should create AEM mapper and send to API', async () => { + const mockConvertAndCreate = vi.fn().mockResolvedValue([{ uid: 'page' }]); + const mockProcessAndSave = vi.fn().mockResolvedValue(['en-US']); + mockContentTypes.mockReturnValue({ convertAndCreate: mockConvertAndCreate }); + mockLocales.mockReturnValue({ processAndSave: mockProcessAndSave }); + mockAxiosRequest + .mockResolvedValueOnce({ status: 200 }) + .mockResolvedValueOnce({ data: { data: { content_mapper: [1] } } }); + + await createAemMapper('/path/to/aem', 'proj-1', 'token', 'csm'); + + expect(mockContentTypes).toHaveBeenCalled(); + expect(mockLocales).toHaveBeenCalled(); + expect(mockAxiosRequest).toHaveBeenCalledTimes(2); + }); + + it('should handle error gracefully', async () => { + mockContentTypes.mockImplementation(() => { throw new Error('AEM error'); }); + await expect(createAemMapper('/path', 'proj-1', 'token', 'csm')).resolves.toBeUndefined(); + }); + + it('should handle API send failure', async () => { + const mockConvertAndCreate = vi.fn().mockResolvedValue([]); + const mockProcessAndSave = vi.fn().mockResolvedValue([]); + mockContentTypes.mockReturnValue({ convertAndCreate: mockConvertAndCreate }); + mockLocales.mockReturnValue({ processAndSave: mockProcessAndSave }); + mockAxiosRequest.mockRejectedValue(new Error('Network error')); + + await expect(createAemMapper('/path', 'proj-1', 'token')).resolves.toBeUndefined(); + }, 30000); +}); diff --git a/upload-api/tests/unit/controllers/awsBucket.controller.test.ts b/upload-api/tests/unit/controllers/awsBucket.controller.test.ts new file mode 100644 index 000000000..6ee1d26e0 --- /dev/null +++ b/upload-api/tests/unit/controllers/awsBucket.controller.test.ts @@ -0,0 +1,73 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { Readable } from 'stream'; + +const { mockSend, MockGetObjectCommand } = vi.hoisted(() => ({ + mockSend: vi.fn(), + MockGetObjectCommand: vi.fn().mockImplementation(function (this: any, params: any) { + Object.assign(this, params); + }), +})); + +vi.mock('../../../src/services/aws/client', () => ({ + client: { send: mockSend }, +})); + +vi.mock('../../../src/config', () => ({ + default: { + awsData: { + awsRegion: 'us-east-2', + awsAccessKeyId: '', + awsSecretAccessKey: '', + awsSessionToken: '', + }, + }, +})); + +vi.mock('@aws-sdk/client-s3', () => ({ + GetObjectCommand: MockGetObjectCommand, + S3Client: vi.fn(), +})); + +import getBuketObject from '../../../src/controllers/awsBucket/index'; + +describe('getBuketObject', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return readable stream from S3', async () => { + const mockStream = new Readable({ read() {} }); + mockSend.mockResolvedValue({ Body: mockStream }); + + const result = await getBuketObject({ Key: 'test-key', Bucket: 'test-bucket' }); + expect(result).toBe(mockStream); + expect(mockSend).toHaveBeenCalled(); + }); + + it('should throw error when S3 body is empty', async () => { + mockSend.mockResolvedValue({ Body: null }); + + await expect( + getBuketObject({ Key: 'test-key', Bucket: 'test-bucket' }) + ).rejects.toThrow('Empty response body from S3'); + }); + + it('should throw error when S3 send fails', async () => { + mockSend.mockRejectedValue(new Error('S3 access denied')); + + await expect( + getBuketObject({ Key: 'test-key', Bucket: 'test-bucket' }) + ).rejects.toThrow('S3 access denied'); + }); + + it('should pass correct params to GetObjectCommand', async () => { + const mockStream = new Readable({ read() {} }); + mockSend.mockResolvedValue({ Body: mockStream }); + + await getBuketObject({ Key: 'my/path/file.zip', Bucket: 'my-bucket' }); + expect(MockGetObjectCommand).toHaveBeenCalledWith({ + Key: 'my/path/file.zip', + Bucket: 'my-bucket', + }); + }); +}); diff --git a/upload-api/tests/unit/controllers/sitecore.controller.test.ts b/upload-api/tests/unit/controllers/sitecore.controller.test.ts new file mode 100644 index 000000000..b8f57817c --- /dev/null +++ b/upload-api/tests/unit/controllers/sitecore.controller.test.ts @@ -0,0 +1,48 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockAxiosRequest, mockDeleteFolderSync } = vi.hoisted(() => ({ + mockAxiosRequest: vi.fn(), + mockDeleteFolderSync: vi.fn(), +})); + +vi.mock('axios', () => ({ default: { request: mockAxiosRequest } })); +vi.mock('../../../src/helper', () => ({ deleteFolderSync: mockDeleteFolderSync, fileOperationLimiter: vi.fn() })); +vi.mock('../../../src/utils/logger', () => ({ default: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn() } })); + +import createSitecoreMapper from '../../../src/controllers/sitecore/index'; + +describe('createSitecoreMapper', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should be a function', () => { + expect(typeof createSitecoreMapper).toBe('function'); + }); + + it('should handle error gracefully when extraction fails', async () => { + mockAxiosRequest.mockResolvedValue({ status: 200 }); + await expect( + createSitecoreMapper('/nonexistent', 'proj-1', 'token', 'csm', {}) + ).resolves.toBeUndefined(); + }); + + it('should not throw on empty path', async () => { + mockAxiosRequest.mockResolvedValue({ status: 200 }); + await expect( + createSitecoreMapper('', 'proj-1', 'token', 'csm', {}) + ).resolves.toBeUndefined(); + }); + + it('should handle API request failure', async () => { + mockAxiosRequest.mockRejectedValue(new Error('Network error')); + await expect( + createSitecoreMapper('/fake', 'proj-1', 'token', 'csm', {}) + ).resolves.toBeUndefined(); + }); + + it('should accept all required parameters', async () => { + mockAxiosRequest.mockResolvedValue({ status: 200 }); + await createSitecoreMapper('/path', 'project-id', 'app-token', 'csm', { option: true }); + }); +}); diff --git a/upload-api/tests/unit/controllers/wordpress.controller.test.ts b/upload-api/tests/unit/controllers/wordpress.controller.test.ts new file mode 100644 index 000000000..86abbc044 --- /dev/null +++ b/upload-api/tests/unit/controllers/wordpress.controller.test.ts @@ -0,0 +1,73 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockAxiosRequest, mockDeleteFolderSync, mockExtractLocale, mockExtractContentTypes } = vi.hoisted(() => ({ + mockAxiosRequest: vi.fn(), + mockDeleteFolderSync: vi.fn(), + mockExtractLocale: vi.fn().mockResolvedValue([]), + mockExtractContentTypes: vi.fn().mockResolvedValue(null), +})); + +vi.mock('migration-wordpress', () => ({ + extractLocale: mockExtractLocale, + extractContentTypes: mockExtractContentTypes, +})); + +vi.mock('axios', () => ({ default: { request: mockAxiosRequest } })); +vi.mock('../../../src/helper', () => ({ deleteFolderSync: mockDeleteFolderSync, fileOperationLimiter: vi.fn() })); +vi.mock('../../../src/utils/logger', () => ({ default: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn() } })); + +import createWordpressMapper from '../../../src/controllers/wordpress/index'; + +describe('createWordpressMapper', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should extract content types and send mapper to API', async () => { + mockExtractLocale.mockResolvedValue(['en']); + mockExtractContentTypes.mockResolvedValue([{ uid: 'post', title: 'Post' }]); + mockAxiosRequest + .mockResolvedValueOnce({ data: { data: { content_mapper: [1] } } }) + .mockResolvedValueOnce({ status: 200 }); + + await createWordpressMapper('/path', 'proj-1', 'token', 'csm', {}); + + expect(mockExtractLocale).toHaveBeenCalledWith('/path'); + expect(mockAxiosRequest).toHaveBeenCalledTimes(2); + expect(mockDeleteFolderSync).toHaveBeenCalled(); + }); + + it('should not send mapper when contentTypeData is falsy', async () => { + mockExtractLocale.mockResolvedValue([]); + mockExtractContentTypes.mockResolvedValue(null); + + await createWordpressMapper('/path', 'proj-1', 'token', 'csm', {}); + expect(mockAxiosRequest).not.toHaveBeenCalled(); + }); + + it('should handle error gracefully', async () => { + mockExtractLocale.mockRejectedValue(new Error('Extract failed')); + await expect(createWordpressMapper('/path', 'proj-1', 'token', 'csm', {})).resolves.toBeUndefined(); + }); + + it('should handle API error gracefully', async () => { + mockExtractLocale.mockResolvedValue(['en']); + mockExtractContentTypes.mockResolvedValue([{ uid: 'page' }]); + mockAxiosRequest.mockRejectedValue({ response: { data: 'API error' } }); + await expect(createWordpressMapper('/path', 'proj-1', 'token', 'csm', {})).resolves.toBeUndefined(); + }); + + it('should set type=content_type on each content type', async () => { + mockExtractLocale.mockResolvedValue(['en']); + mockExtractContentTypes.mockResolvedValue([{ uid: 'post' }, { uid: 'page' }]); + mockAxiosRequest + .mockResolvedValueOnce({ data: { data: { content_mapper: [1] } } }) + .mockResolvedValueOnce({ status: 200 }); + + await createWordpressMapper('/path', 'proj-1', 'token', 'csm', {}); + + const payload = JSON.parse(mockAxiosRequest.mock.calls[0][0].data); + expect(payload.contentTypes[0].type).toBe('content_type'); + expect(payload.contentTypes[1].type).toBe('content_type'); + }); +}); diff --git a/upload-api/tests/unit/helper/index.test.ts b/upload-api/tests/unit/helper/index.test.ts new file mode 100644 index 000000000..66232c178 --- /dev/null +++ b/upload-api/tests/unit/helper/index.test.ts @@ -0,0 +1,249 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { + mockMkdir, mockWriteFile, mockExistsSync, mockReaddirSync, + mockLstatSync, mockUnlinkSync, mockRmdirSync, mockParseStringPromise, +} = vi.hoisted(() => ({ + mockMkdir: vi.fn().mockResolvedValue(undefined), + mockWriteFile: vi.fn().mockResolvedValue(undefined), + mockExistsSync: vi.fn(), + mockReaddirSync: vi.fn(), + mockLstatSync: vi.fn(), + mockUnlinkSync: vi.fn(), + mockRmdirSync: vi.fn(), + mockParseStringPromise: vi.fn().mockResolvedValue({ rss: { channel: { item: [] } } }), +})); + +vi.mock('../../../src/utils/logger', () => ({ + default: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn() }, +})); + +vi.mock('fs', () => ({ + default: { + existsSync: (...a: any[]) => mockExistsSync(...a), + readdirSync: (...a: any[]) => mockReaddirSync(...a), + lstatSync: (...a: any[]) => mockLstatSync(...a), + unlinkSync: (...a: any[]) => mockUnlinkSync(...a), + rmdirSync: (...a: any[]) => mockRmdirSync(...a), + promises: { + mkdir: (...a: any[]) => mockMkdir(...a), + writeFile: (...a: any[]) => mockWriteFile(...a), + }, + }, + existsSync: (...a: any[]) => mockExistsSync(...a), + readdirSync: (...a: any[]) => mockReaddirSync(...a), + lstatSync: (...a: any[]) => mockLstatSync(...a), + unlinkSync: (...a: any[]) => mockUnlinkSync(...a), + rmdirSync: (...a: any[]) => mockRmdirSync(...a), + promises: { + mkdir: (...a: any[]) => mockMkdir(...a), + writeFile: (...a: any[]) => mockWriteFile(...a), + }, +})); + +vi.mock('xml2js', () => ({ + default: { + Parser: class MockParser { parseStringPromise = mockParseStringPromise; }, + }, +})); + +vi.mock('jszip', () => { + const Cls = vi.fn().mockImplementation(function (this: any) { + this.files = {}; + this.loadAsync = vi.fn().mockResolvedValue({ files: {} }); + }); + return { default: Cls, __esModule: true }; +}); + +import { getFileName, saveJson, parseXmlToJson, deleteFolderSync, saveZip } from '../../../src/helper/index'; + +describe('helper/index', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockParseStringPromise.mockResolvedValue({ rss: { channel: { item: [] } } }); + }); + + describe('getFileName', () => { + it('should extract filename and extension from S3 key', () => { + const result = (getFileName as any)({ Key: 'folder/subfolder/test.zip' }); + expect(result.fileName).toBe('test.zip'); + expect(result.fileExt).toBe('zip'); + }); + + it('should handle keys without slashes', () => { + const result = (getFileName as any)({ Key: 'myfile.xml' }); + expect(result.fileName).toBe('myfile.xml'); + expect(result.fileExt).toBe('xml'); + }); + + it('should handle keys with multiple dots', () => { + const result = (getFileName as any)({ Key: 'path/file.v2.tar.gz' }); + expect(result.fileName).toBe('file.v2.tar.gz'); + expect(result.fileExt).toBe('gz'); + }); + + it('should handle empty Key', () => { + const result = (getFileName as any)({ Key: '' }); + expect(result.fileName).toBe(''); + }); + }); + + describe('saveJson', () => { + it('should save JSON content to file', async () => { + const result = await saveJson('{"key":"value"}', 'test.json'); + expect(result).toBe(true); + expect(mockMkdir).toHaveBeenCalled(); + expect(mockWriteFile).toHaveBeenCalled(); + }); + + it('should stringify object content', async () => { + const result = await saveJson({ key: 'value' } as any, 'test.json'); + expect(result).toBe(true); + expect(mockWriteFile).toHaveBeenCalledWith( + expect.any(String), JSON.stringify({ key: 'value' }, null, 4), 'utf8' + ); + }); + + it('should handle falsy content with empty JSON fallback', async () => { + const result = await saveJson('' as any, 'test.json'); + expect(result).toBe(true); + expect(mockWriteFile).toHaveBeenCalledWith(expect.any(String), '{}', 'utf8'); + }); + + it('should return false on write error', async () => { + mockMkdir.mockRejectedValueOnce(new Error('write fail')); + const result = await saveJson('data', 'test.json'); + expect(result).toBe(false); + }); + }); + + describe('parseXmlToJson', () => { + it('should parse valid XML to JSON', async () => { + const result = await parseXmlToJson(''); + expect(result).toEqual({ rss: { channel: { item: [] } } }); + }); + + it('should strip XML comments before parsing', async () => { + const result = await parseXmlToJson(''); + expect(result).toBeDefined(); + }); + + it('should strip DOCTYPE before parsing', async () => { + const result = await parseXmlToJson(''); + expect(result).toBeDefined(); + }); + + it('should return false when parsing fails', async () => { + mockParseStringPromise.mockRejectedValueOnce(new Error('parse error')); + const result = await parseXmlToJson('bad'); + expect(result).toBe(false); + }); + }); + + describe('deleteFolderSync', () => { + it('should do nothing if folder does not exist', () => { + mockExistsSync.mockReturnValue(false); + deleteFolderSync('/fake/path'); + expect(mockReaddirSync).not.toHaveBeenCalled(); + }); + + it('should delete files and folder recursively', () => { + mockExistsSync.mockReturnValue(true); + mockReaddirSync.mockReturnValueOnce(['file1.txt', 'subdir']).mockReturnValueOnce([]); + mockLstatSync + .mockReturnValueOnce({ isDirectory: () => false }) + .mockReturnValueOnce({ isDirectory: () => true }); + + deleteFolderSync('/test/folder'); + expect(mockUnlinkSync).toHaveBeenCalled(); + expect(mockRmdirSync).toHaveBeenCalled(); + }); + }); + + describe('saveZip', () => { + it('should extract regular files from zip', async () => { + const zip = { + files: { + 'folder/file.txt': { dir: false, async: vi.fn().mockResolvedValue(Buffer.from('content')) }, + }, + }; + + const result = await saveZip(zip, 'test-project'); + expect(result.isSaved).toBe(true); + expect(mockMkdir).toHaveBeenCalled(); + expect(mockWriteFile).toHaveBeenCalled(); + }); + + it('should skip directory entries', async () => { + const zip = { files: { 'folder/': { dir: true } } }; + const result = await saveZip(zip, 'test-project'); + expect(result.isSaved).toBe(true); + expect(mockWriteFile).not.toHaveBeenCalled(); + }); + + it('should skip __MACOSX files', async () => { + const zip = { + files: { + '__MACOSX/file.txt': { dir: false, async: vi.fn().mockResolvedValue(Buffer.from('x')) }, + }, + }; + const result = await saveZip(zip, 'test-project'); + expect(result.isSaved).toBe(true); + expect(mockWriteFile).not.toHaveBeenCalled(); + }); + + it('should set filePath for non-sitecore folder files', async () => { + const zip = { + files: { + 'customfolder/page.json': { dir: false, async: vi.fn().mockResolvedValue(Buffer.from('{}')) }, + }, + }; + const result = await saveZip(zip, 'test-project'); + expect(result.isSaved).toBe(true); + expect(result.filePath).toBe('customfolder'); + }); + + it('should not set filePath for sitecore folder files', async () => { + const zip = { + files: { + 'items/master/test.json': { dir: false, async: vi.fn().mockResolvedValue(Buffer.from('{}')) }, + }, + }; + const result = await saveZip(zip, 'test-project'); + expect(result.isSaved).toBe(true); + expect(result.filePath).toBeUndefined(); + }); + + it('should skip filePath for files already under the main folder', async () => { + const zip = { + files: { + 'test-project/file.txt': { dir: false, async: vi.fn().mockResolvedValue(Buffer.from('x')) }, + }, + }; + const result = await saveZip(zip, 'test-project'); + expect(result.isSaved).toBe(true); + }); + + it('should handle nested zip files (non-sitecore)', async () => { + const zip = { + files: { + 'nested.zip': { dir: false, async: vi.fn().mockResolvedValue(Buffer.from('zipdata')) }, + }, + }; + const result = await saveZip(zip, 'test-project'); + expect(result.isSaved).toBe(true); + }); + + it('should return isSaved false on error', async () => { + const result = await saveZip(null as any, 'test'); + expect(result.isSaved).toBe(false); + expect(result.filePath).toBeUndefined(); + }); + + it('should handle empty zip files object', async () => { + const zip = { files: {} }; + const result = await saveZip(zip, 'test-project'); + expect(result.isSaved).toBe(true); + }); + }); +}); diff --git a/upload-api/tests/unit/routes/index.routes.test.ts b/upload-api/tests/unit/routes/index.routes.test.ts new file mode 100644 index 000000000..0d82a1072 --- /dev/null +++ b/upload-api/tests/unit/routes/index.routes.test.ts @@ -0,0 +1,530 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { EventEmitter } from 'events'; + +const { + mockStatSync, + mockCreateReadStream, + mockClientSend, + mockHandleFileProcessing, + mockCreateMapper, + mockConfig, +} = vi.hoisted(() => ({ + mockStatSync: vi.fn(), + mockCreateReadStream: vi.fn(), + mockClientSend: vi.fn(), + mockHandleFileProcessing: vi.fn(), + mockCreateMapper: vi.fn(), + mockConfig: { + cmsType: 'wordpress', + isLocalPath: true, + localPath: 'sql', + awsData: { + awsRegion: 'us-east-2', + bucketName: 'test-bucket', + bucketKey: 'project/test.zip', + }, + mysql: { host: 'localhost', user: 'root', password: 'pw', database: 'drupal', port: '3306' }, + assetsConfig: { base_url: 'http://test.com', public_path: '/files' }, + plan: { dropdown: { optionLimit: 100 } }, + } as any, +})); + +vi.mock('fs', () => ({ + createReadStream: (...args: any[]) => mockCreateReadStream(...args), + statSync: (...args: any[]) => mockStatSync(...args), + default: { + createReadStream: (...args: any[]) => mockCreateReadStream(...args), + statSync: (...args: any[]) => mockStatSync(...args), + }, +})); + +vi.mock('../../../src/services/aws/client', () => ({ + client: { send: (...args: any[]) => mockClientSend(...args) }, +})); + +vi.mock('../../../src/helper', () => ({ + fileOperationLimiter: (_req: any, _res: any, next: any) => next(), + deleteFolderSync: vi.fn(), +})); + +vi.mock('../../../src/services/fileProcessing', () => ({ + default: (...args: any[]) => mockHandleFileProcessing(...args), +})); + +vi.mock('../../../src/services/createMapper', () => ({ + default: (...args: any[]) => mockCreateMapper(...args), +})); + +vi.mock('../../../src/config/index', () => ({ default: mockConfig })); + +vi.mock('@aws-sdk/client-s3', () => ({ + GetObjectCommand: vi.fn().mockImplementation(function (this: any, p: any) { Object.assign(this, p); }), + CreateMultipartUploadCommand: vi.fn().mockImplementation(function (this: any, p: any) { Object.assign(this, p); }), + UploadPartCommand: vi.fn().mockImplementation(function (this: any, p: any) { Object.assign(this, p); }), + CompleteMultipartUploadCommand: vi.fn().mockImplementation(function (this: any, p: any) { Object.assign(this, p); }), +})); + +function getHandler(router: any, method: string, routePath: string) { + const layer = router.stack.find( + (l: any) => l.route?.path === routePath && l.route?.methods?.[method] + ); + if (!layer) throw new Error(`Route ${method.toUpperCase()} ${routePath} not found`); + const handlers = layer.route.stack; + return handlers[handlers.length - 1].handle; +} + +function mockReq(overrides: any = {}) { + return { headers: { projectid: 'proj-1', app_token: 'tk', affix: 'csm' }, ...overrides }; +} + +function mockRes() { + return { + headersSent: false, + status: vi.fn().mockReturnThis(), + json: vi.fn().mockReturnThis(), + send: vi.fn().mockReturnThis(), + }; +} + +async function waitFor(fn: () => boolean, ms = 300) { + const t = Date.now(); + while (Date.now() - t < ms) { + if (fn()) return; + await new Promise((r) => setTimeout(r, 10)); + } +} + +describe('routes/index', () => { + let router: any; + + beforeEach(async () => { + vi.clearAllMocks(); + Object.assign(mockConfig, { + cmsType: 'wordpress', + isLocalPath: true, + localPath: 'sql', + awsData: { awsRegion: 'us-east-2', bucketName: 'test-bucket', bucketKey: 'project/test.zip' }, + mysql: { host: 'localhost', user: 'root', password: 'pw', database: 'drupal', port: '3306' }, + assetsConfig: { base_url: 'http://test.com', public_path: '/files' }, + plan: { dropdown: { optionLimit: 100 } }, + }); + const mod = await import('../../../src/routes/index'); + router = mod.default; + }); + + it('should export a router with 3 routes', () => { + expect(router).toBeDefined(); + const routes = router.stack.filter((l: any) => l.route); + expect(routes.length).toBe(3); + }); + + describe('GET /config', () => { + it('should return config without mysql password', async () => { + const handler = getHandler(router, 'get', '/config'); + const res = mockRes(); + await handler(mockReq(), res); + + expect(res.json).toHaveBeenCalledTimes(1); + const sent = res.json.mock.calls[0][0]; + expect(sent.mysql.password).toBeUndefined(); + expect(sent.mysql.host).toBe('localhost'); + expect(sent.cmsType).toBe('wordpress'); + }); + }); + + describe('POST /upload', () => { + it('should upload file via multipart', async () => { + const handler = getHandler(router, 'post', '/upload'); + mockClientSend + .mockResolvedValueOnce({ UploadId: 'uid-1' }) + .mockResolvedValueOnce({ ETag: 'etag-1' }) + .mockResolvedValueOnce({}); + + const req = mockReq({ file: { originalname: 'test.zip', buffer: Buffer.from('data') } }); + const res = mockRes(); + await handler(req, res); + expect(res.send).toHaveBeenCalledWith('file uploaded sucessfully.'); + }); + + it('should skip upload when no file buffer', async () => { + const handler = getHandler(router, 'post', '/upload'); + const res = mockRes(); + await handler(mockReq({ file: null }), res); + expect(res.send).toHaveBeenCalledWith('file uploaded sucessfully.'); + expect(mockClientSend).not.toHaveBeenCalled(); + }); + + it('should handle upload S3 error', async () => { + const handler = getHandler(router, 'post', '/upload'); + mockClientSend.mockRejectedValueOnce(new Error('S3 fail')); + const res = mockRes(); + await handler(mockReq({ file: { originalname: 'f.zip', buffer: Buffer.from('x') } }), res); + }); + }); + + describe('GET /validator — SQL path', () => { + it('should process SQL and create mapper on 200', async () => { + mockConfig.localPath = 'sql'; + mockHandleFileProcessing.mockResolvedValue({ status: 200, message: 'OK', file_details: {} }); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + await handler(mockReq(), res); + + expect(mockHandleFileProcessing).toHaveBeenCalledWith('sql', null, 'wordpress', 'sql'); + expect(mockCreateMapper).toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(200); + }); + + it('should not call mapper when status is not 200', async () => { + mockConfig.localPath = 'SQL'; + mockHandleFileProcessing.mockResolvedValue({ status: 400, message: 'Bad', file_details: {} }); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + await handler(mockReq(), res); + + expect(mockCreateMapper).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(400); + }); + + it('should return 500 when file processing returns null', async () => { + mockConfig.localPath = 'sql'; + mockHandleFileProcessing.mockResolvedValue(null); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + await handler(mockReq(), res); + + expect(res.status).toHaveBeenCalledWith(500); + }); + + it('should strip mysql password from SQL response', async () => { + mockConfig.localPath = 'sql'; + mockHandleFileProcessing.mockResolvedValue({ status: 200, message: 'OK', file_details: {} }); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + await handler(mockReq(), res); + + const sent = res.json.mock.calls[0][0]; + expect(sent.file_details.mySQLDetails.password).toBeUndefined(); + }); + }); + + describe('GET /validator — directory path', () => { + it('should handle directory and call mapper on 200', async () => { + mockConfig.localPath = '/tmp/content-dir'; + mockStatSync.mockReturnValue({ isDirectory: () => true }); + mockHandleFileProcessing.mockResolvedValue({ status: 200, message: 'OK' }); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + await handler(mockReq(), res); + + expect(mockHandleFileProcessing).toHaveBeenCalledWith('folder', '/tmp/content-dir', 'wordpress', 'content-dir'); + expect(mockCreateMapper).toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(200); + }); + + it('should not call mapper when directory validation fails', async () => { + mockConfig.localPath = '/tmp/bad'; + mockStatSync.mockReturnValue({ isDirectory: () => true }); + mockHandleFileProcessing.mockResolvedValue({ status: 400, message: 'Bad' }); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + await handler(mockReq(), res); + + expect(mockCreateMapper).not.toHaveBeenCalled(); + }); + + it('should return 500 on stat error', async () => { + mockConfig.localPath = '/nonexistent'; + mockStatSync.mockImplementation(() => { throw new Error('ENOENT'); }); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + await handler(mockReq(), res); + + expect(res.status).toHaveBeenCalledWith(500); + expect(res.json).toHaveBeenCalledWith(expect.objectContaining({ status: 'error' })); + }); + }); + + describe('GET /validator — file path (XML)', () => { + it('should process XML file stream', async () => { + mockConfig.localPath = '/tmp/data.xml'; + mockStatSync.mockReturnValue({ isDirectory: () => false }); + + const stream = new EventEmitter(); + mockCreateReadStream.mockReturnValue(stream); + mockHandleFileProcessing.mockResolvedValue({ status: 200, message: 'Valid' }); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + handler(mockReq(), res); + + await new Promise((r) => setTimeout(r, 20)); + stream.emit('data', 'xml'); + stream.emit('end'); + await waitFor(() => res.status.mock.calls.length > 0); + + expect(mockHandleFileProcessing).toHaveBeenCalledWith('xml', 'xml', 'wordpress', 'data'); + expect(res.status).toHaveBeenCalledWith(200); + }); + + it('should return 400 when XML stream is empty', async () => { + mockConfig.localPath = '/tmp/empty.xml'; + mockStatSync.mockReturnValue({ isDirectory: () => false }); + + const stream = new EventEmitter(); + mockCreateReadStream.mockReturnValue(stream); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + handler(mockReq(), res); + + await new Promise((r) => setTimeout(r, 20)); + stream.emit('end'); + await waitFor(() => res.status.mock.calls.length > 0); + + expect(res.status).toHaveBeenCalledWith(400); + }); + + it('should call createMapper for valid XML with 200', async () => { + mockConfig.localPath = '/tmp/data.xml'; + mockStatSync.mockReturnValue({ isDirectory: () => false }); + + const stream = new EventEmitter(); + mockCreateReadStream.mockReturnValue(stream); + mockHandleFileProcessing.mockResolvedValue({ status: 200, message: 'OK' }); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + handler(mockReq(), res); + + await new Promise((r) => setTimeout(r, 20)); + stream.emit('data', ''); + stream.emit('end'); + await waitFor(() => res.json.mock.calls.length > 0); + + expect(mockCreateMapper).toHaveBeenCalled(); + }); + + it('should handle XML processing error', async () => { + mockConfig.localPath = '/tmp/data.xml'; + mockStatSync.mockReturnValue({ isDirectory: () => false }); + + const stream = new EventEmitter(); + mockCreateReadStream.mockReturnValue(stream); + mockHandleFileProcessing.mockRejectedValue(new Error('parse fail')); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + handler(mockReq(), res); + + await new Promise((r) => setTimeout(r, 20)); + stream.emit('data', 'bad data'); + stream.emit('end'); + await waitFor(() => res.status.mock.calls.length > 0); + + expect(res.status).toHaveBeenCalledWith(500); + }); + }); + + describe('GET /validator — file path (ZIP)', () => { + it('should process ZIP file stream', async () => { + mockConfig.localPath = '/tmp/data.zip'; + mockStatSync.mockReturnValue({ isDirectory: () => false }); + + const stream = new EventEmitter(); + mockCreateReadStream.mockReturnValue(stream); + mockHandleFileProcessing.mockResolvedValue({ status: 200, message: 'OK', file: 'inner' }); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + handler(mockReq(), res); + + await new Promise((r) => setTimeout(r, 20)); + stream.emit('data', Buffer.from('zipdata')); + stream.emit('end'); + await waitFor(() => res.status.mock.calls.length > 0); + + expect(mockHandleFileProcessing).toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(200); + expect(mockCreateMapper).toHaveBeenCalled(); + }); + + it('should return 400 when ZIP stream is empty', async () => { + mockConfig.localPath = '/tmp/data.zip'; + mockStatSync.mockReturnValue({ isDirectory: () => false }); + + const stream = new EventEmitter(); + mockCreateReadStream.mockReturnValue(stream); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + handler(mockReq(), res); + + await new Promise((r) => setTimeout(r, 20)); + stream.emit('end'); + await waitFor(() => res.status.mock.calls.length > 0); + + expect(res.status).toHaveBeenCalledWith(400); + }); + + it('should handle stream error', async () => { + mockConfig.localPath = '/tmp/data.zip'; + mockStatSync.mockReturnValue({ isDirectory: () => false }); + + const stream = new EventEmitter(); + mockCreateReadStream.mockReturnValue(stream); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + handler(mockReq(), res); + + await new Promise((r) => setTimeout(r, 20)); + stream.emit('error', new Error('read error')); + await waitFor(() => res.status.mock.calls.length > 0); + + expect(res.status).toHaveBeenCalledWith(500); + }); + + it('should handle ZIP processing error in end callback', async () => { + mockConfig.localPath = '/tmp/data.zip'; + mockStatSync.mockReturnValue({ isDirectory: () => false }); + + const stream = new EventEmitter(); + mockCreateReadStream.mockReturnValue(stream); + mockHandleFileProcessing.mockRejectedValue(new Error('zip fail')); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + handler(mockReq(), res); + + await new Promise((r) => setTimeout(r, 20)); + stream.emit('data', Buffer.from('zipdata')); + stream.emit('end'); + await waitFor(() => res.status.mock.calls.length > 0); + + expect(res.status).toHaveBeenCalledWith(500); + }); + }); + + describe('GET /validator — S3 path', () => { + it('should process S3 file', async () => { + mockConfig.isLocalPath = false; + + const bodyStream = new EventEmitter(); + mockClientSend.mockResolvedValue({ Body: bodyStream }); + mockHandleFileProcessing.mockResolvedValue({ status: 200, message: 'OK' }); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + handler(mockReq(), res); + + await new Promise((r) => setTimeout(r, 50)); + bodyStream.emit('data', Buffer.from('s3data')); + bodyStream.emit('end'); + await waitFor(() => res.status.mock.calls.length > 0); + + expect(mockHandleFileProcessing).toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(200); + }); + + it('should handle S3 with file-specific path', async () => { + mockConfig.isLocalPath = false; + + const bodyStream = new EventEmitter(); + mockClientSend.mockResolvedValue({ Body: bodyStream }); + mockHandleFileProcessing.mockResolvedValue({ status: 200, message: 'OK', file: 'nested' }); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + handler(mockReq(), res); + + await new Promise((r) => setTimeout(r, 50)); + bodyStream.emit('data', Buffer.from('s3zip')); + bodyStream.emit('end'); + await waitFor(() => res.status.mock.calls.length > 0); + + expect(mockCreateMapper).toHaveBeenCalled(); + }); + + it('should handle empty S3 body', async () => { + mockConfig.isLocalPath = false; + mockClientSend.mockResolvedValue({ Body: null }); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + await handler(mockReq(), res); + await new Promise((r) => setTimeout(r, 50)); + + expect(res.status).toHaveBeenCalledWith(500); + }); + + it('should handle S3 error', async () => { + mockConfig.isLocalPath = false; + mockClientSend.mockRejectedValue(new Error('S3 error')); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + await handler(mockReq(), res); + + expect(res.status).toHaveBeenCalledWith(500); + }); + + it('should handle S3 stream error event', async () => { + mockConfig.isLocalPath = false; + + const bodyStream = new EventEmitter(); + mockClientSend.mockResolvedValue({ Body: bodyStream }); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + handler(mockReq(), res); + + await new Promise((r) => setTimeout(r, 50)); + bodyStream.emit('error', new Error('stream fail')); + await waitFor(() => res.status.mock.calls.length > 0); + + expect(res.status).toHaveBeenCalledWith(500); + }); + + it('should handle empty S3 buffer in end callback', async () => { + mockConfig.isLocalPath = false; + + const bodyStream = new EventEmitter(); + mockClientSend.mockResolvedValue({ Body: bodyStream }); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + handler(mockReq(), res); + + await new Promise((r) => setTimeout(r, 50)); + bodyStream.emit('end'); + await waitFor(() => res.status.mock.calls.length > 0 || res.json.mock.calls.length > 0, 500); + }); + + it('should handle S3 processing error in end callback', async () => { + mockConfig.isLocalPath = false; + + const bodyStream = new EventEmitter(); + mockClientSend.mockResolvedValue({ Body: bodyStream }); + mockHandleFileProcessing.mockRejectedValue(new Error('process fail')); + + const handler = getHandler(router, 'get', '/validator'); + const res = mockRes(); + handler(mockReq(), res); + + await new Promise((r) => setTimeout(r, 50)); + bodyStream.emit('data', Buffer.from('data')); + bodyStream.emit('end'); + await waitFor(() => res.status.mock.calls.length > 0); + + expect(res.status).toHaveBeenCalledWith(500); + }); + }); +}); diff --git a/upload-api/tests/unit/services/aws-client.test.ts b/upload-api/tests/unit/services/aws-client.test.ts new file mode 100644 index 000000000..3bab12d4a --- /dev/null +++ b/upload-api/tests/unit/services/aws-client.test.ts @@ -0,0 +1,40 @@ +import { describe, it, expect, vi } from 'vitest'; + +const { mockS3Client } = vi.hoisted(() => ({ + mockS3Client: vi.fn().mockImplementation(function (this: any, config: any) { + this.config = config; + this.send = vi.fn(); + }), +})); + +vi.mock('../../../src/config', () => ({ + default: { + awsData: { + awsRegion: 'us-east-2', + awsAccessKeyId: 'test-key', + awsSecretAccessKey: 'test-secret', + awsSessionToken: 'test-token', + }, + }, +})); + +vi.mock('@aws-sdk/client-s3', () => ({ + S3Client: mockS3Client, + GetObjectCommand: vi.fn(), +})); + +describe('aws/client', () => { + it('should create S3Client with config from environment', async () => { + const { client } = await import('../../../src/services/aws/client'); + + expect(mockS3Client).toHaveBeenCalledWith({ + region: 'us-east-2', + credentials: { + accessKeyId: 'test-key', + secretAccessKey: 'test-secret', + sessionToken: 'test-token', + }, + }); + expect(client).toBeDefined(); + }); +}); diff --git a/upload-api/tests/unit/services/contentful.service.test.ts b/upload-api/tests/unit/services/contentful.service.test.ts new file mode 100644 index 000000000..8c1aa57ec --- /dev/null +++ b/upload-api/tests/unit/services/contentful.service.test.ts @@ -0,0 +1,49 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockAxiosRequest } = vi.hoisted(() => ({ + mockAxiosRequest: vi.fn(), +})); + +vi.mock('axios', () => ({ default: { request: mockAxiosRequest } })); +vi.mock('../../../src/utils/logger', () => ({ default: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn() } })); + +import createContentfulMapper from '../../../src/services/contentful/index'; + +describe('createContentfulMapper', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should be a function', () => { + expect(typeof createContentfulMapper).toBe('function'); + }); + + it('should handle error gracefully when extractLocale fails', async () => { + const config: any = { localPath: '/nonexistent/path' }; + await expect( + createContentfulMapper('proj-1', 'token', 'csm', config) + ).resolves.toBeUndefined(); + }); + + it('should handle empty localPath', async () => { + const config: any = { localPath: '' }; + await expect( + createContentfulMapper('proj-1', 'token', 'csm', config) + ).resolves.toBeUndefined(); + }); + + it('should handle undefined config values', async () => { + const config: any = {}; + await expect( + createContentfulMapper('proj-1', 'token', 'csm', config) + ).resolves.toBeUndefined(); + }); + + it('should handle API error response', async () => { + const config: any = { localPath: '/tmp/nonexistent' }; + mockAxiosRequest.mockRejectedValue({ response: { data: 'API Error' } }); + await expect( + createContentfulMapper('proj-1', 'token', 'csm', config) + ).resolves.toBeUndefined(); + }); +}); diff --git a/upload-api/tests/unit/services/createMapper.test.ts b/upload-api/tests/unit/services/createMapper.test.ts new file mode 100644 index 000000000..69f7f7fc2 --- /dev/null +++ b/upload-api/tests/unit/services/createMapper.test.ts @@ -0,0 +1,160 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { + mockCreateSitecoreMapper, mockCreateContentfulMapper, + mockCreateWordpressMapper, mockCreateAemMapper, mockCreateDrupalMapper, + mockDeleteFolderSync, mockReaddirSync, mockExistsSync, mockStatSync, +} = vi.hoisted(() => ({ + mockCreateSitecoreMapper: vi.fn(), + mockCreateContentfulMapper: vi.fn(), + mockCreateWordpressMapper: vi.fn(), + mockCreateAemMapper: vi.fn(), + mockCreateDrupalMapper: vi.fn(), + mockDeleteFolderSync: vi.fn(), + mockReaddirSync: vi.fn().mockReturnValue([]), + mockExistsSync: vi.fn().mockReturnValue(false), + mockStatSync: vi.fn().mockReturnValue({ isDirectory: () => false }), +})); + +vi.mock('../../../src/controllers/sitecore', () => ({ default: mockCreateSitecoreMapper })); +vi.mock('../../../src/services/contentful', () => ({ default: mockCreateContentfulMapper })); +vi.mock('../../../src/controllers/wordpress', () => ({ default: mockCreateWordpressMapper })); +vi.mock('../../../src/controllers/aem', () => ({ createAemMapper: mockCreateAemMapper })); +vi.mock('../../../src/services/drupal', () => ({ default: mockCreateDrupalMapper })); +vi.mock('../../../src/helper', () => ({ + deleteFolderSync: mockDeleteFolderSync, + fileOperationLimiter: vi.fn(), +})); +vi.mock('../../../src/utils/logger', () => ({ + default: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn() }, +})); + +vi.mock('fs', () => ({ + default: { + readdirSync: (...a: any[]) => mockReaddirSync(...a), + existsSync: (...a: any[]) => mockExistsSync(...a), + statSync: (...a: any[]) => mockStatSync(...a), + }, + readdirSync: (...a: any[]) => mockReaddirSync(...a), + existsSync: (...a: any[]) => mockExistsSync(...a), + statSync: (...a: any[]) => mockStatSync(...a), +})); + +import createMapper from '../../../src/services/createMapper'; + +describe('createMapper', () => { + const defaultConfig: any = { cmsType: 'wordpress', localPath: '/tmp/test' }; + + beforeEach(() => { + vi.clearAllMocks(); + mockReaddirSync.mockReturnValue([]); + mockExistsSync.mockReturnValue(false); + mockStatSync.mockReturnValue({ isDirectory: () => false }); + }); + + it('should dispatch to sitecore mapper', async () => { + mockCreateSitecoreMapper.mockResolvedValue('sitecore-result'); + const config = { ...defaultConfig, cmsType: 'sitecore' }; + const result = await createMapper('/path', 'proj-1', 'token', 'csm', config); + expect(mockCreateSitecoreMapper).toHaveBeenCalledWith('/path', 'proj-1', 'token', 'csm', config); + expect(result).toBe('sitecore-result'); + }); + + it('should dispatch to contentful mapper', async () => { + mockCreateContentfulMapper.mockResolvedValue('contentful-result'); + const config = { ...defaultConfig, cmsType: 'contentful' }; + const result = await createMapper('/path', 'proj-1', 'token', 'csm', config); + expect(mockCreateContentfulMapper).toHaveBeenCalledWith('proj-1', 'token', 'csm', config); + expect(result).toBe('contentful-result'); + }); + + it('should dispatch to wordpress mapper', async () => { + mockCreateWordpressMapper.mockResolvedValue('wordpress-result'); + const config = { ...defaultConfig, cmsType: 'wordpress' }; + const result = await createMapper('/path', 'proj-1', 'token', 'csm', config); + expect(mockCreateWordpressMapper).toHaveBeenCalledWith('/path', 'proj-1', 'token', 'csm', config); + expect(result).toBe('wordpress-result'); + }); + + it('should dispatch to aem mapper', async () => { + mockCreateAemMapper.mockResolvedValue('aem-result'); + const config = { ...defaultConfig, cmsType: 'aem' }; + const result = await createMapper('/path', 'proj-1', 'token', 'csm', config); + expect(mockCreateAemMapper).toHaveBeenCalledWith('/path', 'proj-1', 'token', 'csm'); + expect(result).toBe('aem-result'); + }); + + it('should dispatch to drupal mapper', async () => { + mockCreateDrupalMapper.mockResolvedValue('drupal-result'); + const config = { ...defaultConfig, cmsType: 'drupal' }; + const result = await createMapper('/path', 'proj-1', 'token', 'csm', config); + expect(mockCreateDrupalMapper).toHaveBeenCalledWith(config, 'proj-1', 'token', 'csm'); + expect(result).toBe('drupal-result'); + }); + + it('should return false for unknown CMS type', async () => { + const config = { ...defaultConfig, cmsType: 'unknown' }; + const result = await createMapper('/path', 'proj-1', 'token', 'csm', config); + expect(result).toBe(false); + }); + + it('should handle case-insensitive CMS types', async () => { + mockCreateWordpressMapper.mockResolvedValue('ok'); + const config = { ...defaultConfig, cmsType: 'WordPress' }; + const result = await createMapper('/path', 'proj-1', 'token', 'csm', config); + expect(mockCreateWordpressMapper).toHaveBeenCalled(); + expect(result).toBe('ok'); + }); + + describe('clearAllMigrationData', () => { + it('should delete folders ending with MigrationData', async () => { + mockReaddirSync.mockReturnValue(['sitecoreMigrationData', 'drupalMigrationData', 'other']); + mockExistsSync.mockReturnValue(true); + mockStatSync.mockReturnValue({ isDirectory: () => true }); + + mockCreateWordpressMapper.mockResolvedValue(undefined); + await createMapper('/path', 'proj-1', 'token', 'csm', defaultConfig); + + expect(mockDeleteFolderSync).toHaveBeenCalledTimes(2); + expect(mockDeleteFolderSync).toHaveBeenCalledWith(expect.stringContaining('sitecoreMigrationData')); + expect(mockDeleteFolderSync).toHaveBeenCalledWith(expect.stringContaining('drupalMigrationData')); + }); + + it('should skip non-directory items', async () => { + mockReaddirSync.mockReturnValue(['contentfulMigrationData']); + mockExistsSync.mockReturnValue(true); + mockStatSync.mockReturnValue({ isDirectory: () => false }); + + mockCreateWordpressMapper.mockResolvedValue(undefined); + await createMapper('/path', 'proj-1', 'token', 'csm', defaultConfig); + + expect(mockDeleteFolderSync).not.toHaveBeenCalled(); + }); + + it('should handle no migration folders', async () => { + mockReaddirSync.mockReturnValue(['src', 'node_modules', 'package.json']); + + mockCreateWordpressMapper.mockResolvedValue(undefined); + await createMapper('/path', 'proj-1', 'token', 'csm', defaultConfig); + + expect(mockDeleteFolderSync).not.toHaveBeenCalled(); + }); + + it('should handle delete error gracefully', async () => { + mockReaddirSync.mockReturnValue(['sitecoreMigrationData']); + mockExistsSync.mockReturnValue(true); + mockStatSync.mockReturnValue({ isDirectory: () => true }); + mockDeleteFolderSync.mockImplementation(() => { throw new Error('delete err'); }); + + mockCreateWordpressMapper.mockResolvedValue(undefined); + await createMapper('/path', 'proj-1', 'token', 'csm', defaultConfig); + }); + + it('should handle readdirSync error gracefully', async () => { + mockReaddirSync.mockImplementation(() => { throw new Error('read err'); }); + + mockCreateWordpressMapper.mockResolvedValue(undefined); + await createMapper('/path', 'proj-1', 'token', 'csm', defaultConfig); + }); + }); +}); diff --git a/upload-api/tests/unit/services/drupal.service.test.ts b/upload-api/tests/unit/services/drupal.service.test.ts new file mode 100644 index 000000000..774ddeccf --- /dev/null +++ b/upload-api/tests/unit/services/drupal.service.test.ts @@ -0,0 +1,57 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockAxiosRequest } = vi.hoisted(() => ({ + mockAxiosRequest: vi.fn(), +})); + +vi.mock('axios', () => ({ default: { request: mockAxiosRequest } })); +vi.mock('../../../src/utils/logger', () => ({ default: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn() } })); +vi.mock('fs', () => ({ + default: { existsSync: vi.fn().mockReturnValue(false), promises: { readFile: vi.fn() } }, + existsSync: vi.fn().mockReturnValue(false), + promises: { readFile: vi.fn() }, +})); + +import createDrupalMapper from '../../../src/services/drupal/index'; + +describe('createDrupalMapper', () => { + const config: any = { + mysql: { host: 'localhost', user: 'root', password: 'pw', database: 'drupal' }, + assetsConfig: { base_url: 'http://test.com', public_path: '/files' }, + }; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should be a function', () => { + expect(typeof createDrupalMapper).toBe('function'); + }); + + it('should handle errors gracefully when extraction fails', async () => { + await expect( + createDrupalMapper(config, 'proj-1', 'token', 'csm') + ).resolves.toBeUndefined(); + }); + + it('should handle missing mysql config gracefully', async () => { + const badConfig: any = { mysql: {} }; + await expect( + createDrupalMapper(badConfig, 'proj-1', 'token', 'csm') + ).resolves.toBeUndefined(); + }); + + it('should handle null config values', async () => { + const nullConfig: any = { mysql: null }; + await expect( + createDrupalMapper(nullConfig, 'proj-1', 'token', 'csm') + ).resolves.toBeUndefined(); + }); + + it('should handle API error response', async () => { + mockAxiosRequest.mockRejectedValue({ response: { data: 'API Error' } }); + await expect( + createDrupalMapper(config, 'proj-1', 'token', 'csm') + ).resolves.toBeUndefined(); + }); +}); diff --git a/upload-api/tests/unit/services/fileProcessing.test.ts b/upload-api/tests/unit/services/fileProcessing.test.ts new file mode 100644 index 000000000..2f5be5d54 --- /dev/null +++ b/upload-api/tests/unit/services/fileProcessing.test.ts @@ -0,0 +1,183 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockValidator, mockSaveZip, mockSaveJson, mockParseXmlToJson } = vi.hoisted(() => ({ + mockValidator: vi.fn(), + mockSaveZip: vi.fn(), + mockSaveJson: vi.fn(), + mockParseXmlToJson: vi.fn(), +})); + +vi.mock('../../../src/validators/index', () => ({ default: mockValidator })); +vi.mock('../../../src/helper/index', () => ({ + saveZip: mockSaveZip, + saveJson: mockSaveJson, + parseXmlToJson: mockParseXmlToJson, + fileOperationLimiter: vi.fn(), + deleteFolderSync: vi.fn(), + getFileName: vi.fn(), +})); + +vi.mock('../../../src/utils/logger', () => ({ + default: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn() }, +})); + +vi.mock('../../../src/config/index', () => ({ + default: { + cmsType: 'wordpress', + mysql: { host: 'localhost', user: 'root', password: 'pw', database: 'db', port: '3306' }, + assetsConfig: { base_url: 'http://test.com', public_path: '/files' }, + }, +})); + +vi.mock('jszip', () => ({ + default: vi.fn().mockImplementation(function (this: any) { + this.loadAsync = vi.fn().mockResolvedValue(this); + }), +})); + +import handleFileProcessing from '../../../src/services/fileProcessing'; + +describe('handleFileProcessing', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('zip files', () => { + it('should return OK on valid zip with successful save', async () => { + mockValidator.mockResolvedValue(true); + mockSaveZip.mockResolvedValue({ isSaved: true, filePath: 'extracted' }); + + const result = await handleFileProcessing('zip', Buffer.from('fake-zip'), 'sitecore', 'test'); + expect(result?.status).toBe(200); + expect(result?.message).toContain('validated successfully'); + expect(result?.file).toBe('extracted'); + }); + + it('should return UNAUTHORIZED on invalid zip', async () => { + mockValidator.mockResolvedValue(false); + + const result = await handleFileProcessing('zip', Buffer.from('bad-zip'), 'sitecore', 'test'); + expect(result?.status).toBe(401); + expect(result?.message).toContain('validation failed'); + }); + + it('should return undefined when zip is valid but save fails', async () => { + mockValidator.mockResolvedValue(true); + mockSaveZip.mockResolvedValue({ isSaved: false }); + + const result = await handleFileProcessing('zip', Buffer.from('fake-zip'), 'sitecore', 'test'); + expect(result).toBeUndefined(); + }); + }); + + describe('xml files', () => { + it('should return OK for valid wordpress XML', async () => { + mockValidator.mockResolvedValue(true); + mockParseXmlToJson.mockResolvedValue({ rss: {} }); + mockSaveJson.mockResolvedValue(true); + + const result = await handleFileProcessing('xml', Buffer.from(''), 'wordpress', 'test'); + expect(result?.status).toBe(200); + }); + + it('should return UNAUTHORIZED when XML save fails', async () => { + mockValidator.mockResolvedValue(true); + mockParseXmlToJson.mockResolvedValue({ rss: {} }); + mockSaveJson.mockResolvedValue(false); + + const result = await handleFileProcessing('xml', Buffer.from(''), 'wordpress', 'test'); + expect(result?.status).toBe(401); + }); + + it('should return undefined when XML validation fails', async () => { + mockValidator.mockResolvedValue(false); + + const result = await handleFileProcessing('xml', Buffer.from(''), 'wordpress', 'test'); + expect(result).toBeUndefined(); + }); + + it('should handle drupal XML', async () => { + mockValidator.mockResolvedValue(true); + mockParseXmlToJson.mockResolvedValue({ data: {} }); + mockSaveJson.mockResolvedValue(true); + + const result = await handleFileProcessing('xml', Buffer.from(''), 'drupal', 'test'); + expect(result?.status).toBe(200); + }); + }); + + describe('folder', () => { + it('should return OK for valid folder', async () => { + mockValidator.mockResolvedValue(true); + + const result = await handleFileProcessing('folder', '/path/to/aem', 'aem', 'aem-folder'); + expect(result?.status).toBe(200); + }); + + it('should return UNAUTHORIZED for invalid folder', async () => { + mockValidator.mockResolvedValue(false); + + const result = await handleFileProcessing('folder', '/path/to/aem', 'aem', 'aem-folder'); + expect(result?.status).toBe(401); + }); + }); + + describe('sql', () => { + it('should return OK on valid SQL connection (boolean true)', async () => { + mockValidator.mockResolvedValue(true); + + const result = await handleFileProcessing('sql', null, 'drupal', 'sql'); + expect(result?.status).toBe(200); + expect(result?.message).toBe('File validated successfully'); + }); + + it('should return OK on valid SQL connection (object { success: true })', async () => { + mockValidator.mockResolvedValue({ success: true }); + + const result = await handleFileProcessing('sql', null, 'drupal', 'sql'); + expect(result?.status).toBe(200); + }); + + it('should return UNAUTHORIZED on failed SQL connection', async () => { + mockValidator.mockResolvedValue({ success: false, error: 'DB error' }); + + const result = await handleFileProcessing('sql', null, 'drupal', 'sql'); + expect(result?.status).toBe(401); + expect(result?.message).toBe('DB error'); + }); + + it('should return SERVER_ERROR on exception', async () => { + mockValidator.mockRejectedValue(new Error('Connection timeout')); + + const result = await handleFileProcessing('sql', null, 'drupal', 'sql'); + expect(result?.status).toBe(500); + }); + }); + + describe('other file types (json/default)', () => { + it('should return OK for valid JSON file', async () => { + mockValidator.mockResolvedValue(true); + + const result = await handleFileProcessing( + 'json', + Buffer.from('{"contentTypes":[]}'), + 'contentful', + 'test' + ); + expect(result?.status).toBe(200); + }); + + it('should return UNAUTHORIZED for invalid JSON file', async () => { + mockValidator.mockResolvedValue(false); + + const result = await handleFileProcessing('json', Buffer.from('bad'), 'contentful', 'test'); + expect(result?.status).toBe(401); + }); + + it('should return UNAUTHORIZED when buffer is null for non-SQL file', async () => { + const result = await handleFileProcessing('json', null, 'contentful', 'test'); + expect(result?.status).toBe(401); + expect(result?.message).toBe('File data is missing'); + }); + }); +}); diff --git a/upload-api/tests/unit/utils/index.test.ts b/upload-api/tests/unit/utils/index.test.ts new file mode 100644 index 000000000..75227a22a --- /dev/null +++ b/upload-api/tests/unit/utils/index.test.ts @@ -0,0 +1,71 @@ +import { describe, it, expect } from 'vitest'; +import { getLogMessage, safePromise } from '../../../src/utils/index'; + +describe('utils/index', () => { + describe('getLogMessage', () => { + it('should return a log message object with methodName and message', () => { + const result = getLogMessage('testMethod', 'test message'); + expect(result.methodName).toBe('testMethod'); + expect(result.message).toBe('test message'); + }); + + it('should include user when provided', () => { + const user = { id: 'user-1', name: 'Test' }; + const result = getLogMessage('testMethod', 'test message', user); + expect(result.user).toEqual(user); + }); + + it('should include error when provided', () => { + const error = new Error('test error'); + const result = getLogMessage('testMethod', 'test message', {}, error); + expect(result.error).toBe(error); + expect(result.methodName).toBe('testMethod'); + }); + + it('should include both user and error when provided', () => { + const user = { id: 'user-1' }; + const error = new Error('fail'); + const result = getLogMessage('testMethod', 'test message', user, error); + expect(result.user).toEqual(user); + expect(result.error).toBe(error); + }); + + it('should include user with default empty object', () => { + const result = getLogMessage('testMethod', 'msg'); + expect(result).toHaveProperty('methodName', 'testMethod'); + expect(result).toHaveProperty('message', 'msg'); + }); + + it('should not include error when not provided', () => { + const result = getLogMessage('method', 'msg'); + expect(result).not.toHaveProperty('error'); + }); + }); + + describe('safePromise', () => { + it('should return [null, result] on resolved promise', async () => { + const [err, result] = await safePromise(Promise.resolve('success')); + expect(err).toBeNull(); + expect(result).toBe('success'); + }); + + it('should return [error] on rejected promise', async () => { + const error = new Error('fail'); + const [err, result] = await safePromise(Promise.reject(error)); + expect(err).toBe(error); + expect(result).toBeUndefined(); + }); + + it('should handle promise resolving with undefined', async () => { + const [err, result] = await safePromise(Promise.resolve(undefined)); + expect(err).toBeNull(); + expect(result).toBeUndefined(); + }); + + it('should handle promise resolving with null', async () => { + const [err, result] = await safePromise(Promise.resolve(null)); + expect(err).toBeNull(); + expect(result).toBeNull(); + }); + }); +}); diff --git a/upload-api/tests/unit/utils/sanitize-path.utils.test.ts b/upload-api/tests/unit/utils/sanitize-path.utils.test.ts new file mode 100644 index 000000000..bc17367e1 --- /dev/null +++ b/upload-api/tests/unit/utils/sanitize-path.utils.test.ts @@ -0,0 +1,178 @@ +import { describe, it, expect } from 'vitest'; +import path from 'path'; +import { + sanitizeFilename, + isValidPathSegment, + sanitizeId, + isPathWithinBase, + getSafePath, +} from '../../../src/utils/sanitize-path.utils'; + +describe('sanitize-path.utils', () => { + describe('sanitizeFilename', () => { + it('should return basename of a safe filename', () => { + expect(sanitizeFilename('file.txt')).toBe('file.txt'); + }); + + it('should strip directory components', () => { + expect(sanitizeFilename('/path/to/file.txt')).toBe('file.txt'); + }); + + it('should remove unsafe characters', () => { + expect(sanitizeFilename('file@name!.txt')).toBe('filename.txt'); + }); + + it('should return empty string for null/undefined', () => { + expect(sanitizeFilename(null as any)).toBe(''); + expect(sanitizeFilename(undefined as any)).toBe(''); + }); + + it('should return empty string for non-string input', () => { + expect(sanitizeFilename(123 as any)).toBe(''); + }); + + it('should return empty string for empty string', () => { + expect(sanitizeFilename('')).toBe(''); + }); + + it('should allow spaces, hyphens, underscores, and dots', () => { + expect(sanitizeFilename('my file_v2.0-final.txt')).toBe('my file_v2.0-final.txt'); + }); + + it('should handle path traversal attempts', () => { + const result = sanitizeFilename('../../../etc/passwd'); + expect(result).toBe('passwd'); + }); + + it('should handle filenames with mixed path separators', () => { + const result = sanitizeFilename('some/path/file.txt'); + expect(result).toBe('file.txt'); + }); + }); + + describe('isValidPathSegment', () => { + it('should return true for alphanumeric strings', () => { + expect(isValidPathSegment('abc123')).toBe(true); + }); + + it('should allow underscores, hyphens, and dots', () => { + expect(isValidPathSegment('my-file_v2.0')).toBe(true); + }); + + it('should return false for empty string', () => { + expect(isValidPathSegment('')).toBe(false); + }); + + it('should return false for null/undefined', () => { + expect(isValidPathSegment(null as any)).toBe(false); + expect(isValidPathSegment(undefined as any)).toBe(false); + }); + + it('should return false for strings with slashes', () => { + expect(isValidPathSegment('path/to/file')).toBe(false); + }); + + it('should return false for strings with special characters', () => { + expect(isValidPathSegment('file@name!')).toBe(false); + }); + + it('should return false for strings with spaces', () => { + expect(isValidPathSegment('has space')).toBe(false); + }); + + it('should return false for non-string input', () => { + expect(isValidPathSegment(123 as any)).toBe(false); + }); + }); + + describe('sanitizeId', () => { + it('should return sanitized string for valid input', () => { + expect(sanitizeId('blt1234abcd')).toBe('blt1234abcd'); + }); + + it('should handle array input by using first element', () => { + expect(sanitizeId(['id-123', 'id-456'])).toBe('id-123'); + }); + + it('should strip directory components', () => { + expect(sanitizeId('path/to/id')).toBe('id'); + }); + + it('should remove special characters', () => { + expect(sanitizeId('id@special!')).toBe('idspecial'); + }); + + it('should return empty string for null/undefined', () => { + expect(sanitizeId(null as any)).toBe(''); + expect(sanitizeId(undefined as any)).toBe(''); + }); + + it('should return empty string for empty string', () => { + expect(sanitizeId('')).toBe(''); + }); + + it('should return empty string for empty array', () => { + expect(sanitizeId([])).toBe(''); + }); + + it('should allow dots, hyphens, and underscores', () => { + expect(sanitizeId('stack-id_v2.0')).toBe('stack-id_v2.0'); + }); + }); + + describe('isPathWithinBase', () => { + it('should return true when path is within base', () => { + expect(isPathWithinBase('/tmp/base/file.txt', '/tmp/base')).toBe(true); + }); + + it('should return true for nested paths', () => { + expect(isPathWithinBase('/tmp/base/sub/dir/file.txt', '/tmp/base')).toBe(true); + }); + + it('should return false for path traversal', () => { + expect(isPathWithinBase('/tmp/base/../other/file.txt', '/tmp/base')).toBe(false); + }); + + it('should return false for paths outside base', () => { + expect(isPathWithinBase('/other/path/file.txt', '/tmp/base')).toBe(false); + }); + + it('should return true when path equals base', () => { + expect(isPathWithinBase('/tmp/base', '/tmp/base')).toBe(true); + }); + }); + + describe('getSafePath', () => { + it('should return an absolute path', () => { + const result = getSafePath('/tmp/test.log'); + expect(path.isAbsolute(result)).toBe(true); + }); + + it('should sanitize the filename', () => { + const result = getSafePath('/tmp/test@file!.log'); + expect(result).not.toContain('@'); + expect(result).not.toContain('!'); + }); + + it('should prevent directory escape when baseDir is provided', () => { + const result = getSafePath('../../etc/passwd', '/tmp/logs'); + expect(result).toContain('/tmp/logs'); + }); + + it('should handle relative paths with baseDir', () => { + const result = getSafePath('subdir/file.log', '/tmp/logs'); + expect(result).toContain('file.log'); + expect(path.isAbsolute(result)).toBe(true); + }); + + it('should return default.log on empty input with baseDir', () => { + const result = getSafePath('', '/tmp/logs'); + expect(path.isAbsolute(result)).toBe(true); + }); + + it('should return default.log when path is outside baseDir', () => { + const result = getSafePath('/outside/path.log', '/tmp/logs'); + expect(result).toContain('default.log'); + }); + }); +}); diff --git a/upload-api/tests/unit/validators/aem.validator.test.ts b/upload-api/tests/unit/validators/aem.validator.test.ts new file mode 100644 index 000000000..6fa81cf90 --- /dev/null +++ b/upload-api/tests/unit/validators/aem.validator.test.ts @@ -0,0 +1,53 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockValidator } = vi.hoisted(() => ({ + mockValidator: vi.fn(), +})); + +vi.mock('migration-aem', () => ({ + validator: mockValidator, +})); + +import aemValidator from '../../../src/validators/aem/index'; + +describe('aemValidator', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return true when at least one validation passes', async () => { + mockValidator.mockResolvedValue([false, true, false]); + const result = await aemValidator({ data: '/path/to/aem' }); + expect(result).toBe(true); + }); + + it('should return false when all validations fail', async () => { + mockValidator.mockResolvedValue([false, false, false]); + const result = await aemValidator({ data: '/path/to/aem' }); + expect(result).toBe(false); + }); + + it('should return false when validation returns empty array', async () => { + mockValidator.mockResolvedValue([]); + const result = await aemValidator({ data: '/path/to/aem' }); + expect(result).toBe(false); + }); + + it('should return false when validation returns non-array', async () => { + mockValidator.mockResolvedValue(null); + const result = await aemValidator({ data: '/path/to/aem' }); + expect(result).toBe(false); + }); + + it('should return false on error', async () => { + mockValidator.mockRejectedValue(new Error('AEM validation error')); + const result = await aemValidator({ data: '/path/to/aem' }); + expect(result).toBe(false); + }); + + it('should return true when all validations pass', async () => { + mockValidator.mockResolvedValue([true, true, true]); + const result = await aemValidator({ data: '/path/to/aem' }); + expect(result).toBe(true); + }); +}); diff --git a/upload-api/tests/unit/validators/contentful.validator.test.ts b/upload-api/tests/unit/validators/contentful.validator.test.ts new file mode 100644 index 000000000..670939bc6 --- /dev/null +++ b/upload-api/tests/unit/validators/contentful.validator.test.ts @@ -0,0 +1,74 @@ +import { describe, it, expect, vi } from 'vitest'; + +vi.mock('../../../src/models/contentful.json', () => ({ + default: { + contentTypes: { name: 'contentTypes', required: 'true' }, + entries: { name: 'entries', required: 'true' }, + assets: { name: 'assets', required: 'true' }, + locales: { name: 'locales', required: 'true' }, + editorInterfaces: { name: 'editorInterfaces', required: 'true' }, + tags: { name: 'tags', required: 'false' }, + webhooks: { name: 'webhooks', required: 'false' }, + roles: { name: 'roles', required: 'false' }, + }, +})); + +import contentfulValidator from '../../../src/validators/contentful/index'; + +describe('contentfulValidator', () => { + it('should return true for valid JSON with all required properties', () => { + const data = JSON.stringify({ + contentTypes: [], + entries: [], + assets: [], + locales: [], + editorInterfaces: [], + }); + expect(contentfulValidator(data)).toBe(true); + }); + + it('should return true when optional properties are missing', () => { + const data = JSON.stringify({ + contentTypes: [], + entries: [], + assets: [], + locales: [], + editorInterfaces: [], + }); + expect(contentfulValidator(data)).toBe(true); + }); + + it('should return false when required property is missing', () => { + const data = JSON.stringify({ + contentTypes: [], + entries: [], + }); + expect(contentfulValidator(data)).toBe(false); + }); + + it('should return false for invalid JSON string', () => { + expect(contentfulValidator('not-json')).toBe(false); + }); + + it('should return false for empty string', () => { + expect(contentfulValidator('')).toBe(false); + }); + + it('should return true when all required and optional properties present', () => { + const data = JSON.stringify({ + contentTypes: [], + entries: [], + assets: [], + locales: [], + editorInterfaces: [], + tags: [], + webhooks: [], + roles: [], + }); + expect(contentfulValidator(data)).toBe(true); + }); + + it('should return false for null input', () => { + expect(contentfulValidator(null as any)).toBe(false); + }); +}); diff --git a/upload-api/tests/unit/validators/drupal.validator.test.ts b/upload-api/tests/unit/validators/drupal.validator.test.ts new file mode 100644 index 000000000..7a3237a61 --- /dev/null +++ b/upload-api/tests/unit/validators/drupal.validator.test.ts @@ -0,0 +1,322 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockCreateConnection, mockExecute, mockEnd, mockAxiosHead } = vi.hoisted(() => ({ + mockCreateConnection: vi.fn(), + mockExecute: vi.fn(), + mockEnd: vi.fn().mockResolvedValue(undefined), + mockAxiosHead: vi.fn(), +})); + +vi.mock('mysql2/promise', () => ({ + default: { createConnection: (...a: any[]) => mockCreateConnection(...a) }, + createConnection: (...a: any[]) => mockCreateConnection(...a), +})); + +vi.mock('axios', () => ({ + default: { head: mockAxiosHead }, +})); + +vi.mock('../../../src/utils/logger', () => ({ + default: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn() }, +})); + +import drupalValidator from '../../../src/validators/drupal/index'; + +describe('drupalValidator', () => { + const validData = { + host: 'localhost', + user: 'root', + password: 'password', + database: 'drupal_db', + port: 3306, + }; + + beforeEach(() => { + vi.clearAllMocks(); + mockEnd.mockResolvedValue(undefined); + mockCreateConnection.mockResolvedValue({ execute: mockExecute, end: mockEnd }); + }); + + it('should return success when DB validation passes', async () => { + mockExecute + .mockResolvedValueOnce([{ count: 10 }]) + .mockResolvedValueOnce([[{ name: 'field.field.node.article' }]]); + + const result = await drupalValidator({ data: validData }); + expect(result).toEqual({ success: true }); + }); + + it('should return error when host is missing', async () => { + const result = await drupalValidator({ data: { ...validData, host: '' } }); + expect(result).toEqual(expect.objectContaining({ success: false, error: expect.stringContaining('Missing') })); + }); + + it('should return error when user is missing', async () => { + const result = await drupalValidator({ data: { ...validData, user: '' } }); + expect(result).toEqual(expect.objectContaining({ success: false })); + }); + + it('should return error when database is missing', async () => { + const result = await drupalValidator({ data: { ...validData, database: '' } }); + expect(result).toEqual(expect.objectContaining({ success: false })); + }); + + it('should return error when node_field_data table does not exist', async () => { + mockExecute.mockRejectedValueOnce({ message: 'Table not found', code: 'ER_NO_SUCH_TABLE' }); + const result = await drupalValidator({ data: validData }); + expect(result).toEqual(expect.objectContaining({ success: false, error: expect.stringContaining('Required Drupal table') })); + }); + + it('should return error when config query returns empty', async () => { + mockExecute.mockResolvedValueOnce([{ count: 10 }]).mockResolvedValueOnce([[]]); + const result = await drupalValidator({ data: validData }); + expect(result).toEqual(expect.objectContaining({ success: false, error: expect.stringContaining('schema validation failed') })); + }); + + it('should return error when config table query fails with ER_NO_SUCH_TABLE', async () => { + mockExecute.mockResolvedValueOnce([{ count: 10 }]).mockRejectedValueOnce({ message: 'no table', code: 'ER_NO_SUCH_TABLE' }); + const result = await drupalValidator({ data: validData }); + expect(result).toEqual(expect.objectContaining({ success: false, error: expect.stringContaining('Required Drupal tables not found') })); + }); + + it('should return error when config table query fails with other code', async () => { + mockExecute.mockResolvedValueOnce([{ count: 10 }]).mockRejectedValueOnce({ message: 'syntax error', code: 'ER_PARSE_ERROR' }); + const result = await drupalValidator({ data: validData }); + expect(result).toEqual(expect.objectContaining({ success: false, error: expect.stringContaining('syntax error') })); + }); + + it('should handle ECONNREFUSED error', async () => { + mockCreateConnection.mockRejectedValue({ code: 'ECONNREFUSED', message: 'Connection refused' }); + const result = await drupalValidator({ data: validData }); + expect(result).toEqual(expect.objectContaining({ success: false, error: expect.stringContaining('Cannot connect') })); + }); + + it('should handle ER_ACCESS_DENIED_ERROR', async () => { + mockCreateConnection.mockRejectedValue({ code: 'ER_ACCESS_DENIED_ERROR', message: 'Denied' }); + const result = await drupalValidator({ data: validData }); + expect(result).toEqual(expect.objectContaining({ success: false, error: expect.stringContaining('Access denied') })); + }); + + it('should handle ER_BAD_DB_ERROR', async () => { + mockCreateConnection.mockRejectedValue({ code: 'ER_BAD_DB_ERROR', message: 'Bad DB' }); + const result = await drupalValidator({ data: validData }); + expect(result).toEqual(expect.objectContaining({ success: false, error: expect.stringContaining('does not exist') })); + }); + + it('should handle ETIMEDOUT error', async () => { + mockCreateConnection.mockRejectedValue({ code: 'ETIMEDOUT', message: 'Timeout' }); + const result = await drupalValidator({ data: validData }); + expect(result).toEqual(expect.objectContaining({ success: false, error: expect.stringContaining('Cannot reach') })); + }); + + it('should handle ENOTFOUND error', async () => { + mockCreateConnection.mockRejectedValue({ code: 'ENOTFOUND', message: 'Not found' }); + const result = await drupalValidator({ data: validData }); + expect(result).toEqual(expect.objectContaining({ success: false, error: expect.stringContaining('Cannot reach') })); + }); + + it('should handle generic connection error', async () => { + mockCreateConnection.mockRejectedValue({ code: 'UNKNOWN', message: 'Unknown error' }); + const result = await drupalValidator({ data: validData }); + expect(result).toEqual(expect.objectContaining({ success: false, error: expect.stringContaining('Unknown error') })); + }); + + it('should use default port 3306 when port is NaN', async () => { + mockExecute.mockResolvedValueOnce([{ count: 10 }]).mockResolvedValueOnce([[{ name: 'f' }]]); + await drupalValidator({ data: { ...validData, port: 'invalid' as any } }); + expect(mockCreateConnection).toHaveBeenCalledWith(expect.objectContaining({ port: 3306 })); + }); + + it('should close connection in finally block', async () => { + mockExecute.mockResolvedValueOnce([{ count: 10 }]).mockResolvedValueOnce([[{ name: 'f' }]]); + await drupalValidator({ data: validData }); + expect(mockEnd).toHaveBeenCalled(); + }); + + it('should handle connection close error gracefully', async () => { + mockExecute.mockResolvedValueOnce([{ count: 10 }]).mockResolvedValueOnce([[{ name: 'f' }]]); + mockEnd.mockRejectedValueOnce(new Error('close error')); + const result = await drupalValidator({ data: validData }); + expect(result).toEqual({ success: true }); + }); + + describe('asset validation', () => { + it('should skip validation when assetsConfig has no base_url', async () => { + mockExecute.mockResolvedValueOnce([{ count: 10 }]).mockResolvedValueOnce([[{ name: 'f' }]]); + const result = await drupalValidator({ + data: validData, + assetsConfig: { base_url: '', public_path: '/files' }, + }); + expect(result).toEqual({ success: true }); + expect(mockAxiosHead).not.toHaveBeenCalled(); + }); + + it('should validate assets when base_url is provided', async () => { + mockExecute + .mockResolvedValueOnce([{ count: 10 }]) + .mockResolvedValueOnce([[{ name: 'field.field.node.article' }]]) + .mockResolvedValueOnce([[ + { fid: 1, filename: 'test.jpg', uri: 'public://images/test.jpg', filesize: 1024, filemime: 'image/jpeg' }, + ]]); + + mockAxiosHead.mockResolvedValue({ + status: 200, + headers: { 'content-type': 'image/jpeg' }, + }); + + const result = await drupalValidator({ + data: validData, + assetsConfig: { base_url: 'https://example.com', public_path: '/sites/default/files/' }, + }); + + expect(result).toEqual({ success: true }); + expect(mockAxiosHead).toHaveBeenCalled(); + }); + + it('should fail when assets return HTML content type', async () => { + mockExecute + .mockResolvedValueOnce([{ count: 10 }]) + .mockResolvedValueOnce([[{ name: 'f' }]]) + .mockResolvedValueOnce([[ + { fid: 1, filename: 'test.jpg', uri: 'public://images/test.jpg', filesize: 1024, filemime: 'image/jpeg' }, + ]]); + + mockAxiosHead.mockResolvedValue({ + status: 200, + headers: { 'content-type': 'text/html; charset=utf-8' }, + }); + + const result = await drupalValidator({ + data: validData, + assetsConfig: { base_url: 'https://example.com', public_path: '/files/' }, + }); + + expect(result).toEqual(expect.objectContaining({ success: false, error: expect.stringContaining('Assets validation failed') })); + }); + + it('should fail when all asset requests fail', async () => { + mockExecute + .mockResolvedValueOnce([{ count: 10 }]) + .mockResolvedValueOnce([[{ name: 'f' }]]) + .mockResolvedValueOnce([[ + { fid: 1, filename: 'test.jpg', uri: 'public://img.jpg', filesize: 100, filemime: 'image/jpeg' }, + ]]); + + mockAxiosHead.mockRejectedValue({ response: { status: 404 }, message: 'Not found' }); + + const result = await drupalValidator({ + data: validData, + assetsConfig: { base_url: 'https://example.com', public_path: '/files/' }, + }); + + expect(result).toEqual(expect.objectContaining({ success: false })); + }); + + it('should handle no assets in database', async () => { + mockExecute + .mockResolvedValueOnce([{ count: 10 }]) + .mockResolvedValueOnce([[{ name: 'f' }]]) + .mockResolvedValueOnce([[]]); + + const result = await drupalValidator({ + data: validData, + assetsConfig: { base_url: 'https://example.com', public_path: '/files/' }, + }); + + expect(result).toEqual({ success: true }); + }); + + it('should construct URL for public:// scheme', async () => { + mockExecute + .mockResolvedValueOnce([{ count: 10 }]) + .mockResolvedValueOnce([[{ name: 'f' }]]) + .mockResolvedValueOnce([[ + { fid: 1, filename: 'test.pdf', uri: 'public://docs/test.pdf', filesize: 100, filemime: 'application/pdf' }, + ]]); + + mockAxiosHead.mockResolvedValue({ + status: 200, + headers: { 'content-type': 'application/pdf' }, + }); + + const result = await drupalValidator({ + data: validData, + assetsConfig: { base_url: 'https://example.com', public_path: '/sites/default/files/' }, + }); + + expect(result).toEqual({ success: true }); + expect(mockAxiosHead).toHaveBeenCalledWith( + expect.stringContaining('https://example.com/sites/default/files/docs/test.pdf'), + expect.any(Object) + ); + }); + + it('should normalize base_url by adding protocol when missing', async () => { + mockExecute + .mockResolvedValueOnce([{ count: 10 }]) + .mockResolvedValueOnce([[{ name: 'f' }]]) + .mockResolvedValueOnce([[ + { fid: 1, filename: 'a.jpg', uri: 'public://a.jpg', filesize: 100, filemime: 'image/jpeg' }, + ]]); + + mockAxiosHead.mockResolvedValue({ + status: 200, + headers: { 'content-type': 'image/jpeg' }, + }); + + await drupalValidator({ + data: validData, + assetsConfig: { base_url: 'example.com', public_path: '/files/' }, + }); + + expect(mockAxiosHead).toHaveBeenCalledWith( + expect.stringContaining('https://example.com'), + expect.any(Object) + ); + }); + + it('should handle asset validation error', async () => { + mockExecute + .mockResolvedValueOnce([{ count: 10 }]) + .mockResolvedValueOnce([[{ name: 'f' }]]) + .mockRejectedValueOnce(new Error('query fail')); + + const result = await drupalValidator({ + data: validData, + assetsConfig: { base_url: 'https://example.com', public_path: '/files/' }, + }); + + expect(result).toEqual(expect.objectContaining({ success: false })); + }); + + it('should validate various content types as valid assets', async () => { + const validTypes = ['image/png', 'application/pdf', 'video/mp4', 'audio/mpeg', 'text/plain', 'text/csv', 'application/zip', 'application/octet-stream', 'application/msword', 'application/vnd.openxmlformats']; + + for (const ct of validTypes) { + vi.clearAllMocks(); + mockCreateConnection.mockResolvedValue({ execute: mockExecute, end: mockEnd }); + mockEnd.mockResolvedValue(undefined); + + mockExecute + .mockResolvedValueOnce([{ count: 10 }]) + .mockResolvedValueOnce([[{ name: 'f' }]]) + .mockResolvedValueOnce([[{ fid: 1, filename: 'f', uri: 'public://f', filesize: 1, filemime: ct }]]); + + mockAxiosHead.mockResolvedValue({ status: 200, headers: { 'content-type': ct } }); + + const result = await drupalValidator({ + data: validData, + assetsConfig: { base_url: 'https://x.com', public_path: '/f/' }, + }); + + expect(result).toEqual({ success: true }); + } + }); + + it('should skip asset validation when assetsConfig has no values', async () => { + mockExecute.mockResolvedValueOnce([{ count: 10 }]).mockResolvedValueOnce([[{ name: 'f' }]]); + const result = await drupalValidator({ data: validData, assetsConfig: {} }); + expect(result).toEqual({ success: true }); + }); + }); +}); diff --git a/upload-api/tests/unit/validators/index.test.ts b/upload-api/tests/unit/validators/index.test.ts new file mode 100644 index 000000000..9639c2742 --- /dev/null +++ b/upload-api/tests/unit/validators/index.test.ts @@ -0,0 +1,70 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockSitecoreValidator, mockContentfulValidator, mockWordpressValidator, mockAemValidator, mockDrupalValidator } = vi.hoisted(() => ({ + mockSitecoreValidator: vi.fn(), + mockContentfulValidator: vi.fn(), + mockWordpressValidator: vi.fn(), + mockAemValidator: vi.fn(), + mockDrupalValidator: vi.fn(), +})); + +vi.mock('migration-aem', () => ({ validator: vi.fn() })); +vi.mock('../../../src/validators/sitecore', () => ({ default: mockSitecoreValidator })); +vi.mock('../../../src/validators/contentful', () => ({ default: mockContentfulValidator })); +vi.mock('../../../src/validators/wordpress', () => ({ default: mockWordpressValidator })); +vi.mock('../../../src/validators/aem', () => ({ default: mockAemValidator })); +vi.mock('../../../src/validators/drupal', () => ({ default: mockDrupalValidator })); + +import validator from '../../../src/validators/index'; + +describe('validators/index', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should dispatch to sitecore validator for sitecore-zip', () => { + mockSitecoreValidator.mockReturnValue(true); + const result = validator({ data: 'zipData', type: 'sitecore', extension: 'zip' }); + expect(mockSitecoreValidator).toHaveBeenCalledWith({ data: 'zipData' }); + expect(result).toBe(true); + }); + + it('should dispatch to contentful validator for contentful-json', () => { + mockContentfulValidator.mockReturnValue(true); + const result = validator({ data: '{"contentTypes":[]}', type: 'contentful', extension: 'json' }); + expect(mockContentfulValidator).toHaveBeenCalledWith('{"contentTypes":[]}'); + expect(result).toBe(true); + }); + + it('should dispatch to wordpress validator for wordpress-xml', () => { + mockWordpressValidator.mockReturnValue(true); + const result = validator({ data: '', type: 'wordpress', extension: 'xml' }); + expect(mockWordpressValidator).toHaveBeenCalledWith(''); + expect(result).toBe(true); + }); + + it('should dispatch to aem validator for aem-folder', () => { + mockAemValidator.mockReturnValue(true); + const result = validator({ data: '/path', type: 'aem', extension: 'folder' }); + expect(mockAemValidator).toHaveBeenCalledWith({ data: '/path' }); + expect(result).toBe(true); + }); + + it('should dispatch to drupal validator for drupal-sql', () => { + const assetsConfig = { base_url: 'http://test.com', public_path: '/files' }; + mockDrupalValidator.mockReturnValue({ success: true }); + const result = validator({ data: {}, type: 'drupal', extension: 'sql', assetsConfig }); + expect(mockDrupalValidator).toHaveBeenCalledWith({ data: {}, assetsConfig }); + expect(result).toEqual({ success: true }); + }); + + it('should return false for unknown CMS type', () => { + const result = validator({ data: 'data', type: 'unknown', extension: 'txt' }); + expect(result).toBe(false); + }); + + it('should return false for empty type and extension', () => { + const result = validator({ data: 'data', type: '', extension: '' }); + expect(result).toBe(false); + }); +}); diff --git a/upload-api/tests/unit/validators/sitecore.validator.test.ts b/upload-api/tests/unit/validators/sitecore.validator.test.ts new file mode 100644 index 000000000..47a1985a8 --- /dev/null +++ b/upload-api/tests/unit/validators/sitecore.validator.test.ts @@ -0,0 +1,115 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const { mockJSZip } = vi.hoisted(() => ({ + mockJSZip: vi.fn().mockImplementation(function (this: any) { + this.loadAsync = vi.fn().mockResolvedValue(this); + this.files = {}; + }), +})); + +vi.mock('jszip', () => ({ + default: mockJSZip, +})); + +import sitecoreValidator from '../../../src/validators/sitecore/index'; + +describe('sitecoreValidator', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return true when items and metadata folders are present', async () => { + const data = { + files: { + 'items/master/sitecore/content': {}, + 'metadata/config.yml': {}, + }, + }; + const result = await sitecoreValidator({ data }); + expect(result).toBe(true); + }); + + it('should return true when items and metadata are nested under path', async () => { + const data = { + files: { + 'project/items/master/content': {}, + 'project/metadata/config.yml': {}, + }, + }; + const result = await sitecoreValidator({ data }); + expect(result).toBe(true); + }); + + it('should return false when required folders are missing', async () => { + const data = { + files: { + 'installer/setup.exe': {}, + 'properties/config.txt': {}, + }, + }; + const result = await sitecoreValidator({ data }); + expect(result).toBe(false); + }); + + it('should return false for empty zip', async () => { + const data = { files: {} }; + const result = await sitecoreValidator({ data }); + expect(result).toBe(false); + }); + + it('should return false on error', async () => { + const result = await sitecoreValidator({ data: null as any }); + expect(result).toBe(false); + }); + + it('should handle mixed content with items and metadata among other files', async () => { + const data = { + files: { + 'project/items/master/content.yml': {}, + 'project/metadata/config.yml': {}, + 'project/installer/setup.exe': {}, + 'project/properties/config.txt': {}, + 'readme.txt': {}, + }, + }; + const result = await sitecoreValidator({ data }); + expect(result).toBe(true); + }); + + it('should return false when nested zip does not contain Sitecore folders', async () => { + const nestedFiles = { + 'random/file.txt': { dir: false }, + }; + + mockJSZip.mockImplementation(function (this: any) { + this.loadAsync = vi.fn().mockResolvedValue(this); + this.files = nestedFiles; + }); + + const data = { + files: { + 'archive.zip': { + dir: false, + async: vi.fn().mockResolvedValue(Buffer.from('fake-zip')), + }, + }, + }; + + const result = await sitecoreValidator({ data }); + expect(result).toBe(false); + }); + + it('should handle error when processing nested zip fails', async () => { + const data = { + files: { + 'bad.zip': { + dir: false, + async: vi.fn().mockRejectedValue(new Error('corrupt zip')), + }, + }, + }; + + const result = await sitecoreValidator({ data }); + expect(result).toBe(false); + }); +}); diff --git a/upload-api/tests/unit/validators/wordpress.validator.test.ts b/upload-api/tests/unit/validators/wordpress.validator.test.ts new file mode 100644 index 000000000..9cef16fdb --- /dev/null +++ b/upload-api/tests/unit/validators/wordpress.validator.test.ts @@ -0,0 +1,40 @@ +import { describe, it, expect, vi } from 'vitest'; + +vi.mock('../../../src/models/wordpress.json', () => ({ + default: { + item: { name: 'item', required: 'true' }, + author: { name: 'wp\\:author', required: 'false' }, + category: { name: 'wp\\:category', required: 'false' }, + }, +})); + +import wordpressValidator from '../../../src/validators/wordpress/index'; + +describe('wordpressValidator', () => { + it('should return true for valid WordPress XML with required tags', () => { + const xml = 'Test'; + expect(wordpressValidator(xml)).toBe(true); + }); + + it('should return false when required tag is missing', () => { + const xml = 'Test'; + expect(wordpressValidator(xml)).toBe(false); + }); + + it('should return true when optional tags are missing but required present', () => { + const xml = 'Content'; + expect(wordpressValidator(xml)).toBe(true); + }); + + it('should return false for empty XML', () => { + expect(wordpressValidator('')).toBe(false); + }); + + it('should return false for invalid XML that causes error', () => { + expect(wordpressValidator(null as any)).toBe(false); + }); + + it('should return false for non-XML string', () => { + expect(wordpressValidator('not xml at all')).toBe(false); + }); +}); diff --git a/upload-api/vitest.config.ts b/upload-api/vitest.config.ts new file mode 100644 index 000000000..73a275aa7 --- /dev/null +++ b/upload-api/vitest.config.ts @@ -0,0 +1,40 @@ +import { defineConfig } from 'vitest/config'; +import path from 'path'; + +export default defineConfig({ + resolve: { + alias: { + 'migration-aem': path.resolve(__dirname, 'tests/__mocks__/migration-aem.ts'), + 'migration-sitecore': path.resolve(__dirname, 'tests/__mocks__/migration-sitecore.ts'), + 'migration-wordpress': path.resolve(__dirname, 'tests/__mocks__/migration-wordpress.ts'), + 'migration-contentful': path.resolve(__dirname, 'tests/__mocks__/migration-contentful.ts'), + 'migration-drupal': path.resolve(__dirname, 'tests/__mocks__/migration-drupal.ts'), + }, + }, + test: { + globals: true, + environment: 'node', + setupFiles: ['./tests/setup.ts'], + coverage: { + provider: 'v8', + reporter: ['text', 'lcov', 'html'], + include: ['src/**/*.ts'], + exclude: [ + '**/node_modules/**', + '**/tests/**', + 'src/index.ts', + 'src/main.ts', + 'src/config/index.ts', + 'src/utils/logger.ts', + 'src/models/types.ts', + 'src/generate-schema.d.ts', + ], + thresholds: { + lines: 80, + functions: 80, + branches: 60, + statements: 80, + }, + }, + }, +});