diff --git a/.env.development b/.env.development index eb9dd3935..f606b7c1b 100644 --- a/.env.development +++ b/.env.development @@ -1,2 +1,2 @@ VITE_PIZZA_SERVICE_URL=http://localhost:3000 -VITE_PIZZA_FACTORY_URL=https://pizza-factory.cs329.click \ No newline at end of file +VITE_PIZZA_FACTORY_URL=https://pizza-factory.cs329.click diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..2b0ecd23a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,76 @@ +name: CI Pipeline + +on: + push: + branches: + - main + - user-interface-testing + workflow_dispatch: +jobs: + build: + name: Build + runs-on: ubuntu-latest + outputs: + version: ${{ steps.set_version.outputs.version }} + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '22.x' + + - name: set version + id: set_version + run: | + version=$(date +'%Y%m%d.%H%M%S') + echo "version=$version" >> "$GITHUB_OUTPUT" + printf '{"version": "%s" }' "$version" > public/version.json + + - name: Build + run: | + npm ci && npm run build + + - name: Run tests + run: | + npx playwright install --with-deps chromium + npm run test:coverage + + - name: Update coverage + run: | + coverage=$(jq '.total.lines.pct' coverage/coverage-summary.json) + color=$(echo "$coverage < 80" | bc | awk '{if ($1) print "red"; else print "green"}') + curl -s -X POST "https://pizza-factory.cs329.click/api/badge/${{ secrets.NET_ID }}/jwtpizzacoverage?label=Coverage&value=$coverage%25&color=$color" -H "authorization: bearer ${{ secrets.FACTORY_API_KEY }}" + + - name: Update dist artifact + uses: actions/upload-artifact@v4 + with: + name: package + path: dist/ + deploy: + needs: build + permissions: + id-token: write + runs-on: ubuntu-latest + env: + version: ${{needs.build.outputs.version}} + steps: + - name: Create OIDC token to AWS + uses: aws-actions/configure-aws-credentials@v4 + with: + audience: sts.amazonaws.com + aws-region: us-east-1 + role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT }}:role/${{ secrets.CI_IAM_ROLE }} + + - name: Download dist artifact + uses: actions/download-artifact@v4 + with: + name: package + path: dist/ + + - name: Push to AWS S3 + run: | + echo Deploying $version + aws s3 cp dist s3://${{ secrets.APP_BUCKET }} --recursive + aws cloudfront create-invalidation --distribution-id ${{ secrets.DISTRIBUTION_ID }} --paths "/*" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..6d25cf469 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,37 @@ +name: tests + +on: + pull_request: + branches: + - main + workflow_dispatch: +jobs: + build: + name: Build + runs-on: ubuntu-latest + outputs: + version: ${{ steps.set_version.outputs.version }} + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '22.x' + + - name: set version + id: set_version + run: | + version=$(date +'%Y%m%d.%H%M%S') + echo "version=$version" >> "$GITHUB_OUTPUT" + printf '{"version": "%s" }' "$version" > public/version.json + + - name: Build + run: | + npm ci && npm run build + + - name: Run tests + run: | + npx playwright install --with-deps chromium + npm run test:coverage diff --git a/.gitignore b/.gitignore index 385d21505..cd2570cfb 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,13 @@ config.js coverage dist **/.vite +.idea + +# Playwright +node_modules/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +/playwright/.auth/ +.nyc_output \ No newline at end of file diff --git a/.nycrc.json b/.nycrc.json new file mode 100644 index 000000000..8712d48a7 --- /dev/null +++ b/.nycrc.json @@ -0,0 +1,7 @@ +{ + "check-coverage": true, + "branches": 0, + "lines": 80, + "functions": 0, + "statements": 0 +} \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..f6c4ec803 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": true, + "printWidth": 100, + "tabWidth": 4, + "trailingComma": "all" +} \ No newline at end of file diff --git a/README.md b/README.md index 560298b7a..90bff1aa9 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,14 @@ # 🍕 JWT Pizza -![Coverage badge](https://pizza-factory.cs329.click/api/badge/accountId/jwtpizzacoverage?t=1) +![Coverage badge](https://pizza-factory.cs329.click/api/badge/cjmotyer/jwtpizzacoverage) +[![CI Pipeline](https://github.com/cjmot/jwt-pizza/actions/workflows/ci.yml/badge.svg)](https://github.com/cjmot/jwt-pizza/actions/workflows/ci.yml) -A JSON Web Token, or [JWT](https://jwt.io/introduction), (pronounced JOT) is a digitally signed transfer of information using JSON notation. Because you can validate the digital signature you can buy JWT pizzas with confidence. +A JSON Web Token, or [JWT](https://jwt.io/introduction), (pronounced JOT) is a digitally signed transfer of information +using JSON notation. Because you can validate the digital signature you can buy JWT pizzas with confidence. -`JWT Pizza` takes the next stage of digital evolution by allowing you to buy pizzas that you can never actually eat. Not only does JWT exchange bitcoin and give you nothing in return, it also allows for you to be come a franchisee and turn the whole vapor company into an MLM. +`JWT Pizza` takes the next stage of digital evolution by allowing you to buy pizzas that you can never actually eat. Not +only does JWT exchange bitcoin and give you nothing in return, it also allows for you to be come a franchisee and turn +the whole vapor company into an MLM. You can see a working example of the application at [pizza.cs329.click](https://pizza.cs329.click) @@ -30,7 +34,8 @@ You can see a working example of the application at [pizza.cs329.click](https:// ## Development notes -JWT Pizza uses Vite, React, Tailwind, and Preline. The following contains some notes about how these components were integrated into the project. +JWT Pizza uses Vite, React, Tailwind, and Preline. The following contains some notes about how these components were +integrated into the project. ### Vite @@ -45,15 +50,16 @@ Modify `package.json` ```json "scripts": { - "dev": "vite", - "build": "vite build", - "preview": "vite preview" - }, +"dev": "vite", +"build": "vite build", +"preview": "vite preview" +}, ``` ### React -React works out of the box with Vite, but we do need to install the desired React packages. The `index.html` file loads `index.jsx` which then loads the app component (`src/app.jsx`). +React works out of the box with Vite, but we do need to install the desired React packages. The `index.html` file loads +`index.jsx` which then loads the app component (`src/app.jsx`). ```sh npm install react react-dom react-router-dom @@ -73,11 +79,11 @@ Modify `tailwind.config.js` ```js /** @type {import('tailwindcss').Config} */ module.exports = { - content: ['index.html', './src/**/*.{html,js,jsx}'], - theme: { - extend: {}, - }, - plugins: [], + content: ['index.html', './src/**/*.{html,js,jsx}'], + theme: { + extend: {}, + }, + plugins: [], }; ``` @@ -92,9 +98,10 @@ Create a `main.css` and add the basic Tailwind directives. Modify `index.html` to include tailwind output.css. ```html + - ... - + ... + ``` @@ -102,7 +109,8 @@ Now when you run with `npm run dev` the css will automatically be generated. ### Preline -Added the Tailwind [Preline component library](https://preline.co/) so that I can use all of their nifty nav, slideshow, containers, and cards. +Added the Tailwind [Preline component library](https://preline.co/) so that I can use all of their nifty nav, slideshow, +containers, and cards. ```sh npm i preline @@ -114,15 +122,15 @@ Updated the tailwind config to use preline. const defaultTheme = require('tailwindcss/defaultTheme'); module.exports = { - content: ['index.html', './src/**/*.{html,js,jsx}', './node_modules/preline/preline.js'], - theme: { - extend: { - fontFamily: { - sans: ['Inter var', ...defaultTheme.fontFamily.sans], - }, + content: ['index.html', './src/**/*.{html,js,jsx}', './node_modules/preline/preline.js'], + theme: { + extend: { + fontFamily: { + sans: ['Inter var', ...defaultTheme.fontFamily.sans], + }, + }, }, - }, - plugins: [require('preline/plugin')], + plugins: [require('preline/plugin')], }; ``` @@ -135,15 +143,15 @@ import 'preline/preline'; Initialize components whenever the page location changes. ```js -import { useLocation, Routes, Route, NavLink } from 'react-router-dom'; +import {useLocation, Routes, Route, NavLink} from 'react-router-dom'; export default function App() { - const location = useLocation(); + const location = useLocation(); - useEffect(() => { - window.HSStaticMethods.autoInit(); - }, [location.pathname]); - //... + useEffect(() => { + window.HSStaticMethods.autoInit(); + }, [location.pathname]); +//... ``` ### Icons diff --git a/notes.md b/notes.md index df8a5a7bd..06b2e814c 100644 --- a/notes.md +++ b/notes.md @@ -4,23 +4,23 @@ As part of `Deliverable ⓵ Development deployment: JWT Pizza`, start up the application and debug through the code until you understand how it works. During the learning process fill out the following required pieces of information in order to demonstrate that you have successfully completed the deliverable. -| User activity | Frontend component | Backend endpoints | Database SQL | -| --------------------------------------------------- | ------------------ | ----------------- | ------------ | -| View home page | | | | -| Register new user
(t@jwt.com, pw: test) | | | | -| Login new user
(t@jwt.com, pw: test) | | | | -| Order pizza | | | | -| Verify pizza | | | | -| View profile page | | | | -| View franchise
(as diner) | | | | -| Logout | | | | -| View About page | | | | -| View History page | | | | -| Login as franchisee
(f@jwt.com, pw: franchisee) | | | | -| View franchise
(as franchisee) | | | | -| Create a store | | | | -| Close a store | | | | -| Login as admin
(a@jwt.com, pw: admin) | | | | -| View Admin page | | | | -| Create a franchise for t@jwt.com | | | | -| Close the franchise for t@jwt.com | | | | +| User activity | Frontend component | Backend endpoints | Database SQL | +|-----------------------------------------------------|------------------------|------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| View home page | home.tsx | none | none | +| Register new user
(t@jwt.com, pw: test) | register.tsx | [POST]/api/auth | ```INSERT INTO user (name, email, password) VALUES (?, ?, ?)```
```INSERT INTO userRole (userId, role, objectId) VALUES (?, ?, ?)``` | +| Login new user
(t@jwt.com, pw: test) | login.tsx | [PUT]/api/auth | ```SELECT * FROM user WHERE email=?```
```SELECT * FROM userRole WHERE userId=?```
```INSERT INTO auth (token, userId) VALUES (?, ?) ON DUPLICATE KEY UPDATE token=token``` | +| Order pizza | menu.tsx, payment.tsx | [GET]/api/order/menu
[GET]/api/franchise?page=\${page}&limit\${limit}&name=${nameFilter} | ```SELECT * FROM menu```
```SELECT id, name FROM franchise WHERE name LIKE ? LIMIT ${limit + 1} OFFSET ${offset}```
```SELECT id, name FROM store WHERE franchiseId=?```
```INSERT INTO dinerOrder (dinerId, franchiseId, storeId, date) VALUES (?, ?, ?, now())```
```INSERT INTO orderItem (orderId, menuId, description, price) VALUES (?, ?, ?, ?)``` | +| Verify pizza | delivery.tsx | [POST]api/order/verify | none | +| View profile page | dinerDashboard.tsx | [GET]/api/orders | ```SELECT id, franchiseId, storeId, date FROM dinerOrder WHERE dinerId=? LIMIT ${offset},${config.db.listPerPage}``` | +| View franchise
(as diner) | franchiseDashboard.tsx | [GET]/api/franchise/${user.id} | ```SELECT objectId FROM userRole WHERE role='franchisee' AND userId=?``` | +| Logout | logout.tsx | [DELETE]/api/auth | ```DELETE FROM auth WHERE token=?``` | +| View About page | about.tsx | none | none | +| View History page | history.tsx | none | none | +| Login as franchisee
(f@jwt.com, pw: franchisee) | login.tsx | [POST]/api/auth | ```SELECT * FROM user WHERE email=?```
```SELECT * FROM userRole WHERE userId=?```
```INSERT INTO auth (token, userId) VALUES (?, ?) ON DUPLICATE KEY UPDATE token=token``` | +| View franchise
(as franchisee) | franchiseDashboard.tsx | [GET]/api/franchise/${user.id} | ```SELECT u.id, u.name, u.email FROM userRole AS ur JOIN user AS u ON u.id=ur.userId WHERE ur.objectId=? AND ur.role='franchisee'```
```SELECT s.id, s.name, COALESCE(SUM(oi.price), 0) AS totalRevenue FROM dinerOrder AS do JOIN orderItem AS oi ON do.id=oi.orderId RIGHT JOIN store AS s ON s.id=do.storeId WHERE s.franchiseId=? GROUP BY s.id``` | +| Create a store | createStore.tsx | [POST]/api/franchise/1/store | ```INSERT INTO store (franchiseId, name) VALUES (?, ?)``` | +| Close a store | closeStore.tsx | [DELETE]/api/franchise/\${franchise.id}/store/${store.id} | ```DELETE FROM store WHERE franchiseId=? AND id=?``` | +| Login as admin
(a@jwt.com, pw: admin) | login.tsx | [PUT]/api/auth | ```SELECT * FROM user WHERE email=?```
```SELECT * FROM userRole WHERE userId=?```
```INSERT INTO auth (token, userId) VALUES (?, ?) ON DUPLICATE KEY UPDATE token=token``` | +| View Admin page | adminDashboard.tsx | [GET]/api/franchise?page=\${page}&limit=\${limit}&name=${nameFilter} | ```SELECT id, name FROM franchise WHERE name LIKE ? LIMIT ${limit + 1} OFFSET ${offset}```
```SELECT u.id, u.name, u.email FROM userRole AS ur JOIN user AS u ON u.id=ur.userId WHERE ur.objectId=? AND ur.role='franchisee'```
```SELECT s.id, s.name, COALESCE(SUM(oi.price), 0) AS totalRevenue FROM dinerOrder AS do JOIN orderItem AS oi ON do.id=oi.orderId RIGHT JOIN store AS s ON s.id=do.storeId WHERE s.franchiseId=? GROUP BY s.id``` | +| Create a franchise for t@jwt.com | createFranchise.tsx | [POST]/api/franchise/${user.id} | ```SELECT id, name FROM user WHERE email=?```
```INSERT INTO franchise (name) VALUES (?)```
```INSERT INTO userRole (userId, role, objectId) VALUES (?, ?, ?)``` | +| Close the franchise for t@jwt.com | closeFranchise.tsx | [DELETE]/api/franchise/${franchise.id} | ```DELETE FROM store WHERE franchiseId=?```
```DELETE FROM userRole WHERE objectId=?```
```DELETE FROM franchise WHERE id=?``` | diff --git a/package-lock.json b/package-lock.json index 586c558e7..a2acdf7f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,13 +15,19 @@ "react-router-dom": "^6.22.3" }, "devDependencies": { + "@playwright/test": "^1.58.1", + "@types/node": "^25.2.0", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "autoprefixer": "^10.4.19", + "nyc": "^17.1.0", + "playwright-test-coverage": "^1.2.12", "postcss": "^8.4.38", + "prettier": "^3.8.1", "tailwindcss": "^3.4.3", "typescript": "^5.5.4", - "vite": "^5.2.8" + "vite": "7.1.11", + "vite-plugin-istanbul": "^7.2.1" } }, "node_modules/@alloc/quick-lru": { @@ -29,6 +35,7 @@ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -36,372 +43,714 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@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" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@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.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", - "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", "cpu": [ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@isaacs/cliui": { @@ -409,6 +758,7 @@ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -421,49 +771,163 @@ "node": ">=12" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/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==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=6.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, "engines": { - "node": ">=6.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -474,6 +938,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" @@ -487,6 +952,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" } @@ -496,6 +962,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" @@ -509,273 +976,514 @@ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=14" } }, - "node_modules/@popperjs/core": { + "node_modules/@playwright/test": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz", + "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "playwright": "1.58.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" } }, "node_modules/@remix-run/router": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", - "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==", + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz", + "integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==", + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.1.tgz", - "integrity": "sha512-fH8/o8nSUek8ceQnT7K4EQbSiV7jgkHq81m9lWZFIXjJ7lJzpWXbQFpT/Zh6OZYnpFykvzC3fbEvEAFZu03dPA==", + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.58.0.tgz", + "integrity": "sha512-mr0tmS/4FoVk1cnaeN244A/wjvGDNItZKR8hRhnmCzygyRXYtKF5jVDSIILR1U97CTzAYmbgIj/Dukg62ggG5w==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.1.tgz", - "integrity": "sha512-Y/9OHLjzkunF+KGEoJr3heiD5X9OLa8sbT1lm0NYeKyaM3oMhhQFvPB0bNZYJwlq93j8Z6wSxh9+cyKQaxS7PQ==", + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.58.0.tgz", + "integrity": "sha512-+s++dbp+/RTte62mQD9wLSbiMTV+xr/PeRJEc/sFZFSBRlHPNPVaf5FXlzAL77Mr8FtSfQqCN+I598M8U41ccQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.1.tgz", - "integrity": "sha512-+kecg3FY84WadgcuSVm6llrABOdQAEbNdnpi5X3UwWiFVhZIZvKgGrF7kmLguvxHNQy+UuRV66cLVl3S+Rkt+Q==", + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.58.0.tgz", + "integrity": "sha512-MFWBwTcYs0jZbINQBXHfSrpSQJq3IUOakcKPzfeSznONop14Pxuqa0Kg19GD0rNBMPQI2tFtu3UzapZpH0Uc1Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.1.tgz", - "integrity": "sha512-2pYRzEjVqq2TB/UNv47BV/8vQiXkFGVmPFwJb+1E0IFFZbIX8/jo1olxqqMbo6xCXf8kabANhp5bzCij2tFLUA==", + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.58.0.tgz", + "integrity": "sha512-yiKJY7pj9c9JwzuKYLFaDZw5gma3fI9bkPEIyofvVfsPqjCWPglSHdpdwXpKGvDeYDms3Qal8qGMEHZ1M/4Udg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.58.0.tgz", + "integrity": "sha512-x97kCoBh5MOevpn/CNK9W1x8BEzO238541BGWBc315uOlN0AD/ifZ1msg+ZQB05Ux+VF6EcYqpiagfLJ8U3LvQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.58.0.tgz", + "integrity": "sha512-Aa8jPoZ6IQAG2eIrcXPpjRcMjROMFxCt1UYPZZtCxRV68WkuSigYtQ/7Zwrcr2IvtNJo7T2JfDXyMLxq5L4Jlg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.1.tgz", - "integrity": "sha512-mS6wQ6Do6/wmrF9aTFVpIJ3/IDXhg1EZcQFYHZLHqw6AzMBjTHWnCG35HxSqUNphh0EHqSM6wRTT8HsL1C0x5g==", + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.58.0.tgz", + "integrity": "sha512-Ob8YgT5kD/lSIYW2Rcngs5kNB/44Q2RzBSPz9brf2WEtcGR7/f/E9HeHn1wYaAwKBni+bdXEwgHvUd0x12lQSA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.58.0.tgz", + "integrity": "sha512-K+RI5oP1ceqoadvNt1FecL17Qtw/n9BgRSzxif3rTL2QlIu88ccvY+Y9nnHe/cmT5zbH9+bpiJuG1mGHRVwF4Q==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.1.tgz", - "integrity": "sha512-p9rGKYkHdFMzhckOTFubfxgyIO1vw//7IIjBBRVzyZebWlzRLeNhqxuSaZ7kCEKVkm/kuC9fVRW9HkC/zNRG2w==", + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.58.0.tgz", + "integrity": "sha512-T+17JAsCKUjmbopcKepJjHWHXSjeW7O5PL7lEFaeQmiVyw4kkc5/lyYKzrv6ElWRX/MrEWfPiJWqbTvfIvjM1Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.1.tgz", - "integrity": "sha512-nDY6Yz5xS/Y4M2i9JLQd3Rofh5OR8Bn8qe3Mv/qCVpHFlwtZSBYSPaU4mrGazWkXrdQ98GB//H0BirGR/SKFSw==", + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.58.0.tgz", + "integrity": "sha512-cCePktb9+6R9itIJdeCFF9txPU7pQeEHB5AbHu/MKsfH/k70ZtOeq1k4YAtBv9Z7mmKI5/wOLYjQ+B9QdxR6LA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.58.0.tgz", + "integrity": "sha512-iekUaLkfliAsDl4/xSdoCJ1gnnIXvoNz85C8U8+ZxknM5pBStfZjeXgB8lXobDQvvPRCN8FPmmuTtH+z95HTmg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.58.0.tgz", + "integrity": "sha512-68ofRgJNl/jYJbxFjCKE7IwhbfxOl1muPN4KbIqAIe32lm22KmU7E8OPvyy68HTNkI2iV/c8y2kSPSm2mW/Q9Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.58.0.tgz", + "integrity": "sha512-dpz8vT0i+JqUKuSNPCP5SYyIV2Lh0sNL1+FhM7eLC457d5B9/BC3kDPp5BBftMmTNsBarcPcoz5UGSsnCiw4XQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.1.tgz", - "integrity": "sha512-im7HE4VBL+aDswvcmfx88Mp1soqL9OBsdDBU8NqDEYtkri0qV0THhQsvZtZeNNlLeCUQ16PZyv7cqutjDF35qw==", + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.58.0.tgz", + "integrity": "sha512-4gdkkf9UJ7tafnweBCR/mk4jf3Jfl0cKX9Np80t5i78kjIH0ZdezUv/JDI2VtruE5lunfACqftJ8dIMGN4oHew==", "cpu": [ - "ppc64le" + "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.1.tgz", - "integrity": "sha512-RWdiHuAxWmzPJgaHJdpvUUlDz8sdQz4P2uv367T2JocdDa98iRw2UjIJ4QxSyt077mXZT2X6pKfT2iYtVEvOFw==", + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.58.0.tgz", + "integrity": "sha512-YFS4vPnOkDTD/JriUeeZurFYoJhPf9GQQEF/v4lltp3mVcBmnsAdjEWhr2cjUCZzZNzxCG0HZOvJU44UGHSdzw==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.58.0.tgz", + "integrity": "sha512-x2xgZlFne+QVNKV8b4wwaCS8pwq3y14zedZ5DqLzjdRITvreBk//4Knbcvm7+lWmms9V9qFp60MtUd0/t/PXPw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.1.tgz", - "integrity": "sha512-VMgaGQ5zRX6ZqV/fas65/sUGc9cPmsntq2FiGmayW9KMNfWVG/j0BAqImvU4KTeOOgYSf1F+k6at1UfNONuNjA==", + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.58.0.tgz", + "integrity": "sha512-jIhrujyn4UnWF8S+DHSkAkDEO3hLX0cjzxJZPLF80xFyzyUIYgSMRcYQ3+uqEoyDD2beGq7Dj7edi8OnJcS/hg==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.1.tgz", - "integrity": "sha512-9Q7DGjZN+hTdJomaQ3Iub4m6VPu1r94bmK2z3UeWP3dGUecRC54tmVu9vKHTm1bOt3ASoYtEz6JSRLFzrysKlA==", + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.58.0.tgz", + "integrity": "sha512-+410Srdoh78MKSJxTQ+hZ/Mx+ajd6RjjPwBPNd0R3J9FtL6ZA0GqiiyNjCO9In0IzZkCNrpGymSfn+kgyPQocg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.1.tgz", - "integrity": "sha512-JNEG/Ti55413SsreTguSx0LOVKX902OfXIKVg+TCXO6Gjans/k9O6ww9q3oLGjNDaTLxM+IHFMeXy/0RXL5R/g==", + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.58.0.tgz", + "integrity": "sha512-ZjMyby5SICi227y1MTR3VYBpFTdZs823Rs/hpakufleBoufoOIB6jtm9FEoxn/cgO7l6PM2rCEl5Kre5vX0QrQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.58.0.tgz", + "integrity": "sha512-ds4iwfYkSQ0k1nb8LTcyXw//ToHOnNTJtceySpL3fa7tc/AsE+UpUFphW126A6fKBGJD5dhRvg8zw1rvoGFxmw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.58.0.tgz", + "integrity": "sha512-fd/zpJniln4ICdPkjWFhZYeY/bpnaN9pGa6ko+5WD38I0tTqk9lXMgXZg09MNdhpARngmxiCg0B0XUamNw/5BQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.1.tgz", - "integrity": "sha512-ryS22I9y0mumlLNwDFYZRDFLwWh3aKaC72CWjFcFvxK0U6v/mOkM5Up1bTbCRAhv3kEIwW2ajROegCIQViUCeA==", + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.58.0.tgz", + "integrity": "sha512-YpG8dUOip7DCz3nr/JUfPbIUo+2d/dy++5bFzgi4ugOGBIox+qMbbqt/JoORwvI/C9Kn2tz6+Bieoqd5+B1CjA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.1.tgz", - "integrity": "sha512-TdloItiGk+T0mTxKx7Hp279xy30LspMso+GzQvV2maYePMAWdmrzqSNZhUpPj3CGw12aGj57I026PgLCTu8CGg==", + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.58.0.tgz", + "integrity": "sha512-b9DI8jpFQVh4hIXFr0/+N/TzLdpBIoPzjt0Rt4xJbW3mzguV3mduR9cNgiuFcuL/TeORejJhCWiAXe3E/6PxWA==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.58.0.tgz", + "integrity": "sha512-CSrVpmoRJFN06LL9xhkitkwUcTZtIotYAF5p6XOR2zW0Zz5mzb3IPpcoPhB02frzMHFNo1reQ9xSF5fFm3hUsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.1.tgz", - "integrity": "sha512-wQGI+LY/Py20zdUPq+XCem7JcPOyzIJBm3dli+56DJsQOHbnXZFEwgmnC6el1TPAfC8lBT3m+z69RmLykNUbew==", + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.58.0.tgz", + "integrity": "sha512-QFsBgQNTnh5K0t/sBsjJLq24YVqEIVkGpfN2VHsnN90soZyhaiA9UUHufcctVNL4ypJY0wrwad0wslx2KJQ1/w==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.0.tgz", + "integrity": "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~7.18.0" + } }, "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", - "dev": true + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, + "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.3", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", - "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", + "version": "18.3.28", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", + "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", "dev": true, + "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", - "csstype": "^3.0.2" + "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { - "version": "18.3.0", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", - "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/acorn": { + "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", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, + "license": "MIT", "dependencies": { - "@types/react": "*" + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": ">=8" } }, "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=12" + "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" @@ -785,13 +1493,15 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, + "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -800,16 +1510,47 @@ "node": ">= 8" } }, + "node_modules/append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-require-extensions": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true, + "license": "MIT" + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } }, "node_modules/autoprefixer": { - "version": "10.4.19", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", - "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", + "version": "10.4.24", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.24.tgz", + "integrity": "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw==", "dev": true, "funding": [ { @@ -825,12 +1566,12 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001599", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001766", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -847,13 +1588,28 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", + "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -862,30 +1618,33 @@ } }, "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "dev": true, "funding": [ { @@ -901,11 +1660,14 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", + "peer": true, "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" @@ -914,19 +1676,46 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/camelcase-css": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001609", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001609.tgz", - "integrity": "sha512-JFPQs34lHKx1B5t1EpQpWH4c+29zIyn/haGsbpfq3suuV9v56enjFt23zqijxGTMwy1p/4H2tjnQMY+p1WoAyA==", + "version": "1.0.30001770", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001770.tgz", + "integrity": "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==", "dev": true, "funding": [ { @@ -941,13 +1730,15 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, + "license": "MIT", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -972,6 +1763,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -979,11 +1771,34 @@ "node": ">= 6" } }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -995,22 +1810,46 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6" } }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/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==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1025,6 +1864,7 @@ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, + "license": "MIT", "bin": { "cssesc": "bin/cssesc" }, @@ -1033,99 +1873,207 @@ } }, "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-require-extensions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", + "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "strip-bom": "^4.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.4.735", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.735.tgz", - "integrity": "sha512-pkYpvwg8VyOTQAeBqZ7jsmpCjko1Qc6We1ZtZCjRyYbT5v4AIUKDy5cQTRotQlSSZmMr8jqpEt6JtOj5k7lR7A==", - "dev": true + "version": "1.5.302", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz", + "integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==", + "dev": true, + "license": "ISC" }, "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT" }, "node_modules/esbuild": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" } }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -1136,6 +2084,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -1144,19 +2093,21 @@ } }, "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "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" } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -1164,13 +2115,46 @@ "node": ">=8" } }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, + "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -1180,25 +2164,68 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/foreground-child/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==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", "dev": true, + "license": "MIT", "engines": { "node": "*" }, "funding": { - "type": "patreon", + "type": "github", "url": "https://github.com/sponsors/rawify" } }, + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "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, + "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==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1212,27 +2239,58 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/glob": { - "version": "10.3.12", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", - "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "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, + "license": "ISC", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", - "minimatch": "^9.0.1", - "minipass": "^7.0.4", - "path-scurry": "^1.10.2" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "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": ">=16 || 14 >=14.17" + "node": "*" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -1243,6 +2301,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -1250,11 +2309,46 @@ "node": ">=10.13.0" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -1262,11 +2356,58 @@ "node": ">= 0.4" } }, + "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/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "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" + } + }, + "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, + "license": "ISC", + "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", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, + "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" }, @@ -1275,12 +2416,16 @@ } }, "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, + "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1291,6 +2436,7 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -1300,6 +2446,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -1309,6 +2456,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -1321,26 +2469,174 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" }, - "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "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-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@isaacs/cliui": "^8.0.2" + "append-transform": "^2.0.0" }, "engines": { - "node": ">=14" + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-processinfo": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "dev": true, + "license": "ISC", + "dependencies": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^8.3.2" + }, + "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/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/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "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/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -1350,10 +2646,12 @@ } }, "node_modules/jiti": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", - "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "dev": true, + "license": "MIT", + "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -1361,27 +2659,94 @@ "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": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } }, "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, "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==", - "dev": true + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "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" }, @@ -1390,12 +2755,39 @@ } }, "node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, "engines": { - "node": "14 || >=16.14" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, "node_modules/merge2": { @@ -1403,17 +2795,19 @@ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -1421,34 +2815,41 @@ } }, "node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" + "brace-expansion": "^1.1.7" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": "*" } }, "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true, + "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", "dev": true, + "license": "MIT", "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", @@ -1456,9 +2857,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, "funding": [ { @@ -1466,6 +2867,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -1473,28 +2875,76 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "process-on-spawn": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "node_modules/nyc": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-17.1.0.tgz", + "integrity": "sha512-U42vQ4czpKa0QdI1hu950XuNhYqgoM+ZF1HT+VuUHL9hPfDPVvNQyltmMqdE9bUHMVa+8yNbc3QKTj8zQhlVxQ==", "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^3.3.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^6.0.2", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "bin": { + "nyc": "bin/nyc.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">=18" } }, "node_modules/object-assign": { @@ -1502,6 +2952,7 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -1511,15 +2962,122 @@ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-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, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -1528,35 +3086,46 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/path-scurry": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", - "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "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" }, @@ -1569,23 +3138,80 @@ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6" } }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/playwright": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz", + "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.58.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", + "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright-test-coverage": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/playwright-test-coverage/-/playwright-test-coverage-1.2.12.tgz", + "integrity": "sha512-WdR3shV+7IWtlB1AZcXSysUyZmyAqXM9+DT3iBSS1p4SkJoWZslkTqk04l5ZExSkuaM+5fKl0M9TyUuJWuGgLA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@playwright/test": "^1.14.1" + } + }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, "funding": [ { @@ -1601,10 +3227,12 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", + "peer": true, "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -1615,6 +3243,7 @@ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", @@ -1628,28 +3257,35 @@ } }, "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "dependencies": { "camelcase-css": "^2.0.1" }, "engines": { "node": "^12 || ^14 || >= 16" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, "peerDependencies": { "postcss": "^8.4.21" } }, "node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", "dev": true, "funding": [ { @@ -1661,62 +3297,66 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" + "lilconfig": "^3.1.1" }, "engines": { - "node": ">= 14" + "node": ">= 18" }, "peerDependencies": { + "jiti": ">=1.21.0", "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { + "jiti": { + "optional": true + }, "postcss": { "optional": true }, - "ts-node": { + "tsx": { + "optional": true + }, + "yaml": { "optional": true } } }, - "node_modules/postcss-load-config/node_modules/lilconfig": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", - "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, "node_modules/postcss-nested": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.0.11" + "postcss-selector-parser": "^6.1.1" }, "engines": { "node": ">=12.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, "peerDependencies": { "postcss": "^8.2.14" } }, "node_modules/postcss-selector-parser": { - "version": "6.0.16", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", - "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, + "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -1729,16 +3369,47 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/preline": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/preline/-/preline-2.1.0.tgz", - "integrity": "sha512-7pwrGNPVfGYUv1XHW9i49ejowIVHCDTx8JvbM43hapLi1GCjj9NnAZmTKupxaROboB4Nvp2x0ADBVKAhpmZppA==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/preline/-/preline-2.7.0.tgz", + "integrity": "sha512-xMuMVZ7aftBT1/5/3Rb48i/t+LWQfsUjBX7bls4peqS6h5OZTbIgz0N6eMDzuDlOZF3DiSWLehl00oGlAkvovw==", + "license": "Licensed under MIT and Preline UI Fair Use License", "dependencies": { "@popperjs/core": "^2.11.2" } }, + "node_modules/prettier": { + "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" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/process-on-spawn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.1.0.tgz", + "integrity": "sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "fromentries": "^1.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -1757,12 +3428,15 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "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", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -1771,23 +3445,26 @@ } }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "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", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.3.1" } }, "node_modules/react-router": { - "version": "6.22.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz", - "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==", + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz", + "integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==", + "license": "MIT", "dependencies": { - "@remix-run/router": "1.15.3" + "@remix-run/router": "1.23.2" }, "engines": { "node": ">=14.0.0" @@ -1797,12 +3474,13 @@ } }, "node_modules/react-router-dom": { - "version": "6.22.3", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz", - "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==", + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz", + "integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==", + "license": "MIT", "dependencies": { - "@remix-run/router": "1.15.3", - "react-router": "6.22.3" + "@remix-run/router": "1.23.2", + "react-router": "6.30.3" }, "engines": { "node": ">=14.0.0" @@ -1817,6 +3495,7 @@ "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", "dev": true, + "license": "MIT", "dependencies": { "pify": "^2.3.0" } @@ -1826,6 +3505,7 @@ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, + "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, @@ -1833,40 +3513,103 @@ "node": ">=8.10.0" } }, + "node_modules/release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", + "dev": true, + "license": "ISC", + "dependencies": { + "es6-error": "^4.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true, + "license": "ISC" + }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "dev": true, + "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "1.1.0", + "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" } }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "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" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/rollup": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.1.tgz", - "integrity": "sha512-4LnHSdd3QK2pa1J6dFbfm1HN0D7vSK/ZuZTsdyUAlA6Rr1yTouUTL13HaDOGJVgby461AhrNGBS7sCGXXtT+SA==", + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.58.0.tgz", + "integrity": "sha512-wbT0mBmWbIvvq8NeEYWWvevvxnOyhKChir47S66WCxw1SXqhw7ssIYejnQEVt7XYQpsj2y8F9PM+Cr3SNEa0gw==", "dev": true, + "license": "MIT", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -1876,21 +3619,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.14.1", - "@rollup/rollup-android-arm64": "4.14.1", - "@rollup/rollup-darwin-arm64": "4.14.1", - "@rollup/rollup-darwin-x64": "4.14.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.14.1", - "@rollup/rollup-linux-arm64-gnu": "4.14.1", - "@rollup/rollup-linux-arm64-musl": "4.14.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.14.1", - "@rollup/rollup-linux-riscv64-gnu": "4.14.1", - "@rollup/rollup-linux-s390x-gnu": "4.14.1", - "@rollup/rollup-linux-x64-gnu": "4.14.1", - "@rollup/rollup-linux-x64-musl": "4.14.1", - "@rollup/rollup-win32-arm64-msvc": "4.14.1", - "@rollup/rollup-win32-ia32-msvc": "4.14.1", - "@rollup/rollup-win32-x64-msvc": "4.14.1", + "@rollup/rollup-android-arm-eabi": "4.58.0", + "@rollup/rollup-android-arm64": "4.58.0", + "@rollup/rollup-darwin-arm64": "4.58.0", + "@rollup/rollup-darwin-x64": "4.58.0", + "@rollup/rollup-freebsd-arm64": "4.58.0", + "@rollup/rollup-freebsd-x64": "4.58.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.58.0", + "@rollup/rollup-linux-arm-musleabihf": "4.58.0", + "@rollup/rollup-linux-arm64-gnu": "4.58.0", + "@rollup/rollup-linux-arm64-musl": "4.58.0", + "@rollup/rollup-linux-loong64-gnu": "4.58.0", + "@rollup/rollup-linux-loong64-musl": "4.58.0", + "@rollup/rollup-linux-ppc64-gnu": "4.58.0", + "@rollup/rollup-linux-ppc64-musl": "4.58.0", + "@rollup/rollup-linux-riscv64-gnu": "4.58.0", + "@rollup/rollup-linux-riscv64-musl": "4.58.0", + "@rollup/rollup-linux-s390x-gnu": "4.58.0", + "@rollup/rollup-linux-x64-gnu": "4.58.0", + "@rollup/rollup-linux-x64-musl": "4.58.0", + "@rollup/rollup-openbsd-x64": "4.58.0", + "@rollup/rollup-openharmony-arm64": "4.58.0", + "@rollup/rollup-win32-arm64-msvc": "4.58.0", + "@rollup/rollup-win32-ia32-msvc": "4.58.0", + "@rollup/rollup-win32-x64-gnu": "4.58.0", + "@rollup/rollup-win32-x64-msvc": "4.58.0", "fsevents": "~2.3.2" } }, @@ -1913,23 +3666,46 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } }, "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "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" } }, + "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/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -1942,54 +3718,83 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "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==", "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=0.10.0" } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "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/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "node_modules/spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", "dev": true, + "license": "ISC", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/spawn-wrap/node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8.0.0" } }, - "node_modules/string-width-cjs": { - "name": "string-width", + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "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==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -1999,26 +3804,28 @@ "node": ">=8" } }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, "engines": { "node": ">=8" } }, - "node_modules/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==", - "dev": true - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { + "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -2026,27 +3833,13 @@ "node": ">=8" } }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -2054,27 +3847,29 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", - "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { @@ -2085,11 +3880,25 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -2098,33 +3907,34 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", - "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", "dev": true, + "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", - "chokidar": "^3.5.3", + "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", - "fast-glob": "^3.3.0", + "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.21.0", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", @@ -2134,11 +3944,27 @@ "node": ">=14.0.0" } }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", "dev": true, + "license": "MIT", "dependencies": { "any-promise": "^1.0.0" } @@ -2148,6 +3974,7 @@ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", "dev": true, + "license": "MIT", "dependencies": { "thenify": ">= 3.1.0 < 4" }, @@ -2155,11 +3982,61 @@ "node": ">=0.8" } }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -2171,13 +4048,35 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-typedarray": "^1.0.0" + } }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2186,10 +4085,17 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -2205,9 +4111,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -2220,23 +4127,38 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } }, "node_modules/vite": { - "version": "5.2.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz", - "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==", + "version": "7.1.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.11.tgz", + "integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==", "dev": true, + "license": "MIT", "dependencies": { - "esbuild": "^0.20.1", - "postcss": "^8.4.38", - "rollup": "^4.13.0" + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -2245,18 +4167,25 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", "lightningcss": "^1.21.0", - "sass": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.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 }, @@ -2266,6 +4195,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, @@ -2274,14 +4206,161 @@ }, "terser": { "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-plugin-istanbul": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/vite-plugin-istanbul/-/vite-plugin-istanbul-7.2.1.tgz", + "integrity": "sha512-DSPi4ulvYsjnP44sTI5oriNosbM0E6m3uoCxjdxboTtVzxSkFwcDy3/JnSYKebjr+ZToJwVLTms+2CM0rmbbzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/generator": "^7.28.0", + "@istanbuljs/load-nyc-config": "^1.1.0", + "@types/babel__generator": "7.6.8", + "espree": "^10.3.0", + "istanbul-lib-instrument": "^6.0.3", + "picocolors": "^1.1.1", + "source-map": "^0.7.4", + "test-exclude": "^7.0.1" + }, + "peerDependencies": { + "vite": ">=4 <=7" + } + }, + "node_modules/vite-plugin-istanbul/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/vite-plugin-istanbul/node_modules/glob": { + "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", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/vite-plugin-istanbul/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/vite-plugin-istanbul/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/vite-plugin-istanbul/node_modules/test-exclude": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", + "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^9.0.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true } } }, + "node_modules/vite/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -2292,21 +4371,26 @@ "node": ">= 8" } }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true, + "license": "ISC" + }, "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=8" } }, "node_modules/wrap-ansi-cjs": { @@ -2315,6 +4399,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -2327,72 +4412,75 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true, - "engines": { - "node": ">=8" - } + "license": "ISC" }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, + "license": "ISC", "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } }, - "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==", - "dev": true + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true, + "license": "ISC" }, - "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==", + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } + "license": "ISC" }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" }, "engines": { "node": ">=8" } }, - "node_modules/yaml": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", - "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, - "bin": { - "yaml": "bin.mjs" + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" }, "engines": { - "node": ">= 14" + "node": ">=6" } } } diff --git a/package.json b/package.json index a748d7d40..06d3f7f45 100644 --- a/package.json +++ b/package.json @@ -8,16 +8,24 @@ "scripts": { "dev": "vite", "build": "vite build", - "preview": "vite preview" + "preview": "vite preview", + "test": "playwright test", + "test:coverage": "nyc --reporter=json-summary --reporter=text playwright test" }, "devDependencies": { + "@playwright/test": "^1.58.1", + "@types/node": "^25.2.0", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "autoprefixer": "^10.4.19", + "nyc": "^17.1.0", + "playwright-test-coverage": "^1.2.12", "postcss": "^8.4.38", + "prettier": "^3.8.1", "tailwindcss": "^3.4.3", "typescript": "^5.5.4", - "vite": "^5.2.8" + "vite": "7.1.11", + "vite-plugin-istanbul": "^7.2.1" }, "dependencies": { "preline": "^2.1.0", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 000000000..c4f12f8aa --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,31 @@ +import { defineConfig, devices } from '@playwright/test'; + +export default defineConfig({ + testDir: './tests', + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: 'html', + timeout: 5000, + use: { + baseURL: 'http://localhost:5173', + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'], viewport: { width: 800, height: 600 } }, + }, + ], + + /* Run your local dev server before starting the tests */ + webServer: { + command: 'npm run dev', + url: 'http://localhost:5173', + reuseExistingServer: !process.env.CI, + timeout: 5000, + }, +}); diff --git a/src/app/app.tsx b/src/app/app.tsx index 083353ae3..1a61442df 100644 --- a/src/app/app.tsx +++ b/src/app/app.tsx @@ -18,6 +18,7 @@ import CreateStore from '../views/createStore'; import CreateFranchise from '../views/createFranchise'; import CloseFranchise from '../views/closeFranchise'; import CloseStore from '../views/closeStore'; +import DeleteUser from '../views/deleteUser'; import Payment from '../views/payment'; import NotFound from '../views/notFound'; import Docs from '../views/docs'; @@ -27,81 +28,136 @@ import { Role, User } from '../service/pizzaService'; import 'preline/preline'; declare global { - interface Window { - HSStaticMethods: any; - } + interface Window { + HSStaticMethods: any; + } } export default function App() { - const [user, setUser] = React.useState(null); - const location = useLocation(); + const [user, setUser] = React.useState(null); + const location = useLocation(); - useEffect(() => { - (async () => { - const user = await pizzaService.getUser(); - setUser(user); - })(); - }, []); + useEffect(() => { + (async () => { + const user = await pizzaService.getUser(); + setUser(user); + })(); + }, []); - useEffect(() => { - window.HSStaticMethods.autoInit(); - window.scrollTo(0, 0); - }, [location.pathname]); + useEffect(() => { + window.HSStaticMethods.autoInit(); + window.scrollTo(0, 0); + }, [location.pathname]); - function loggedIn() { - return !!user; - } - function loggedOut() { - return !loggedIn(); - } - function isAdmin() { - return Role.isRole(user, Role.Admin); - } - function isNotAdmin() { - return !isAdmin(); - } + function loggedIn() { + return !!user; + } + function loggedOut() { + return !loggedIn(); + } + function isAdmin() { + return Role.isRole(user, Role.Admin); + } + function isNotAdmin() { + return !isAdmin(); + } - const navItems = [ - { title: 'Home', to: '/', component: , display: [] }, - { title: 'Diner', to: '/diner-dashboard', component: , display: [] }, - { title: 'Order', to: '/menu', component: , display: ['nav'] }, - { - title: 'Franchise', - to: '/franchise-dashboard', - component: , - constraints: [isNotAdmin], - display: ['nav', 'footer'], - }, - { title: 'About', to: '/about', component: , display: ['footer'] }, - { title: 'History', to: '/history', component: , display: ['footer'] }, - { title: 'Admin', to: '/admin-dashboard', component: , constraints: [isAdmin], display: ['nav'] }, - { title: 'Create franchise', to: '/:subPath?/create-franchise', component: , display: [] }, - { title: 'Close franchise', to: '/:subPath?/close-franchise', component: , display: [] }, - { title: 'Create store', to: '/:subPath?/create-store', component: , display: [] }, - { title: 'Close store', to: '/:subPath?/close-store', component: , display: [] }, - { title: 'Payment', to: '/payment', component: , display: [] }, - { title: 'Delivery', to: '/delivery', component: , display: [] }, - { title: 'Login', to: '/:subPath?/login', component: , constraints: [loggedOut], display: ['nav'] }, - { title: 'Register', to: '/:subPath?/register', component: , constraints: [loggedOut], display: ['nav'] }, - { title: 'Logout', to: '/:subPath?/logout', component: , constraints: [loggedIn], display: ['nav'] }, - { title: 'Docs', to: '/docs/:docType?', component: , display: [] }, - { title: 'Opps', to: '*', component: , display: [] }, - ]; + const navItems = [ + { title: 'Home', to: '/', component: , display: [] }, + { + title: 'Diner', + to: '/diner-dashboard', + component: , + display: [], + }, + { title: 'Order', to: '/menu', component: , display: ['nav'] }, + { + title: 'Franchise', + to: '/franchise-dashboard', + component: , + constraints: [isNotAdmin], + display: ['nav', 'footer'], + }, + { title: 'About', to: '/about', component: , display: ['footer'] }, + { title: 'History', to: '/history', component: , display: ['footer'] }, + { + title: 'Admin', + to: '/admin-dashboard', + component: , + constraints: [isAdmin], + display: ['nav'], + }, + { + title: 'Create franchise', + to: '/:subPath?/create-franchise', + component: , + display: [], + }, + { + title: 'Close franchise', + to: '/:subPath?/close-franchise', + component: , + display: [], + }, + { + title: 'Create store', + to: '/:subPath?/create-store', + component: , + display: [], + }, + { + title: 'Close store', + to: '/:subPath?/close-store', + component: , + display: [], + }, + { + title: 'Delete user', + to: '/:subPath?/delete-user', + component: , + display: [], + }, + { title: 'Payment', to: '/payment', component: , display: [] }, + { title: 'Delivery', to: '/delivery', component: , display: [] }, + { + title: 'Login', + to: '/:subPath?/login', + component: , + constraints: [loggedOut], + display: ['nav'], + }, + { + title: 'Register', + to: '/:subPath?/register', + component: , + constraints: [loggedOut], + display: ['nav'], + }, + { + title: 'Logout', + to: '/:subPath?/logout', + component: , + constraints: [loggedIn], + display: ['nav'], + }, + { title: 'Docs', to: '/docs/:docType?', component: , display: [] }, + { title: 'Opps', to: '*', component: , display: [] }, + ]; - return ( -
-
- + return ( +
+
+ -
- - {navItems.map((item) => ( - - ))} - -
+
+ + {navItems.map((item) => ( + + ))} + +
-
-
- ); +
+
+ ); } diff --git a/src/app/footer.tsx b/src/app/footer.tsx index 369eeb867..49eb86501 100644 --- a/src/app/footer.tsx +++ b/src/app/footer.tsx @@ -2,36 +2,39 @@ import React from 'react'; import { NavLink } from 'react-router-dom'; interface Props { - navItems: { title: string; to: string; display: string[] }[]; + navItems: { title: string; to: string; display: string[] }[]; } export default function Footer(props: Props) { - const [version, setVersion] = React.useState(''); + const [version, setVersion] = React.useState(''); - React.useEffect(() => { - fetch('/version.json') - .then((response) => response.json()) - .then((data) => setVersion(data.version)); - }, []); + React.useEffect(() => { + fetch('/version.json') + .then((response) => response.json()) + .then((data) => setVersion(data.version)); + }, []); - return ( -
-
- -

© 2024 JWT Pizza LTD. All rights reserved. Version: {version}

-
-
- ); + return ( +
+
+ +

+ © 2024 JWT Pizza LTD. All rights reserved. Version: {version} +

+
+
+ ); } diff --git a/src/app/header.tsx b/src/app/header.tsx index 511fc1312..eced6e14a 100644 --- a/src/app/header.tsx +++ b/src/app/header.tsx @@ -4,65 +4,83 @@ import { HamburgerIcon, CloseIcon } from '../icons'; import { User } from '../service/pizzaService'; interface Props { - user: User | null; - navItems: { title: string; to: string; display: string[]; constraints?: (() => boolean)[] }[]; + user: User | null; + navItems: { title: string; to: string; display: string[]; constraints?: (() => boolean)[] }[]; } export default function Header(props: Props) { - function validateConstraints(constraints: (() => boolean)[]) { - return constraints.every((c) => c()); - } + function validateConstraints(constraints: (() => boolean)[]) { + return constraints.every((c) => c()); + } - function generateUserText(user: User) { - const names = user?.name?.split(' ') || ['?']; - return names.length > 1 ? names[0].charAt(0) + names[names.length - 1].charAt(0) : names[0].charAt(0); - } + function generateUserText(user: User) { + const names = user?.name?.split(' ') || ['?']; + return names.length > 1 + ? names[0].charAt(0) + names[names.length - 1].charAt(0) + : names[0].charAt(0); + } - return ( -
-
- -
-
- ); + return ( +
+
+ +
+
+ ); } diff --git a/src/components/breadcrumb.tsx b/src/components/breadcrumb.tsx index 5baf80fea..3372084ec 100644 --- a/src/components/breadcrumb.tsx +++ b/src/components/breadcrumb.tsx @@ -3,34 +3,44 @@ import { NavLink } from 'react-router-dom'; import { HouseIcon, GreaterThanIcon } from '../icons'; interface Props { - location: string; + location: string; } export default function Breadcrumb(props: Props) { - let currentPath = ''; - const paths = props.location.split('/').map((path) => { - currentPath += '/' + path; - return ( -
  • - + let currentPath = ''; + const paths = props.location.split('/').map((path) => { + currentPath += '/' + path; + return ( +
  • + - - {path} - -
  • + + {path} + + + ); + }); + return ( +
      +
    1. + + + + + home + +
    2. + {location && paths} +
    ); - }); - return ( -
      -
    1. - - - - - home - -
    2. - {location && paths} -
    - ); } diff --git a/src/components/button.tsx b/src/components/button.tsx index 30c237eac..2cbd43ac1 100644 --- a/src/components/button.tsx +++ b/src/components/button.tsx @@ -1,18 +1,23 @@ import React from 'react'; interface Props { - title: string; - onPress: () => void; - disabled?: boolean; - submit?: boolean; - className?: string; + title: string; + onPress: () => void; + disabled?: boolean; + submit?: boolean; + className?: string; } export default function Button(props: Props) { - const finalClassName = `w-32 m-4 py-3 px-4 text-sm font-semibold rounded-lg border border-transparent bg-orange-800 text-white hover:bg-orange-600 ${props.className}`; - return ( - - ); + const finalClassName = `w-32 m-4 py-3 px-4 text-sm font-semibold rounded-lg border border-transparent bg-orange-800 text-white hover:bg-orange-600 ${props.className}`; + return ( + + ); } diff --git a/src/components/card.tsx b/src/components/card.tsx index d5c1e4129..5cda4013d 100644 --- a/src/components/card.tsx +++ b/src/components/card.tsx @@ -1,27 +1,30 @@ import React from 'react'; interface Props { - title: string; - description: string; - image: string; + title: string; + description: string; + image: string; } export default function Card(props: Props) { - return ( -
    - - - ); + ); } diff --git a/src/components/carousel.tsx b/src/components/carousel.tsx index 62094e11a..41f4b233d 100644 --- a/src/components/carousel.tsx +++ b/src/components/carousel.tsx @@ -1,29 +1,33 @@ import React from 'react'; interface Props { - slides: React.ReactNode[]; + slides: React.ReactNode[]; } export default function Carousel(props: Props) { - return ( -
    -
    -
    - {props.slides.map((slide, index) => ( -
    - {slide} + return ( +
    +
    +
    + {props.slides.map((slide, index) => ( +
    + {slide} +
    + ))} +
    - ))} -
    -
    -
    - {props.slides.map((_, index) => ( - - ))} -
    -
    - ); +
    + {props.slides.map((_, index) => ( + + ))} +
    +
    + ); } diff --git a/src/components/quote.tsx b/src/components/quote.tsx index c7fa1a9f1..b67898d1b 100644 --- a/src/components/quote.tsx +++ b/src/components/quote.tsx @@ -1,34 +1,36 @@ import React from 'react'; interface Props { - quote: string; - author: string; + quote: string; + author: string; } export default function Quote(props: Props) { - return ( -
    - + return ( +
    + -
    -

    - - {props.quote} - — {props.author} - -

    -
    -
    - ); +
    +

    + + {props.quote} + — {props.author} + +

    +
    +
    + ); } diff --git a/src/hooks/appNavigation.tsx b/src/hooks/appNavigation.tsx index 4b3ac4982..5f3bf8c50 100644 --- a/src/hooks/appNavigation.tsx +++ b/src/hooks/appNavigation.tsx @@ -1,20 +1,20 @@ import { useLocation, useNavigate } from 'react-router-dom'; function useBreadcrumb(sibling?: string) { - const location = useLocation(); - const navigate = useNavigate(); + const location = useLocation(); + const navigate = useNavigate(); - const navigateByBreadcrumb = () => { - let newPath = location.pathname.substring(0, location.pathname.lastIndexOf('/')); - if (sibling) { - newPath = newPath + '/' + sibling; - } else if (newPath === '') { - newPath = '/'; - } - navigate(newPath, { state: location.state }); - }; + const navigateByBreadcrumb = () => { + let newPath = location.pathname.substring(0, location.pathname.lastIndexOf('/')); + if (sibling) { + newPath = newPath + '/' + sibling; + } else if (newPath === '') { + newPath = '/'; + } + navigate(newPath, { state: location.state }); + }; - return navigateByBreadcrumb; + return navigateByBreadcrumb; } export { useBreadcrumb }; diff --git a/src/service/httpPizzaService.ts b/src/service/httpPizzaService.ts index 12ffbdb49..7004519dd 100644 --- a/src/service/httpPizzaService.ts +++ b/src/service/httpPizzaService.ts @@ -1,121 +1,164 @@ -import { PizzaService, Franchise, FranchiseList, Store, OrderHistory, User, Menu, Order, Endpoints, OrderResponse, JWTPayload } from './pizzaService'; +import { + PizzaService, + Franchise, + FranchiseList, + UserList, + Store, + OrderHistory, + User, + Menu, + Order, + Endpoints, + OrderResponse, + JWTPayload, +} from './pizzaService'; const pizzaServiceUrl = import.meta.env.VITE_PIZZA_SERVICE_URL; const pizzaFactoryUrl = import.meta.env.VITE_PIZZA_FACTORY_URL; class HttpPizzaService implements PizzaService { - async callEndpoint(path: string, method: string = 'GET', body?: any): Promise { - return new Promise(async (resolve, reject) => { - try { - const options: any = { - method: method, - headers: { - 'Content-Type': 'application/json', - }, - credentials: 'include', - }; - - const authToken = localStorage.getItem('token'); - if (authToken) { - options.headers['Authorization'] = `Bearer ${authToken}`; - } + async callEndpoint(path: string, method: string = 'GET', body?: any): Promise { + return new Promise(async (resolve, reject) => { + try { + const options: any = { + method: method, + headers: { + 'Content-Type': 'application/json', + }, + credentials: 'include', + }; + + const authToken = localStorage.getItem('token'); + if (authToken) { + options.headers['Authorization'] = `Bearer ${authToken}`; + } + + if (body) { + options.body = JSON.stringify(body); + } + + if (!path.startsWith('http')) { + path = pizzaServiceUrl + path; + } + + const r = await fetch(path, options); + const j = await r.json(); + if (r.ok) { + resolve(j); + } else { + reject({ code: r.status, message: j.message }); + } + } catch (e: any) { + reject({ code: 500, message: e.message }); + } + }); + } - if (body) { - options.body = JSON.stringify(body); - } + async login(email: string, password: string): Promise { + const { user, token } = await this.callEndpoint('/api/auth', 'PUT', { email, password }); + localStorage.setItem('token', token); + return Promise.resolve(user); + } - if (!path.startsWith('http')) { - path = pizzaServiceUrl + path; - } + async register(name: string, email: string, password: string): Promise { + const { user, token } = await this.callEndpoint('/api/auth', 'POST', { + name, + email, + password, + }); + localStorage.setItem('token', token); + return Promise.resolve(user); + } - const r = await fetch(path, options); - const j = await r.json(); - if (r.ok) { - resolve(j); - } else { - reject({ code: r.status, message: j.message }); - } - } catch (e: any) { - reject({ code: 500, message: e.message }); - } - }); - } - - async login(email: string, password: string): Promise { - const { user, token } = await this.callEndpoint('/api/auth', 'PUT', { email, password }); - localStorage.setItem('token', token); - return Promise.resolve(user); - } - - async register(name: string, email: string, password: string): Promise { - const { user, token } = await this.callEndpoint('/api/auth', 'POST', { name, email, password }); - localStorage.setItem('token', token); - return Promise.resolve(user); - } - - logout(): void { - this.callEndpoint('/api/auth', 'DELETE'); - localStorage.removeItem('token'); - } - - async getUser(): Promise { - let result: User | null = null; - if (localStorage.getItem('token')) { - try { - result = await this.callEndpoint('/api/user/me'); - } catch (e) { + logout(): void { + this.callEndpoint('/api/auth', 'DELETE'); localStorage.removeItem('token'); - } } - return Promise.resolve(result); - } - async getMenu(): Promise { - return this.callEndpoint('/api/order/menu'); - } + async getUser(): Promise { + let result: User | null = null; + if (localStorage.getItem('token')) { + try { + result = await this.callEndpoint('/api/user/me'); + } catch (e) { + localStorage.removeItem('token'); + } + } + return Promise.resolve(result); + } - async getOrders(user: User): Promise { - return this.callEndpoint('/api/order'); - } + async listUsers( + page: number = 0, + limit: number = 10, + nameFilter: string = '*', + ): Promise { + return this.callEndpoint(`/api/user?page=${page}&limit=${limit}&name=${nameFilter}`); + } - async order(order: Order): Promise { - return this.callEndpoint('/api/order', 'POST', order); - } + async updateUser(updatedUser: User): Promise { + const { user, token } = await this.callEndpoint( + `/api/user/${updatedUser.id}`, + 'PUT', + updatedUser, + ); + localStorage.setItem('token', token); + return Promise.resolve(user); + } - async verifyOrder(jwt: string): Promise { - return this.callEndpoint(pizzaFactoryUrl + '/api/order/verify', 'POST', { jwt }); - } + async deleteUser(userId: string): Promise { + return this.callEndpoint(`/api/user/${userId}`, 'DELETE'); + } - async getFranchise(user: User): Promise { - return this.callEndpoint(`/api/franchise/${user.id}`); - } + async getMenu(): Promise { + return this.callEndpoint('/api/order/menu'); + } - async createFranchise(franchise: Franchise): Promise { - return this.callEndpoint('/api/franchise', 'POST', franchise); - } + async getOrders(user: User): Promise { + return this.callEndpoint('/api/order'); + } - async getFranchises(page: number = 0, limit: number = 10, nameFilter: string = '*'): Promise { - return this.callEndpoint(`/api/franchise?page=${page}&limit=${limit}&name=${nameFilter}`); - } + async order(order: Order): Promise { + return this.callEndpoint('/api/order', 'POST', order); + } - async closeFranchise(franchise: Franchise): Promise { - return this.callEndpoint(`/api/franchise/${franchise.id}`, 'DELETE'); - } + async verifyOrder(jwt: string): Promise { + return this.callEndpoint(pizzaFactoryUrl + '/api/order/verify', 'POST', { jwt }); + } - async createStore(franchise: Franchise, store: Store): Promise { - return this.callEndpoint(`/api/franchise/${franchise.id}/store`, 'POST', store); - } + async getFranchise(user: User): Promise { + return this.callEndpoint(`/api/franchise/${user.id}`); + } - async closeStore(franchise: Franchise, store: Store): Promise { - return this.callEndpoint(`/api/franchise/${franchise.id}/store/${store.id}`, 'DELETE'); - } + async createFranchise(franchise: Franchise): Promise { + return this.callEndpoint('/api/franchise', 'POST', franchise); + } - async docs(docType: string): Promise { - if (docType === 'factory') { - return this.callEndpoint(pizzaFactoryUrl + `/api/docs`); + async getFranchises( + page: number = 0, + limit: number = 10, + nameFilter: string = '*', + ): Promise { + return this.callEndpoint(`/api/franchise?page=${page}&limit=${limit}&name=${nameFilter}`); + } + + async closeFranchise(franchise: Franchise): Promise { + return this.callEndpoint(`/api/franchise/${franchise.id}`, 'DELETE'); + } + + async createStore(franchise: Franchise, store: Store): Promise { + return this.callEndpoint(`/api/franchise/${franchise.id}/store`, 'POST', store); + } + + async closeStore(franchise: Franchise, store: Store): Promise { + return this.callEndpoint(`/api/franchise/${franchise.id}/store/${store.id}`, 'DELETE'); + } + + async docs(docType: string): Promise { + if (docType === 'factory') { + return this.callEndpoint(pizzaFactoryUrl + `/api/docs`); + } + return this.callEndpoint(`/api/docs`); } - return this.callEndpoint(`/api/docs`); - } } const httpPizzaService = new HttpPizzaService(); diff --git a/src/service/pizzaService.ts b/src/service/pizzaService.ts index 60dae2604..92635efc1 100644 --- a/src/service/pizzaService.ts +++ b/src/service/pizzaService.ts @@ -1,115 +1,142 @@ enum Role { - Diner = 'diner', - Franchisee = 'franchisee', - Admin = 'admin', + Diner = 'diner', + Franchisee = 'franchisee', + Admin = 'admin', } namespace Role { - export function isRole(user: User | null, role: Role): boolean { - return user != null && Array.isArray(user.roles) && !!user.roles.find((r) => r.role === role); - } + export function isRole(user: User | null, role: Role): boolean { + return ( + user != null && Array.isArray(user.roles) && !!user.roles.find((r) => r.role === role) + ); + } } type Menu = Pizza[]; type Pizza = { - id: string; - title: string; - description: string; - image: string; - price: number; + id: string; + title: string; + description: string; + image: string; + price: number; }; type OrderItem = { - menuId: string; - description: string; - price: number; + menuId: string; + description: string; + price: number; }; type Order = { - id: string; - franchiseId: string; - storeId: string; - date: string; - items: OrderItem[]; + id: string; + franchiseId: string; + storeId: string; + date: string; + items: OrderItem[]; }; type OrderResponse = { - order: Order; - jwt: string; + order: Order; + jwt: string; }; type OrderHistory = { - id: string; - dinerId: string; - orders: Order[]; + id: string; + dinerId: string; + orders: Order[]; }; type UserRole = { - role: Role; - objectId?: string; + role: Role; + objectId?: string; }; type User = { - id?: string; - name?: string; - email?: string; - password?: string; - roles?: UserRole[]; + id?: string; + name?: string; + email?: string; + password?: string; + roles?: UserRole[]; }; type Store = { - id: string; - name: string; - totalRevenue?: number; + id: string; + name: string; + totalRevenue?: number; }; type Franchise = { - id: string; - admins?: { email: string; id?: string; name?: string }[]; - name: string; - stores: Store[]; + id: string; + admins?: { email: string; id?: string; name?: string }[]; + name: string; + stores: Store[]; }; type FranchiseList = { - franchises: Franchise[]; - more: boolean; + franchises: Franchise[]; + more: boolean; +}; + +type UserList = { + users: User[]; + more: boolean; }; type Endpoint = { - requiresAuth: boolean; - method: string; - path: string; - description: string; - example: string; - response: any; + requiresAuth: boolean; + method: string; + path: string; + description: string; + example: string; + response: any; }; type Endpoints = { - endpoints: Endpoint[]; + endpoints: Endpoint[]; }; type JWTPayload = { - message: string; - payload: string; + message: string; + payload: string; }; interface PizzaService { - login(email: string, password: string): Promise; - register(email: string, password: string, role: string): Promise; - logout(): void; - getUser(): Promise; - getMenu(): Promise; - getOrders(user: User): Promise; - order(order: Order): Promise; - verifyOrder(jwt: string): Promise; - getFranchise(user: User): Promise; - createFranchise(franchise: Franchise): Promise; - getFranchises(page: number, limit: number, nameFilter: string): Promise; - closeFranchise(franchise: Franchise): Promise; - createStore(franchise: Franchise, store: Store): Promise; - closeStore(franchise: Franchise, store: Store): Promise; - docs(docType: string): Promise; + login(email: string, password: string): Promise; + register(email: string, password: string, role: string): Promise; + logout(): void; + getUser(): Promise; + listUsers(page: number, limit: number, nameFilter: string): Promise; + updateUser(user: User): Promise; + deleteUser(userId: string): Promise; + getMenu(): Promise; + getOrders(user: User): Promise; + order(order: Order): Promise; + verifyOrder(jwt: string): Promise; + getFranchise(user: User): Promise; + createFranchise(franchise: Franchise): Promise; + getFranchises(page: number, limit: number, nameFilter: string): Promise; + closeFranchise(franchise: Franchise): Promise; + createStore(franchise: Franchise, store: Store): Promise; + closeStore(franchise: Franchise, store: Store): Promise; + docs(docType: string): Promise; } -export { Role, PizzaService, User, Menu, Pizza, OrderHistory, Order, Franchise, FranchiseList, Store, OrderItem, Endpoint, Endpoints, OrderResponse, JWTPayload }; +export { + Role, + PizzaService, + User, + Menu, + Pizza, + OrderHistory, + Order, + Franchise, + FranchiseList, + UserList, + Store, + OrderItem, + Endpoint, + Endpoints, + OrderResponse, + JWTPayload, +}; diff --git a/src/views/about.tsx b/src/views/about.tsx index a38ee4e2c..1f2955131 100644 --- a/src/views/about.tsx +++ b/src/views/about.tsx @@ -2,92 +2,114 @@ import React from 'react'; import View from './view'; export default function About() { - return ( - -
    - -

    - At JWT Pizza, our amazing employees are the secret behind our delicious pizzas. They are passionate about their craft and spend every waking moment dreaming about how to make our pizzas even - better. From selecting the finest ingredients to perfecting the dough and sauce recipes, our employees go above and beyond to ensure the highest quality and taste in every bite. Their - dedication and attention to detail make all the difference in creating a truly exceptional pizza experience for our customers. We take pride in our team and their commitment to delivering - the best pizza in town. -

    -

    - Our talented employees at JWT Pizza are true artisans. They pour their heart and soul into every pizza they create, striving for perfection in every aspect. From hand-stretching the dough to - carefully layering the toppings, they take pride in their work and are constantly seeking ways to elevate the pizza-making process. Their creativity and expertise shine through in every - slice, resulting in a pizza that is not only delicious but also a work of art. We are grateful for our dedicated team and their unwavering commitment to delivering the most flavorful and - satisfying pizzas to our valued customers. -

    + return ( + +
    + +

    + At JWT Pizza, our amazing employees are the secret behind our delicious pizzas. + They are passionate about their craft and spend every waking moment dreaming + about how to make our pizzas even better. From selecting the finest ingredients + to perfecting the dough and sauce recipes, our employees go above and beyond to + ensure the highest quality and taste in every bite. Their dedication and + attention to detail make all the difference in creating a truly exceptional + pizza experience for our customers. We take pride in our team and their + commitment to delivering the best pizza in town. +

    +

    + Our talented employees at JWT Pizza are true artisans. They pour their heart and + soul into every pizza they create, striving for perfection in every aspect. From + hand-stretching the dough to carefully layering the toppings, they take pride in + their work and are constantly seeking ways to elevate the pizza-making process. + Their creativity and expertise shine through in every slice, resulting in a + pizza that is not only delicious but also a work of art. We are grateful for our + dedicated team and their unwavering commitment to delivering the most flavorful + and satisfying pizzas to our valued customers. +

    -

    Our employees

    +

    + Our employees +

    -

    - JWT Pizza is home to a team of pizza enthusiasts who are truly passionate about their craft. They are constantly experimenting with new flavors, techniques, and ingredients to push the - boundaries of traditional pizza-making. Their relentless pursuit of perfection is evident in every bite, as they strive to create a pizza experience that is unparalleled. Our employees - understand that the secret to a great pizza lies in the details, and they leave no stone unturned in their quest for pizza perfection. We are proud to have such dedicated individuals on our - team, as they are the driving force behind our reputation for exceptional quality and taste. -

    -
    -
    - Employee stock photo - - James - -
    -
    - Employee stock photo - - Maria - -
    -
    - Employee stock photo - - Anna - -
    -
    - Employee stock photo - - Brian - -
    -
    -

    - At JWT Pizza, our employees are more than just pizza makers. They are culinary artists who are deeply passionate about their craft. They approach each pizza with creativity, precision, and a - genuine love for what they do. From experimenting with unique flavor combinations to perfecting the cooking process, our employees are constantly pushing the boundaries of what a pizza can - be. Their dedication and expertise result in pizzas that are not only delicious but also a reflection of their passion and commitment. We are grateful for our talented team and the - incredible pizzas they create day in and day out. -

    -
    -
    - ); +

    + JWT Pizza is home to a team of pizza enthusiasts who are truly passionate about + their craft. They are constantly experimenting with new flavors, techniques, and + ingredients to push the boundaries of traditional pizza-making. Their relentless + pursuit of perfection is evident in every bite, as they strive to create a pizza + experience that is unparalleled. Our employees understand that the secret to a + great pizza lies in the details, and they leave no stone unturned in their quest + for pizza perfection. We are proud to have such dedicated individuals on our + team, as they are the driving force behind our reputation for exceptional + quality and taste. +

    +
    +
    + Employee stock photo + + James + +
    +
    + Employee stock photo + + Maria + +
    +
    + Employee stock photo + + Anna + +
    +
    + Employee stock photo + + Brian + +
    +
    +

    + At JWT Pizza, our employees are more than just pizza makers. They are culinary + artists who are deeply passionate about their craft. They approach each pizza + with creativity, precision, and a genuine love for what they do. From + experimenting with unique flavor combinations to perfecting the cooking process, + our employees are constantly pushing the boundaries of what a pizza can be. + Their dedication and expertise result in pizzas that are not only delicious but + also a reflection of their passion and commitment. We are grateful for our + talented team and the incredible pizzas they create day in and day out. +

    +
    +
    + ); } diff --git a/src/views/adminDashboard.tsx b/src/views/adminDashboard.tsx index d49e16e06..70195431e 100644 --- a/src/views/adminDashboard.tsx +++ b/src/views/adminDashboard.tsx @@ -4,128 +4,300 @@ import { useNavigate } from 'react-router-dom'; import NotFound from './notFound'; import Button from '../components/button'; import { pizzaService } from '../service/service'; -import { Franchise, FranchiseList, Role, Store, User } from '../service/pizzaService'; +import { Franchise, FranchiseList, Role, Store, User, UserList } from '../service/pizzaService'; import { TrashIcon } from '../icons'; interface Props { - user: User | null; + user: User | null; } export default function AdminDashboard(props: Props) { - const navigate = useNavigate(); - const [franchiseList, setFranchiseList] = React.useState({ franchises: [], more: false }); - const [franchisePage, setFranchisePage] = React.useState(0); - const filterFranchiseRef = React.useRef(null); + const navigate = useNavigate(); + const [activeList, setActiveList] = React.useState<'franchises' | 'users'>('franchises'); + const [nameFilter, setNameFilter] = React.useState('*'); + const [franchiseList, setFranchiseList] = React.useState({ + franchises: [], + more: false, + }); + const [userList, setUserList] = React.useState({ users: [], more: false }); + const [listPage, setListPage] = React.useState(0); + const filterFranchiseRef = React.useRef(null); - React.useEffect(() => { - (async () => { - setFranchiseList(await pizzaService.getFranchises(franchisePage, 3, '*')); - })(); - }, [props.user, franchisePage]); + React.useEffect(() => { + (async () => { + if (activeList === 'franchises') { + setFranchiseList(await pizzaService.getFranchises(listPage, 3, nameFilter)); + return; + } + setUserList(await pizzaService.listUsers(listPage, 10, nameFilter)); + })(); + }, [props.user, listPage, activeList, nameFilter]); - function createFranchise() { - navigate('/admin-dashboard/create-franchise'); - } + function createFranchise() { + navigate('/admin-dashboard/create-franchise'); + } - async function closeFranchise(franchise: Franchise) { - navigate('/admin-dashboard/close-franchise', { state: { franchise: franchise } }); - } + async function closeFranchise(franchise: Franchise) { + navigate('/admin-dashboard/close-franchise', { state: { franchise: franchise } }); + } - async function closeStore(franchise: Franchise, store: Store) { - navigate('/admin-dashboard/close-store', { state: { franchise: franchise, store: store } }); - } + async function closeStore(franchise: Franchise, store: Store) { + navigate('/admin-dashboard/close-store', { state: { franchise: franchise, store: store } }); + } - async function filterFranchises() { - setFranchiseList(await pizzaService.getFranchises(franchisePage, 10, `*${filterFranchiseRef.current?.value}*`)); - } + async function filterFranchises() { + const value = filterFranchiseRef.current?.value?.trim() || ''; + setNameFilter(value ? `*${value}*` : '*'); + setListPage(0); + } - let response = ; - if (Role.isRole(props.user, Role.Admin)) { - response = ( - -
    -

    Franchises

    -
    -
    -
    -
    -
    - - - - {['Franchise', 'Franchisee', 'Store', 'Revenue', 'Action'].map((header) => ( - - ))} - - - {franchiseList.franchises.map((franchise, findex) => { - return ( - - - - - - + function openDeleteUser(user: User) { + navigate('/admin-dashboard/delete-user', { state: { user } }); + } - {franchise.stores.map((store, sindex) => { - return ( - - - - - - ); - })} - - ); - })} - - - - - - -
    - {header} -
    {franchise.name} - {franchise.admins?.map((o) => o.name).join(', ')} - - -
    - {store.name} - {store.totalRevenue?.toLocaleString()} ₿ - -
    - - - - - -
    -
    + let response = ; + if (Role.isRole(props.user, Role.Admin)) { + response = ( + +
    +
    + + +
    +

    + {activeList === 'franchises' ? 'Franchises' : 'Users'} +

    +
    +
    +
    +
    +
    + + + + {(activeList === 'franchises' + ? [ + 'Franchise', + 'Franchisee', + 'Store', + 'Revenue', + 'Action', + ] + : ['User', 'Email', 'Action'] + ).map((header) => ( + + ))} + + + {activeList === 'franchises' && + franchiseList.franchises.map((franchise, findex) => { + return ( + + + + + + + + {franchise.stores.map( + (store, sindex) => { + return ( + + + + + + ); + }, + )} + + ); + })} + {activeList === 'users' && ( + + {userList.users.map((user, index) => ( + + + + + + ))} + + )} + + + + + + +
    + {header} +
    + {franchise.name} + + {franchise.admins + ?.map((o) => o.name) + .join(', ')} + + +
    + {store.name} + + {store.totalRevenue?.toLocaleString()}{' '} + ₿ + + +
    + {user.name || 'Unknown user'} + + {user.email || 'No email'} + + +
    + + + + + +
    +
    +
    +
    +
    +
    -
    -
    -
    -
    -
    -
    - - ); - } + {activeList === 'franchises' && ( +
    +
    + )} + + ); + } - return response; + return response; } diff --git a/src/views/closeFranchise.tsx b/src/views/closeFranchise.tsx index 2a7029f68..73853e2c6 100644 --- a/src/views/closeFranchise.tsx +++ b/src/views/closeFranchise.tsx @@ -6,24 +6,30 @@ import Button from '../components/button'; import { useBreadcrumb } from '../hooks/appNavigation'; export default function CloseFranchise() { - const state = useLocation().state; - const navigateToParentPath = useBreadcrumb(); + const state = useLocation().state; + const navigateToParentPath = useBreadcrumb(); - async function close() { - await pizzaService.closeFranchise(state.franchise); - navigateToParentPath(); - } + async function close() { + await pizzaService.closeFranchise(state.franchise); + navigateToParentPath(); + } - return ( - -
    -
    - Are you sure you want to close the {state.franchise.name} franchise? This will close all associated stores and cannot be restored. All outstanding - revenue will not be refunded. -
    -
    -
    - ); + return ( + +
    +
    + Are you sure you want to close the{' '} + {state.franchise.name} franchise? This + will close all associated stores and cannot be restored. All outstanding revenue + will not be refunded. +
    +
    +
    + ); } diff --git a/src/views/closeStore.tsx b/src/views/closeStore.tsx index 5e78b1796..22ef619d1 100644 --- a/src/views/closeStore.tsx +++ b/src/views/closeStore.tsx @@ -6,24 +6,30 @@ import View from './view'; import Button from '../components/button'; export default function CloseStore() { - const state = useLocation().state; - const navigateToParent = useBreadcrumb(); + const state = useLocation().state; + const navigateToParent = useBreadcrumb(); - async function close() { - await pizzaService.closeStore(state.franchise, state.store); - navigateToParent(); - } + async function close() { + await pizzaService.closeStore(state.franchise, state.store); + navigateToParent(); + } - return ( - -
    -
    - Are you sure you want to close the {state.franchise.name} store {state.store.name} ? This cannot be - restored. All outstanding revenue will not be refunded. -
    -
    -
    - ); + return ( + +
    +
    + Are you sure you want to close the{' '} + {state.franchise.name} store{' '} + {state.store.name} ? This cannot be + restored. All outstanding revenue will not be refunded. +
    +
    +
    + ); } diff --git a/src/views/createFranchise.tsx b/src/views/createFranchise.tsx index daa0440fa..e19b6826e 100644 --- a/src/views/createFranchise.tsx +++ b/src/views/createFranchise.tsx @@ -8,51 +8,62 @@ import Button from '../components/button'; import { Franchise } from '../service/pizzaService'; export default function CreateFranchise() { - const navigateToParentPath = useBreadcrumb(); - const [franchise, setFranchise] = React.useState({ stores: [], id: '', name: '' }); + const navigateToParentPath = useBreadcrumb(); + const [franchise, setFranchise] = React.useState({ stores: [], id: '', name: '' }); - async function createFranchise(event: React.FormEvent) { - event.preventDefault(); - await pizzaService.createFranchise(franchise); - navigateToParentPath(); - } + async function createFranchise(event: React.FormEvent) { + event.preventDefault(); + await pizzaService.createFranchise(franchise); + navigateToParentPath(); + } - return ( - -
    -
    -
    Want to create franchise?
    -
    -
    - setFranchise({ ...franchise, name: e.target.value })} - className="peer py-3 px-4 ps-11 block w-full bg-gray-100 border-transparent rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none" - placeholder="franchise name" - /> -
    - -
    -
    -
    - setFranchise({ ...franchise, admins: [{ email: e.target.value }] })} - className="peer py-3 px-4 ps-11 block w-full bg-gray-100 border-transparent rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none" - placeholder="franchisee admin email" - /> -
    - -
    -
    -
    + return ( + +
    + +
    Want to create franchise?
    +
    +
    + + setFranchise({ ...franchise, name: e.target.value }) + } + className="peer py-3 px-4 ps-11 block w-full bg-gray-100 border-transparent rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none" + placeholder="franchise name" + /> +
    + +
    +
    +
    + + setFranchise({ + ...franchise, + admins: [{ email: e.target.value }], + }) + } + className="peer py-3 px-4 ps-11 block w-full bg-gray-100 border-transparent rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none" + placeholder="franchisee admin email" + /> +
    + +
    +
    +
    -
    -
    - ); +
    +
    + ); } diff --git a/src/views/createStore.tsx b/src/views/createStore.tsx index 924f98ed2..1846d653f 100644 --- a/src/views/createStore.tsx +++ b/src/views/createStore.tsx @@ -9,41 +9,45 @@ import { pizzaService } from '../service/service'; import { Store } from '../service/pizzaService'; export default function CreateStore() { - const state = useLocation().state; - const navigateToParentPath = useBreadcrumb(); - const [store, setStore] = React.useState({ id: '', name: '' }); + const state = useLocation().state; + const navigateToParentPath = useBreadcrumb(); + const [store, setStore] = React.useState({ id: '', name: '' }); - async function createStore(event: React.FormEvent) { - event.preventDefault(); - await pizzaService.createStore(state.franchise, store); - navigateToParentPath(); - } + async function createStore(event: React.FormEvent) { + event.preventDefault(); + await pizzaService.createStore(state.franchise, store); + navigateToParentPath(); + } - return ( - -
    -
    -
    -
    -
    - setStore({ ...store, name: e.target.value })} - className="peer py-3 px-4 ps-11 block w-full bg-gray-100 border-transparent rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none" - placeholder="store name" - /> -
    - -
    -
    -
    -
    + return ( + +
    + +
    +
    +
    + setStore({ ...store, name: e.target.value })} + className="peer py-3 px-4 ps-11 block w-full bg-gray-100 border-transparent rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none" + placeholder="store name" + /> +
    + +
    +
    +
    +
    -
    -
    - ); +
    +
    + ); } diff --git a/src/views/deleteUser.tsx b/src/views/deleteUser.tsx new file mode 100644 index 000000000..527371036 --- /dev/null +++ b/src/views/deleteUser.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { useLocation } from 'react-router-dom'; +import { useBreadcrumb } from '../hooks/appNavigation'; +import { pizzaService } from '../service/service'; +import View from './view'; +import Button from '../components/button'; +import { User } from '../service/pizzaService'; + +export default function DeleteUser() { + const state = useLocation().state as { user?: User } | null; + const navigateToParent = useBreadcrumb(); + const user = state?.user; + + async function remove() { + if (!user?.id) { + navigateToParent(); + return; + } + await pizzaService.deleteUser(user.id); + navigateToParent(); + } + + return ( + +
    +
    + Are you sure you want to delete user{' '} + {user?.name || 'Unknown user'}{' '} + ({user?.email || 'No email'})? This + cannot be restored. +
    +
    +
    + ); +} diff --git a/src/views/delivery.tsx b/src/views/delivery.tsx index 703c1916c..342d60b29 100644 --- a/src/views/delivery.tsx +++ b/src/views/delivery.tsx @@ -8,84 +8,127 @@ import { HSOverlay } from 'preline'; import { JWTPayload, Order } from '../service/pizzaService'; export default function Delivery() { - const navigate = useNavigate(); - const location = useLocation(); - const order: Order = location.state?.order || { pizzas: [] }; - const jwt: string = location.state?.jwt || 'error'; - const [jwtPayload, setJwtPayload] = React.useState({ message: 'invalid', payload: "{ error: 'invalid JWT' }" }); + const navigate = useNavigate(); + const location = useLocation(); + const order: Order = location.state?.order || { pizzas: [] }; + const jwt: string = location.state?.jwt || 'error'; + const [jwtPayload, setJwtPayload] = React.useState({ + message: 'invalid', + payload: "{ error: 'invalid JWT' }", + }); - async function verify() { - try { - const r = await pizzaService.verifyOrder(jwt); - setJwtPayload(r); - } catch (e: any) { - setJwtPayload({ ...e, payload: { error: 'invalid JWT. Looks like you have a bad pizza!' } }); + async function verify() { + try { + const r = await pizzaService.verifyOrder(jwt); + setJwtPayload(r); + } catch (e: any) { + setJwtPayload({ + ...e, + payload: { error: 'invalid JWT. Looks like you have a bad pizza!' }, + }); + } + HSOverlay.open(document.getElementById('hs-jwt-modal')!); } - HSOverlay.open(document.getElementById('hs-jwt-modal')!); - } - return ( - -
    - - - - + return ( + +
    + + + + -
    -
    +
    +
    -
    -
    order ID:
    {order.id}
    -
    pie count:
    {order.items?.length}
    -
    total:
    {' '} -
    {order.items?.reduce((a: number, c: any) => a + c.price, 0).toLocaleString()} ₿
    -
    +
    +
    order ID:
    {' '} +
    {order.id}
    +
    pie count:
    {' '} +
    {order.items?.length}
    +
    total:
    {' '} +
    + {order.items + ?.reduce((a: number, c: any) => a + c.price, 0) + .toLocaleString()}{' '} + ₿ +
    +
    -
    - {jwt} -
    -
    - -
    -
    -
    -
    -

    - JWT Pizza - {jwtPayload.message} -

    - -
    -
    -
    {JSON.stringify(jwtPayload.payload, null, 2)}
    +
    + {jwt} +
    -
    - + +
    +
    +
    +
    +

    + JWT Pizza -{' '} + + {jwtPayload.message} + +

    + +
    +
    +
    +                                {JSON.stringify(jwtPayload.payload, null, 2)}
    +                            
    +
    +
    + +
    +
    +
    -
    -
    -
    - - ); + + ); } diff --git a/src/views/dinerDashboard.tsx b/src/views/dinerDashboard.tsx index ae535c386..9093c893a 100644 --- a/src/views/dinerDashboard.tsx +++ b/src/views/dinerDashboard.tsx @@ -3,102 +3,220 @@ import { Link } from 'react-router-dom'; import View from './view'; import { pizzaService } from '../service/service'; import { Order, OrderHistory, Role, User } from '../service/pizzaService'; +import { CloseIcon } from '../icons'; +import { HSOverlay } from 'preline'; +import Button from '../components/button'; interface Props { - user: User | null; + user: User | null; + setUser: (user: User) => void; } export default function DinerDashboard(props: Props) { - const user = props.user || ({} as User); - const [orders, setOrders] = React.useState([]); + const user = props.user || ({} as User); + const [orders, setOrders] = React.useState([]); - React.useEffect(() => { - (async () => { - if (user) { - const r: OrderHistory = await pizzaService.getOrders(user); - setOrders(r.orders); - } - })(); - }, [user]); + const nameRef = React.useRef(null); + const emailRef = React.useRef(null); + const passwordRef = React.useRef(null); - function formatRole(role: { role: Role; objectId?: string }) { - if (role.role === Role.Franchisee) { - return `Franchisee on ${role.objectId}`; + React.useEffect(() => { + (async () => { + if (user) { + const r: OrderHistory = await pizzaService.getOrders(user); + setOrders(r.orders); + } + })(); + }, [user]); + + async function updateUser() { + let updatedUser: User = { + id: user.id, + name: nameRef.current?.value, + email: emailRef.current?.value, + password: passwordRef.current?.value || undefined, + roles: user.roles, + }; + + await pizzaService.updateUser(updatedUser); + props.setUser(updatedUser); + setTimeout(() => { + HSOverlay.close(document.getElementById('hs-jwt-modal')!); + }, 100); } - return role.role; - } + function formatRole(role: { role: Role; objectId?: string }) { + if (role.role === Role.Franchisee) { + return `Franchisee on ${role.objectId}`; + } - return ( - -
    -
    - Employee stock photo -
    + return role.role; + } -
    -
    name:
    {user.name}
    -
    email:
    {user.email}
    -
    role:
    {' '} -
    - {user.roles && - user.roles.map((role, index) => ( - - {index === 0 ? '' : ', '} {formatRole(role)} - - ))} -
    -
    + return ( + +
    +
    + Employee stock photo +
    - {orders?.length === 0 && ( -
    - How have you lived this long without having a pizza?{' '} - - Buy one - {' '} - now! -
    - )} - {orders?.length > 0 && ( - <> -
    Here is your history of all the good times.
    -
    -
    -
    -
    -
    - - - - - - - - - - {orders.map((order, index) => ( - - - - - - ))} - -
    - ID - - Price - - Date -
    {order.id}{order.items.reduce((a, c) => a + c.price, 0).toLocaleString()} ₿{order.date.toLocaleString()}
    +
    +
    name:
    {' '} +
    {user.name}
    +
    email:
    {' '} +
    {user.email}
    +
    role:
    {' '} +
    + {user.roles && + user.roles.map((role, index) => ( + + {index === 0 ? '' : ', '} {formatRole(role)} + + ))} +
    +
    + + {orders?.length === 0 && ( +
    + How have you lived this long without having a pizza?{' '} + + Buy one + {' '} + now! +
    + )} + {orders?.length > 0 && ( + <> +
    + Here is your history of all the good times. +
    +
    +
    +
    +
    +
    + + + + + + + + + + {orders.map((order, index) => ( + + + + + + ))} + +
    + ID + + Price + + Date +
    + {order.id} + + {order.items + .reduce( + (a, c) => a + c.price, + 0, + ) + .toLocaleString()}{' '} + ₿ + + {order.date.toLocaleString()} +
    +
    +
    +
    +
    +
    + + )} +
    + -
    - - )} -
    - - ); + + ); } diff --git a/src/views/docs.tsx b/src/views/docs.tsx index bf6a395b8..f443f1bfc 100644 --- a/src/views/docs.tsx +++ b/src/views/docs.tsx @@ -5,52 +5,56 @@ import { useParams } from 'react-router-dom'; import { Endpoints } from '../service/pizzaService'; const apis = [ - { name: 'service', url: import.meta.env.VITE_PIZZA_SERVICE_URL }, - { name: 'factory', url: import.meta.env.VITE_PIZZA_FACTORY_URL }, + { name: 'service', url: import.meta.env.VITE_PIZZA_SERVICE_URL }, + { name: 'factory', url: import.meta.env.VITE_PIZZA_FACTORY_URL }, ]; const Docs = () => { - const { docType } = useParams(); - const [docs, setDocs] = React.useState({ endpoints: [] }); - React.useEffect(() => { - (async () => { - setDocs(await pizzaService.docs(docType!)); - })(); - }, []); + const { docType } = useParams(); + const [docs, setDocs] = React.useState({ endpoints: [] }); + React.useEffect(() => { + (async () => { + setDocs(await pizzaService.docs(docType!)); + })(); + }, []); - return ( - -
    - {docs.endpoints.map((doc, index) => ( -
    -

    - {doc.requiresAuth && 🔐} [{doc.method}] {doc.path} -

    -

    {doc.description}

    + return ( + +
    + {docs.endpoints.map((doc, index) => ( +
    +

    + {doc.requiresAuth && 🔐} [{doc.method}] {doc.path} +

    +

    {doc.description}

    -
    - -
    {doc.example}
    +
    + +
    + {doc.example} +
    +
    +
    + +
    +                                {JSON.stringify(doc.response, null, 2)}
    +                            
    +
    +
    + ))}
    -
    - -
    {JSON.stringify(doc.response, null, 2)}
    +
    + {apis.map((api, index) => ( +
    + {api.name}:  + + {api.url} + +
    + ))}
    -
    - ))} -
    -
    - {apis.map((api, index) => ( -
    - {api.name}:  - - {api.url} - -
    - ))} -
    -
    - ); + + ); }; export default Docs; diff --git a/src/views/franchiseDashboard.tsx b/src/views/franchiseDashboard.tsx index a62ee46ee..652c3e90c 100644 --- a/src/views/franchiseDashboard.tsx +++ b/src/views/franchiseDashboard.tsx @@ -8,188 +8,255 @@ import { pizzaService } from '../service/service'; import { Franchise, Store, User } from '../service/pizzaService'; interface Props { - user: User | null; + user: User | null; } export default function FranchiseDashboard(props: Props) { - const navigate = useNavigate(); - const [franchise, setFranchise] = React.useState(null); + const navigate = useNavigate(); + const [franchise, setFranchise] = React.useState(null); - React.useEffect(() => { - (async () => { - if (props.user) { - const franchises = await pizzaService.getFranchise(props.user); - if (franchises.length) setFranchise(franchises[0]); - } - })(); - }, [props.user]); + React.useEffect(() => { + (async () => { + if (props.user) { + const franchises = await pizzaService.getFranchise(props.user); + if (franchises.length) setFranchise(franchises[0]); + } + })(); + }, [props.user]); - function createStore() { - navigate('/franchise-dashboard/create-store', { state: { franchise: franchise } }); - } + function createStore() { + navigate('/franchise-dashboard/create-store', { state: { franchise: franchise } }); + } - function closeStore(franchise: Franchise, store: Store) { - navigate('/franchise-dashboard/close-store', { state: { franchise: franchise, store: store } }); - } + function closeStore(franchise: Franchise, store: Store) { + navigate('/franchise-dashboard/close-store', { + state: { franchise: franchise, store: store }, + }); + } - if (!franchise) { - return whyFranchise(); - } + if (!franchise) { + return whyFranchise(); + } - return ( - -
    Everything you need to run an JWT Pizza franchise. Your gateway to success.
    + return ( + +
    + Everything you need to run an JWT Pizza franchise. Your gateway to success. +
    -
    -
    -
    -
    -
    - - - - {['Name', 'Revenue', 'Action'].map((header) => ( - - ))} - - - - {franchise.stores?.map((store, index) => ( - - - - - - ))} - -
    - {header} -
    {store.name}{store.totalRevenue?.toLocaleString()} ₿ - -
    -
    +
    +
    +
    +
    +
    + + + + {['Name', 'Revenue', 'Action'].map((header) => ( + + ))} + + + + {franchise.stores?.map((store, index) => ( + + + + + + ))} + +
    + {header} +
    + {store.name} + + {store.totalRevenue?.toLocaleString()} ₿ + + +
    +
    +
    +
    +
    -
    -
    -
    -
    -

    , + , + , + , + ]} + /> +

    +

    - Pizza is an absolute delight that brings joy to people of all ages. The perfect combination of crispy crust, savory sauce, and gooey cheese - makes pizza an irresistible treat. At JWT Pizza, we take pride in serving the web's best pizza, crafted with love and passion. Our skilled - chefs use only the finest ingredients to create mouthwatering pizzas that will leave you craving for more. Whether you prefer classic flavors - or adventurous toppings, our diverse menu has something for everyone. So why wait? Indulge in the pizza experience of a lifetime and visit JWT - Pizza today! -

    - -

    - Pizza has come a long way since its humble beginnings. From its origins in Italy to becoming a global sensation, pizza has captured the hearts - and taste buds of people worldwide. It has become a symbol of comfort, celebration, and togetherness. At JWT Pizza, we understand the magic of - pizza and strive to deliver an unforgettable dining experience. Our cozy ambiance, friendly staff, and delectable pizzas create the perfect - setting for a memorable meal. Whether you're dining with family, friends, or enjoying a solo pizza night, Pizza Shop is the place to be. -

    -

    - Pizza is not just a food; it's an experience. The aroma of freshly baked pizza, the sight of melted cheese stretching with every bite, and the - explosion of flavors in your mouth - it's a sensory journey like no other. At JWT Pizza, we believe in the power of pizza to bring people - together. Our inviting atmosphere and warm hospitality make every visit a special occasion. Whether you're celebrating a birthday, - anniversary, or simply craving a delicious meal, JWT Pizza is here to make your experience extraordinary. Join us and discover the magic of - pizza at its finest. -

    -

    - Pizza is a universal language that transcends borders and cultures. It's a dish that brings people from all walks of life together, united by - their love for this culinary masterpiece. At JWT Pizza, we embrace diversity and celebrate the joy of sharing a pizza with friends and family. - Our menu features a wide range of flavors inspired by different cuisines, ensuring there's something for everyone. Whether you're a meat - lover, a vegetarian, or have dietary restrictions, our pizzas are crafted to satisfy every palate. Come and experience the magic of pizza at - JWT Pizza today! -

    - - ); + first-letter:mr-3 first-letter:float-left" + > + Pizza is an absolute delight that brings joy to people of all ages. The perfect + combination of crispy crust, savory sauce, and gooey cheese makes pizza an + irresistible treat. At JWT Pizza, we take pride in serving the web's best pizza, + crafted with love and passion. Our skilled chefs use only the finest ingredients to + create mouthwatering pizzas that will leave you craving for more. Whether you prefer + classic flavors or adventurous toppings, our diverse menu has something for + everyone. So why wait? Indulge in the pizza experience of a lifetime and visit JWT + Pizza today! +

    + +

    + Pizza has come a long way since its humble beginnings. From its origins in Italy to + becoming a global sensation, pizza has captured the hearts and taste buds of people + worldwide. It has become a symbol of comfort, celebration, and togetherness. At JWT + Pizza, we understand the magic of pizza and strive to deliver an unforgettable + dining experience. Our cozy ambiance, friendly staff, and delectable pizzas create + the perfect setting for a memorable meal. Whether you're dining with family, + friends, or enjoying a solo pizza night, Pizza Shop is the place to be. +

    +

    + Pizza is not just a food; it's an experience. The aroma of freshly baked pizza, the + sight of melted cheese stretching with every bite, and the explosion of flavors in + your mouth - it's a sensory journey like no other. At JWT Pizza, we believe in the + power of pizza to bring people together. Our inviting atmosphere and warm + hospitality make every visit a special occasion. Whether you're celebrating a + birthday, anniversary, or simply craving a delicious meal, JWT Pizza is here to make + your experience extraordinary. Join us and discover the magic of pizza at its + finest. +

    +

    + Pizza is a universal language that transcends borders and cultures. It's a dish that + brings people from all walks of life together, united by their love for this + culinary masterpiece. At JWT Pizza, we embrace diversity and celebrate the joy of + sharing a pizza with friends and family. Our menu features a wide range of flavors + inspired by different cuisines, ensuring there's something for everyone. Whether + you're a meat lover, a vegetarian, or have dietary restrictions, our pizzas are + crafted to satisfy every palate. Come and experience the magic of pizza at JWT Pizza + today! +

    + + ); } diff --git a/src/views/login.tsx b/src/views/login.tsx index 77075f8ba..46feb28fa 100644 --- a/src/views/login.tsx +++ b/src/views/login.tsx @@ -7,102 +7,114 @@ import View from './view'; import { User } from '../service/pizzaService'; interface Props { - setUser: (user: User) => void; + setUser: (user: User) => void; } export default function Login(props: Props) { - const [password, setPassword] = React.useState(''); - const [email, setEmail] = React.useState(''); - const [message, setMessage] = React.useState(''); + const [password, setPassword] = React.useState(''); + const [email, setEmail] = React.useState(''); + const [message, setMessage] = React.useState(''); - const navigateToParent = useBreadcrumb(); - const navigateToRegistration = useBreadcrumb('register'); - const emailRef = React.useRef(null); + const navigateToParent = useBreadcrumb(); + const navigateToRegistration = useBreadcrumb('register'); + const emailRef = React.useRef(null); - useEffect(() => { - emailRef.current?.focus(); - }, []); + useEffect(() => { + emailRef.current?.focus(); + }, []); - async function login(event: React.FormEvent) { - event.preventDefault(); - try { - props.setUser(await pizzaService.login(email, password)); - navigateToParent(); - } catch (error) { - displayMessage(JSON.stringify(error)); + async function login(event: React.FormEvent) { + event.preventDefault(); + try { + props.setUser(await pizzaService.login(email, password)); + navigateToParent(); + } catch (error) { + console.log(JSON.stringify(error)); + if (JSON.stringify(error).includes('401')) { + displayMessage('Invalid email or password'); + } else { + displayMessage(JSON.stringify(error)); + } + } } - } - function displayMessage(msg: string) { - setMessage(msg); - } + function displayMessage(msg: string) { + setMessage(msg); + } - return ( - -
    -
    {message}
    + return ( + +
    +
    {message}
    -
    -
    -
    - -
    - setEmail(e.target.value)} - id="email" - className="py-3 ps-11 pe-4 block w-full bg-white/10 border-white/20 text-white placeholder:text-white rounded-lg text-sm focus:border-white/30 focus:ring-white/30 sm:p-4 sm:ps-11" - placeholder="Email address" - /> -
    - -
    -
    -
    -
    - -
    - setPassword(e.target.value)} - className="py-3 ps-11 pe-4 block w-full bg-white/10 border-white/20 text-white placeholder:text-white rounded-lg text-sm focus:border-white/30 focus:ring-white/30 sm:p-4 sm:ps-11" - placeholder="Password" - /> - + +
    +
    + +
    + setEmail(e.target.value)} + id="email" + className="py-3 ps-11 pe-4 block w-full bg-white/10 border-white/20 text-white placeholder:text-white rounded-lg text-sm focus:border-white/30 focus:ring-white/30 sm:p-4 sm:ps-11" + placeholder="Email address" + /> +
    + +
    +
    +
    +
    + +
    + setPassword(e.target.value)} + className="py-3 ps-11 pe-4 block w-full bg-white/10 border-white/20 text-white placeholder:text-white rounded-lg text-sm focus:border-white/30 focus:ring-white/30 sm:p-4 sm:ps-11" + placeholder="Password" + /> + -
    - -
    -
    -
    +
    + +
    +
    +
    -
    -
    -
    - Are you new?{' '} - - Register - {' '} - instead. +
    +
    +
    + Are you new?{' '} + + Register + {' '} + instead. +
    +
    +
    -
    - -
    -
    - ); + + ); } diff --git a/src/views/logout.tsx b/src/views/logout.tsx index e2429c306..ecdbcc025 100644 --- a/src/views/logout.tsx +++ b/src/views/logout.tsx @@ -5,21 +5,21 @@ import View from './view'; import { User } from '../service/pizzaService'; interface Props { - setUser: (user: User | null) => void; + setUser: (user: User | null) => void; } export default function Logout(props: Props) { - const navigate = useNavigate(); + const navigate = useNavigate(); - React.useEffect(() => { - pizzaService.logout(); - props.setUser(null); - navigate('/'); - }, []); + React.useEffect(() => { + pizzaService.logout(); + props.setUser(null); + navigate('/'); + }, []); - return ( - -
    Logging out ...
    -
    - ); + return ( + +
    Logging out ...
    +
    + ); } diff --git a/src/views/menu.tsx b/src/views/menu.tsx index 2389bdefe..68347d370 100644 --- a/src/views/menu.tsx +++ b/src/views/menu.tsx @@ -7,76 +7,107 @@ import { useNavigate, useLocation } from 'react-router-dom'; import { Franchise, Menu, Pizza, Store } from '../service/pizzaService'; export default function Menu() { - const [order, setOrder] = useState(useLocation().state?.order || { items: [] }); - const [menu, setMenu] = useState([]); - const [storeMap, setStoreMap] = useState<{ [key: string]: { store: Store; franchise: Franchise } }>({}); - const [selectedStore, setSelectedStore] = useState(order.storeId || ''); - const navigate = useNavigate(); + const [order, setOrder] = useState(useLocation().state?.order || { items: [] }); + const [menu, setMenu] = useState([]); + const [storeMap, setStoreMap] = useState<{ + [key: string]: { store: Store; franchise: Franchise }; + }>({}); + const [selectedStore, setSelectedStore] = useState(order.storeId || ''); + const navigate = useNavigate(); - useEffect(() => { - (async () => { - const menu = await pizzaService.getMenu(); - setMenu(menu); - const franchiseList = await pizzaService.getFranchises(0, 20, '*'); - const newStoreMap: { [key: string]: { store: Store; franchise: Franchise } } = {}; - franchiseList.franchises.forEach((franchise) => franchise.stores.forEach((store) => (newStoreMap[store.id] = { store, franchise }))); - setStoreMap(newStoreMap); - })(); - }, []); + useEffect(() => { + (async () => { + const menu = await pizzaService.getMenu(); + setMenu(menu); + const franchiseList = await pizzaService.getFranchises(0, 20, '*'); + const newStoreMap: { [key: string]: { store: Store; franchise: Franchise } } = {}; + franchiseList.franchises.forEach((franchise) => + franchise.stores.forEach((store) => (newStoreMap[store.id] = { store, franchise })), + ); + setStoreMap(newStoreMap); + })(); + }, []); - function selectPizza(pizza: Pizza) { - setOrder({ items: [...order.items, { menuId: pizza.id, description: pizza.title, price: pizza.price }] }); - } + function selectPizza(pizza: Pizza) { + setOrder({ + items: [ + ...order.items, + { menuId: pizza.id, description: pizza.title, price: pizza.price }, + ], + }); + } - function checkout(event: React.FormEvent) { - event.preventDefault(); - if (selectedStore && order.items.length > 0) { - order.storeId = selectedStore; - order.franchiseId = storeMap[selectedStore].franchise.id; - navigate('/payment', { state: { order: order } }); + function checkout(event: React.FormEvent) { + event.preventDefault(); + if (selectedStore && order.items.length > 0) { + order.storeId = selectedStore; + order.franchiseId = storeMap[selectedStore].franchise.id; + navigate('/payment', { state: { order: order } }); + } } - } - return ( - -
    -
    -
    Pick your store and pizzas from below. Remember to order extra for a midnight party.
    + return ( + + +
    +
    + Pick your store and pizzas from below. Remember to order extra for a + midnight party. +
    -
    - -
    +
    + +
    -
    {order.items.length > 0 ? 'Selected pizzas: ' + order.items.length : 'What are you waiting for? Pick a store and then add some pizzas!'}
    - - ))} -
    -
    -
    -
    - ); +
    + {menu.map((pizza) => ( + + ))} +
    +
    + +
    + ); } diff --git a/src/views/notFound.tsx b/src/views/notFound.tsx index 4a2ab905f..ec1cd57c7 100644 --- a/src/views/notFound.tsx +++ b/src/views/notFound.tsx @@ -2,9 +2,11 @@ import React from 'react'; import View from './view'; export default function NotFound() { - return ( - -
    It looks like we have dropped a pizza on the floor. Please try another page.
    -
    - ); + return ( + +
    + It looks like we have dropped a pizza on the floor. Please try another page. +
    +
    + ); } diff --git a/src/views/payment.tsx b/src/views/payment.tsx index 072083b4f..0f67411ba 100644 --- a/src/views/payment.tsx +++ b/src/views/payment.tsx @@ -6,87 +6,115 @@ import { pizzaService } from '../service/service'; import { Order, OrderItem } from '../service/pizzaService'; export default function Payment() { - const [errMessage, setErrorMessage] = React.useState(''); - const location = useLocation(); - const order: Order = location.state?.order || { items: [] }; - const navigate = useNavigate(); + const [errMessage, setErrorMessage] = React.useState(''); + const location = useLocation(); + const order: Order = location.state?.order || { items: [] }; + const navigate = useNavigate(); - React.useEffect(() => { - (async () => { - const user = await pizzaService.getUser(); - if (!user) { - const loginPath = location.pathname + '/login'; - navigate(loginPath, { state: location.state }); - } - })(); - }, []); + React.useEffect(() => { + (async () => { + const user = await pizzaService.getUser(); + if (!user) { + const loginPath = location.pathname + '/login'; + navigate(loginPath, { state: location.state }); + } + })(); + }, []); - async function processPayment() { - try { - const confirmation = await pizzaService.order(order); - navigate('/delivery', { state: { order: confirmation.order, jwt: confirmation.jwt } }); - } catch (err: any) { - setErrorMessage(err.message); + async function processPayment() { + try { + const confirmation = await pizzaService.order(order); + navigate('/delivery', { state: { order: confirmation.order, jwt: confirmation.jwt } }); + } catch (err: any) { + setErrorMessage(err.message); + } } - } - function cancel() { - navigate('/menu', { state: { order: order } }); - } + function cancel() { + navigate('/menu', { state: { order: order } }); + } - return ( - -
    - {errMessage &&
    ⚠️ {errMessage}
    } - {!errMessage && order.items.length === 1 &&
    Send me that pizza right now!
    } - {!errMessage && order.items.length > 1 && ( -
    Send me those {order.items.length} pizzas right now!
    - )} -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - {order.items.map((item: OrderItem, index: number) => ( - - - - - ))} - - - - - - - -
    - Pie - - Price -
    {item.description}{item.price.toLocaleString()} ₿
    - {order.items.length} pie{order.items.length > 1 ? 's' : ''} - - {order.items.reduce((a: any, c: any) => a + c.price, 0).toLocaleString()} ₿ -
    + return ( + +
    + {errMessage && ( +
    + ⚠️ {errMessage} +
    + )} + {!errMessage && order.items.length === 1 && ( +
    + Send me that pizza right now! +
    + )} + {!errMessage && order.items.length > 1 && ( +
    + Send me those {order.items.length} pizzas right now! +
    + )} +
    +
    +
    +
    +
    +
    +
    + + + + + + + + + {order.items.map((item: OrderItem, index: number) => ( + + + + + ))} + + + + + + + +
    + Pie + + Price +
    + {item.description} + + {item.price.toLocaleString()} ₿ +
    + {order.items.length} pie + {order.items.length > 1 ? 's' : ''} + + {order.items + .reduce((a: any, c: any) => a + c.price, 0) + .toLocaleString()}{' '} + ₿ +
    +
    +
    +
    +
    -
    -
    -
    -
    - - ); + + ); } diff --git a/src/views/register.tsx b/src/views/register.tsx index 3d7cd63d0..b0775dd7d 100644 --- a/src/views/register.tsx +++ b/src/views/register.tsx @@ -7,118 +7,125 @@ import View from './view'; import { User } from '../service/pizzaService'; interface Props { - setUser: (user: User) => void; + setUser: (user: User) => void; } export default function Register(props: Props) { - const [name, setName] = React.useState(''); - const [password, setPassword] = React.useState(''); - const [email, setEmail] = React.useState(''); - const [message, setMessage] = React.useState(''); + const [name, setName] = React.useState(''); + const [password, setPassword] = React.useState(''); + const [email, setEmail] = React.useState(''); + const [message, setMessage] = React.useState(''); - const navigateToParentPath = useBreadcrumb(); - const navigateToLogin = useBreadcrumb('login'); - const nameRef = React.useRef(null); + const navigateToParentPath = useBreadcrumb(); + const navigateToLogin = useBreadcrumb('login'); + const nameRef = React.useRef(null); - useEffect(() => { - nameRef.current?.focus(); - }, []); + useEffect(() => { + nameRef.current?.focus(); + }, []); - async function register(event: React.FormEvent) { - event.preventDefault(); - try { - props.setUser(await pizzaService.register(name, email, password)); - navigateToParentPath(); - } catch (error) { - displayMessage(JSON.stringify(error)); + async function register(event: React.FormEvent) { + event.preventDefault(); + try { + props.setUser(await pizzaService.register(name, email, password)); + navigateToParentPath(); + } catch (error) { + displayMessage(JSON.stringify(error)); + } } - } - function displayMessage(msg: string) { - setMessage(msg); - } + function displayMessage(msg: string) { + setMessage(msg); + } - return ( - -
    -
    {message}
    + return ( + +
    +
    {message}
    -
    -
    -
    - -
    - setName(e.target.value)} - id="name" - className="py-3 ps-11 pe-4 block w-full bg-white/10 border-white/20 text-white placeholder:text-white rounded-lg text-sm focus:border-white/30 focus:ring-white/30 sm:p-4 sm:ps-11" - placeholder="Full name" - /> -
    - -
    -
    -
    -
    - setEmail(e.target.value)} - id="email" - className="py-3 ps-11 pe-4 block w-full bg-white/10 border-white/20 text-white placeholder:text-white rounded-lg text-sm focus:border-white/30 focus:ring-white/30 sm:p-4 sm:ps-11" - placeholder="Email address" - /> -
    - -
    -
    -
    - -
    - setPassword(e.target.value)} - className="py-3 ps-11 pe-4 block w-full bg-white/10 border-white/20 text-white placeholder:text-white rounded-lg text-sm focus:border-white/30 focus:ring-white/30 sm:p-4 sm:ps-11" - placeholder="Password" - /> - + +
    +
    + +
    + setName(e.target.value)} + id="name" + className="py-3 ps-11 pe-4 block w-full bg-white/10 border-white/20 text-white placeholder:text-white rounded-lg text-sm focus:border-white/30 focus:ring-white/30 sm:p-4 sm:ps-11" + placeholder="Full name" + /> +
    + +
    +
    +
    +
    + setEmail(e.target.value)} + id="email" + className="py-3 ps-11 pe-4 block w-full bg-white/10 border-white/20 text-white placeholder:text-white rounded-lg text-sm focus:border-white/30 focus:ring-white/30 sm:p-4 sm:ps-11" + placeholder="Email address" + /> +
    + +
    +
    +
    + +
    + setPassword(e.target.value)} + className="py-3 ps-11 pe-4 block w-full bg-white/10 border-white/20 text-white placeholder:text-white rounded-lg text-sm focus:border-white/30 focus:ring-white/30 sm:p-4 sm:ps-11" + placeholder="Password" + /> + -
    - -
    -
    -
    +
    + +
    +
    +
    -
    -
    -
    - Already have an account?{' '} - - Login - {' '} - instead. +
    +
    +
    + Already have an account?{' '} + + Login + {' '} + instead. +
    +
    +
    -
    - -
    -
    - ); + + ); } diff --git a/src/views/view.tsx b/src/views/view.tsx index 2f1abeb8b..ef884cc1b 100644 --- a/src/views/view.tsx +++ b/src/views/view.tsx @@ -1,24 +1,26 @@ import React from 'react'; interface Props { - title: string; - children: React.ReactNode; + title: string; + children: React.ReactNode; } export default function View(props: Props) { - return ( - <> -
    -
    -
    -

    - {props.title} -

    -
    + return ( + <> +
    +
    +
    +

    + + {props.title} + +

    +
    - {props.children} -
    -
    - - ); + {props.children} +
    +
    + + ); } diff --git a/tests/adminFunctions.spec.ts b/tests/adminFunctions.spec.ts new file mode 100644 index 000000000..8ebab8d18 --- /dev/null +++ b/tests/adminFunctions.spec.ts @@ -0,0 +1,157 @@ +import { test, expect } from 'playwright-test-coverage'; +import { Page } from '@playwright/test'; +import { basicInit } from './helpers'; + +async function loginAsAdminAndOpenDashboard(page: Page) { + await page.getByRole('link', { name: 'Login' }).click(); + await page.getByRole('textbox', { name: 'Email address' }).fill('a@jwt.com'); + await page.getByRole('textbox', { name: 'Password' }).fill('admin'); + await page.getByRole('button', { name: 'Login' }).click(); + await page.getByRole('link', { name: 'Admin' }).click(); +} + +test('create and close franchise', async ({ page }) => { + await basicInit(page); + + await loginAsAdminAndOpenDashboard(page); + + await expect(page.getByText("Mama Ricci's kitchen")).toBeVisible(); + await expect(page.getByText('LotaPizza')).toBeVisible(); + await expect(page.getByRole('cell', { name: 'Lehi' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'Springville' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'American Fork' })).toBeVisible(); + + // Create franchise + await page.getByRole('button', { name: 'Add Franchise' }).click(); + await page.getByRole('textbox', { name: 'franchise name' }).click(); + await page.getByRole('textbox', { name: 'franchise name' }).fill('newFranchise'); + await page.getByRole('textbox', { name: 'franchisee admin email' }).click(); + await page.getByRole('textbox', { name: 'franchisee admin email' }).fill('a@jwt.com'); + await page.getByRole('button', { name: 'Create' }).click(); + await Promise.all([ + page.waitForRequest((r) => r.url().includes('/api/franchise') && r.method() === 'GET'), + ]); + await expect(page.getByRole('cell', { name: 'newFranchise' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'Pizza Admin' })).toBeVisible(); + await page + .getByRole('row', { name: 'newFranchise Pizza Admin Close' }) + .getByRole('button') + .click(); + await expect(page.getByText('Sorry to see you go')).toBeVisible(); + await expect(page.getByText('newFranchise')).toBeVisible(); + await page.getByRole('button', { name: 'Close' }).click(); + await expect(page.getByRole('cell', { name: 'LotaPizza' })).toBeVisible(); +}); + +test('create and close store as admin', async ({ page }) => { + await basicInit(page); + + await loginAsAdminAndOpenDashboard(page); + + // Close Spanish Fork store + await page.getByRole('row', { name: 'Lehi 450 ₿ Close' }).getByRole('button').click(); + await expect(page.getByRole('main')).toContainText( + 'Are you sure you want to close the LotaPizza store Lehi ? This cannot be restored. All outstanding revenue will not be refunded.', + ); + await page.getByRole('button', { name: 'Close' }).click(); + await expect(page.getByRole('cell', { name: 'Lehi' })).toHaveCount(0); +}); + +test('admin can switch between franchises and users lists', async ({ page }) => { + await basicInit(page); + await loginAsAdminAndOpenDashboard(page); + + await expect(page.getByRole('button', { name: 'Franchises' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'LotaPizza' })).toBeVisible(); + + await page.getByRole('button', { name: 'Users' }).click(); + await expect(page.getByRole('heading', { name: 'Users' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'Pizza Admin' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'a@jwt.com' })).toBeVisible(); + await expect(page.getByRole('button', { name: 'Add Franchise' })).toHaveCount(0); + + await page.getByRole('button', { name: 'Franchises' }).click(); + await expect(page.getByRole('cell', { name: 'LotaPizza' })).toBeVisible(); + await expect(page.getByRole('button', { name: 'Add Franchise' })).toBeVisible(); +}); + +test('admin can filter users list by name and email', async ({ page }) => { + await basicInit(page); + await loginAsAdminAndOpenDashboard(page); + + await page.getByRole('button', { name: 'Users' }).click(); + await expect(page.getByRole('cell', { name: 'Pizza Admin' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'Pizza Diner' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'Pizza Franchisee' })).toBeVisible(); + + await page.getByPlaceholder('Filter users').fill('franchisee'); + await page.getByRole('button', { name: 'Submit' }).click(); + await expect(page.getByRole('cell', { name: 'Pizza Franchisee' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'Pizza Admin' })).toHaveCount(0); + + await page.getByPlaceholder('Filter users').fill('a@jwt.com'); + await page.getByRole('button', { name: 'Submit' }).click(); + const adminEmailCells = await page.getByRole('cell', { name: 'a@jwt.com' }).count(); + expect(adminEmailCells).toBeGreaterThanOrEqual(1); + await expect(page.getByRole('cell', { name: 'f@jwt.com' })).toHaveCount(0); +}); + +test('users list has no close actions and list filter resets when switching back', async ({ + page, +}) => { + await basicInit(page); + await loginAsAdminAndOpenDashboard(page); + + await page.getByRole('button', { name: 'Users' }).click(); + await expect(page.getByRole('button', { name: 'Close' })).toHaveCount(0); + await expect(page.getByRole('button', { name: 'Add Franchise' })).toHaveCount(0); + + await page.getByPlaceholder('Filter users').fill('franchisee'); + await page.getByRole('button', { name: 'Submit' }).click(); + await expect(page.getByRole('cell', { name: 'Pizza Franchisee' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'Pizza Admin' })).toHaveCount(0); + + await page.getByRole('button', { name: 'Franchises' }).click(); + await expect(page.getByPlaceholder('Filter franchises')).toHaveValue(''); + await expect(page.getByRole('cell', { name: 'LotaPizza' })).toBeVisible(); +}); + +test('admin can delete a user from users list', async ({ page }) => { + await basicInit(page); + await loginAsAdminAndOpenDashboard(page); + + await page.getByRole('button', { name: 'Users' }).click(); + await expect(page.getByRole('cell', { name: 'Pizza Diner' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'd@jwt.com' })).toBeVisible(); + + await page + .getByRole('row', { name: 'Pizza Diner d@jwt.com Delete' }) + .getByRole('button', { name: 'Delete' }) + .click(); + await expect(page.getByText('Are you sure you want to delete user')).toBeVisible(); + await expect(page.getByText('Pizza Diner')).toBeVisible(); + await page.getByRole('button', { name: 'Delete' }).click(); + + await page.getByRole('button', { name: 'Users' }).click(); + await expect(page.getByRole('cell', { name: 'Pizza Diner' })).toHaveCount(0); + await expect(page.getByRole('cell', { name: 'd@jwt.com' })).toHaveCount(0); + await expect(page.getByRole('cell', { name: 'Pizza Admin' })).toBeVisible(); +}); + +test('admin can cancel delete user and keep user in list', async ({ page }) => { + await basicInit(page); + await loginAsAdminAndOpenDashboard(page); + + await page.getByRole('button', { name: 'Users' }).click(); + await page + .getByRole('row', { name: 'Pizza Diner d@jwt.com Delete' }) + .getByRole('button', { name: 'Delete' }) + .click(); + + await expect(page.getByText('Are you sure you want to delete user')).toBeVisible(); + await page.getByRole('button', { name: 'Cancel' }).click(); + + await page.getByRole('button', { name: 'Users' }).click(); + await expect(page.getByRole('cell', { name: 'Pizza Diner' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'd@jwt.com' })).toBeVisible(); +}); diff --git a/tests/auth.spec.ts b/tests/auth.spec.ts new file mode 100644 index 000000000..6fc44b89b --- /dev/null +++ b/tests/auth.spec.ts @@ -0,0 +1,68 @@ +import { test, expect } from 'playwright-test-coverage'; +import { basicInit } from './helpers'; + +test('login and logout', async ({ page }) => { + await basicInit(page); + + // login, check logged in + await page.getByRole('link', { name: 'Login' }).click(); + await page.getByRole('textbox', { name: 'Email address' }).fill('d@jwt.com'); + await page.getByRole('textbox', { name: 'Password' }).fill('a'); + await page.getByRole('button', { name: 'Login' }).click(); + + await expect(page.getByRole('link', { name: 'PD' })).toBeVisible(); + + // Logout, check logged out + await page.getByRole('link', { name: 'Logout' }).click(); + await expect(page.getByLabel('Global')).toContainText('JWT PizzaOrderFranchiseLoginRegister'); +}); + +test('purchase with login', async ({ page }) => { + await basicInit(page); + + // Go to order page + await page.getByRole('button', { name: 'Order now' }).click(); + + // Create order + await expect(page.locator('h2')).toContainText('Awesome is a click away'); + await page.getByRole('combobox').selectOption('4'); + await page.getByRole('link', { name: 'Image Description Veggie A' }).click(); + await page.getByRole('link', { name: 'Image Description Pepperoni' }).click(); + await expect(page.locator('form')).toContainText('Selected pizzas: 2'); + await page.getByRole('button', { name: 'Checkout' }).click(); + + // Login + await page.getByPlaceholder('Email address').click(); + await page.getByPlaceholder('Email address').fill('d@jwt.com'); + await page.getByPlaceholder('Email address').press('Tab'); + await page.getByPlaceholder('Password').fill('a'); + await page.getByRole('button', { name: 'Login' }).click(); + + // Pay + await expect(page.getByRole('main')).toContainText('Send me those 2 pizzas right now!'); + await expect(page.locator('tbody')).toContainText('Veggie'); + await expect(page.locator('tbody')).toContainText('Pepperoni'); + await expect(page.locator('tfoot')).toContainText('0.008 ₿'); + await page.getByRole('button', { name: 'Pay now' }).click(); + + // Check balance + await expect(page.getByText('0.008')).toBeVisible(); +}); + +test('register', async ({ page }) => { + await basicInit(page); + + // Register new user + await page.getByRole('link', { name: 'Register' }).click(); + await expect(page.getByText('Welcome to the party')).toBeVisible(); + await page.getByPlaceholder('Full name').fill('New Name'); + await page.getByPlaceholder('Email address').fill('newEmail@test.com'); + await page.getByPlaceholder('Password').fill('newPassword'); + await page.getByRole('button', { name: 'Register' }).click(); + + // Diner Dashboard + await page.getByRole('link', { name: 'NN' }).click(); + await expect(page.getByText('New Name')).toBeVisible(); + await expect(page.getByText('newEmail@test.com')).toBeVisible(); + await expect(page.getByText('diner', { exact: true })).toBeVisible(); +}); diff --git a/tests/franchiseeFunctions.spec.ts b/tests/franchiseeFunctions.spec.ts new file mode 100644 index 000000000..a4f5097bf --- /dev/null +++ b/tests/franchiseeFunctions.spec.ts @@ -0,0 +1,32 @@ +import { test, expect } from 'playwright-test-coverage'; +import { basicInit } from './helpers'; + +test('create and close store as franchisee', async ({ page }) => { + await basicInit(page); + + await page.getByRole('link', { name: 'Login' }).click(); + await page.getByRole('textbox', { name: 'Email address' }).fill('f@jwt.com'); + await page.getByRole('textbox', { name: 'Password' }).fill('f'); + await page.getByRole('button', { name: 'Login' }).click(); + await page + .getByRole('navigation', { name: 'Global' }) + .getByRole('link', { name: 'Franchise' }) + .click(); + await expect(page.getByText('LotaPizza')).toBeVisible(); + await expect(page.getByText('Lehi')).toBeVisible(); + + // Create and close new store + await page.getByRole('button', { name: 'Create store' }).click(); + await expect(page.getByText('Create store')).toBeVisible(); + await page.getByRole('textbox', { name: 'store name' }).fill('newStore'); + await page.getByRole('button', { name: 'Create' }).click(); + await Promise.all([page.waitForResponse((r) => r.url().includes('/api/franchise'))]); + await expect(page.locator('tbody')).toContainText('newStore'); + await page.getByRole('row', { name: 'newStore 0 ₿ Close' }).getByRole('button').click(); + await expect(page.getByRole('main')).toContainText( + 'Are you sure you want to close the LotaPizza store newStore ? This cannot be restored. All outstanding revenue will not be refunded.', + ); + await page.getByRole('button', { name: 'Close' }).click(); + await Promise.all([page.waitForResponse((r) => r.url().includes('/api/franchise'))]); + await expect(page.locator('tbody')).not.toContainText('newStore'); +}); diff --git a/tests/helpers.ts b/tests/helpers.ts new file mode 100644 index 000000000..bb4fa3af0 --- /dev/null +++ b/tests/helpers.ts @@ -0,0 +1,305 @@ +import { Page } from '@playwright/test'; +import { Role, User } from '../src/service/pizzaService'; +import { expect } from 'playwright-test-coverage'; + +async function basicInit(page: Page) { + let loggedInUser: User | undefined; + + await page.addInitScript(() => { + localStorage.clear(); + sessionStorage.clear(); + }); + const validUsers: Record = { + 'd@jwt.com': { + id: '3', + name: 'Pizza Diner', + email: 'd@jwt.com', + password: 'a', + roles: [{ role: Role.Diner }], + }, + 'f@jwt.com': { + id: '4', + name: 'Pizza Franchisee', + email: 'f@jwt.com', + password: 'f', + roles: [{ role: Role.Franchisee }], + }, + 'a@jwt.com': { + id: '5', + name: 'Pizza Admin', + email: 'a@jwt.com', + password: 'admin', + roles: [{ role: Role.Admin }], + }, + }; + + let validFranchises: { + id: string; + name: string; + stores: { id: string; name: string; franchiseId: string; totalRevenue?: number }[]; + admins: User[]; + }[] = [ + { + id: '2', + name: 'LotaPizza', + stores: [ + { id: '4', name: 'Lehi', franchiseId: '2', totalRevenue: 450 }, + { id: '5', name: 'Springville', franchiseId: '2', totalRevenue: 5 }, + { id: '6', name: 'American Fork', franchiseId: '2', totalRevenue: 890 }, + ], + admins: [{ id: '4', name: 'Pizza Franchisee', email: 'f@jwt.com' }], + }, + { + id: '3', + name: 'PizzaCorp', + stores: [{ id: '7', name: 'Spanish Fork', franchiseId: '3', totalRevenue: 0 }], + admins: [], + }, + { id: '4', name: 'topSpot', stores: [], admins: [] }, + ]; + + // Authorize login for the given user + await page.route('*/**/api/auth', async (route) => { + if (route.request().method() === 'DELETE') { + loggedInUser = undefined; + await route.fulfill({ json: { message: 'logged out successfully' } }); + return; + } + const req = route.request().postDataJSON(); + if (route.request().method() === 'PUT') { + const user = validUsers[req.email]; + if (!user || user.password !== req.password) { + await route.fulfill({ status: 401, json: { error: 'Unauthorized' } }); + return; + } + loggedInUser = { ...validUsers[req.email] }; + delete loggedInUser.password; + const loginRes = { + user: loggedInUser, + token: 'abcdef', + }; + expect(route.request().method()).toBe('PUT'); + await route.fulfill({ json: loginRes }); + return; + } + const newUser = { ...req, id: '15', roles: [{ role: Role.Diner }] }; + validUsers[req.email] = { ...newUser }; + const userResponse = { ...newUser }; + delete userResponse.password; + loggedInUser = userResponse; + await route.fulfill({ json: { user: userResponse, token: 'abcdef' } }); + }); + + // Return the currently logged in user + await page.route('*/**/api/user/me', async (route) => { + expect(route.request().method()).toBe('GET'); + await route.fulfill({ json: loggedInUser }); + }); + + // List users + await page.route(/\/api\/user(\?.*)$/, async (route) => { + expect(route.request().method()).toBe('GET'); + const url = new URL(route.request().url()); + const nameFilter = (url.searchParams.get('name') || '*').replace(/\*/g, '').toLowerCase(); + + const users = Object.values(validUsers) + .filter((user) => { + if (!nameFilter) { + return true; + } + const userName = (user.name || '').toLowerCase(); + const userEmail = (user.email || '').toLowerCase(); + return userName.includes(nameFilter) || userEmail.includes(nameFilter); + }) + .map((user) => { + const responseUser = { ...user }; + delete responseUser.password; + return responseUser; + }); + + await route.fulfill({ json: { users, more: false } }); + }); + + // Update a user + await page.route(/\/api\/user\/(\d+)$/, async (route) => { + const method = route.request().method(); + const userId = route.request().url().split('/').pop(); + + if (!userId) { + await route.fulfill({ status: 400, json: { error: 'missing user id' } }); + return; + } + + const currentEntry = Object.entries(validUsers).find(([, user]) => user.id === userId); + if (!currentEntry) { + await route.fulfill({ status: 404, json: { error: 'user not found' } }); + return; + } + + if (method === 'DELETE') { + const [currentEmail, currentUser] = currentEntry; + delete validUsers[currentEmail]; + if (loggedInUser?.id === currentUser.id) { + loggedInUser = undefined; + } + await route.fulfill({ json: { message: 'user deleted successfully' } }); + return; + } + + expect(method).toBe('PUT'); + const userReq = route.request().postDataJSON(); + const [currentEmail, currentUser] = currentEntry; + const updatedUser: User = { + ...currentUser, + ...userReq, + id: currentUser.id, + roles: userReq.roles || currentUser.roles, + password: userReq.password || currentUser.password, + }; + + if (userReq.email && userReq.email !== currentEmail) { + delete validUsers[currentEmail]; + } + validUsers[updatedUser.email || currentEmail] = updatedUser; + + const userResponse = { ...updatedUser }; + delete userResponse.password; + loggedInUser = userResponse; + await route.fulfill({ json: { user: userResponse, token: 'abcdef' } }); + }); + + // A standard menu + await page.route('*/**/api/order/menu', async (route) => { + const menuRes = [ + { + id: '1', + title: 'Veggie', + image: 'pizza1.png', + price: 0.0038, + description: 'A garden of delight', + }, + { + id: '2', + title: 'Pepperoni', + image: 'pizza2.png', + price: 0.0042, + description: 'Spicy treat', + }, + ]; + expect(route.request().method()).toBe('GET'); + await route.fulfill({ json: menuRes }); + }); + + // Create a franchise + await page.route(/\/api\/franchise$/, async (route) => { + expect(route.request().method()).toBe('POST'); + const franchiseReq = route.request().postDataJSON(); + expect(franchiseReq.admins[0].email).toBe(loggedInUser?.email); + expect(franchiseReq.name).toBe('newFranchise'); + const newFranchise = { + name: franchiseReq.name, + id: '8', + admins: [ + { + name: loggedInUser?.name || '', + email: franchiseReq.admins[0].email, + id: loggedInUser?.id, + }, + ], + }; + validFranchises.push({ ...newFranchise, stores: [] }); + await route.fulfill({ json: newFranchise }); + }); + + // Delete a franchise or get a user's franchises + await page.route(/\/api\/franchise\/(\d+)$/, async (route) => { + const franchiseId = route.request().url().split('/').pop(); + if (franchiseId) { + if (route.request().method() === 'DELETE') { + validFranchises = validFranchises.filter((f) => f.id !== franchiseId); + await route.fulfill({ json: { message: 'franchise deleted' } }); + return; + } else if (route.request().method() === 'GET') { + const userId = franchiseId; + const franchises = validFranchises.filter((f) => + f.admins.find((a) => a.id === userId), + ); + await route.fulfill({ json: franchises }); + return; + } + } else { + await route.fulfill({ status: 400, json: { error: 'missing franchise id' } }); + } + }); + + // Get franchises + await page.route(/\/api\/franchise(\?.*)$/, async (route) => { + const franchiseRes = { + franchises: validFranchises, + }; + expect(route.request().method()).toBe('GET'); + await route.fulfill({ json: franchiseRes }); + }); + + // Order a pizza. + await page.route('*/**/api/order', async (route) => { + if (route.request().method() === 'POST') { + const orderReq = route.request().postDataJSON(); + const orderRes = { + order: { ...orderReq, id: 23 }, + jwt: 'eyJpYXQ', + }; + await route.fulfill({ json: orderRes }); + } + }); + + // Delete and Create store + await page.route(/\/api\/franchise\/(\d+)\/store(\/\d+)?/, async (route) => { + if (route.request().method() === 'DELETE') { + const storeId = route.request().url().split('/').pop(); + const franchiseId = route + .request() + .url() + .match(/\/franchise\/(\d+)\/store/)?.[1]; + if (!storeId || !franchiseId) { + await route.fulfill({ + status: 400, + json: { error: 'missing store or franchise id' }, + }); + return; + } + const franchise = validFranchises.find((f) => f.id === franchiseId); + if (!franchise) { + await route.fulfill({ status: 404, json: { error: 'franchise not found' } }); + return; + } + franchise.stores = franchise.stores.filter((s) => s.id !== storeId); + await route.fulfill({ json: { message: 'store deleted' } }); + return; + } else if (route.request().method() === 'POST') { + const storeReq = route.request().postDataJSON(); + const franchiseId = route + .request() + .url() + .match(/\/franchise\/(\d+)\/store/)?.[1]; + if (!franchiseId) { + await route.fulfill({ status: 400, json: { error: 'missing franchise id' } }); + return; + } + const newStore = { name: storeReq.name, id: '12', franchiseId, totalRevenue: 0 }; + const franchise = validFranchises.find((f) => f.id === franchiseId); + if (!franchise) { + await route.fulfill({ status: 404, json: { error: 'franchise not found' } }); + return; + } + franchise.stores.push(newStore); + await route.fulfill({ + json: { id: newStore.id, name: newStore.name, franchiseId }, + }); + } + }); + + await page.goto('/'); +} + +export { basicInit }; diff --git a/tests/ui.spec.ts b/tests/ui.spec.ts new file mode 100644 index 000000000..62bb4c6cb --- /dev/null +++ b/tests/ui.spec.ts @@ -0,0 +1,18 @@ +import { test, expect } from 'playwright-test-coverage'; + +test('history, about, docs', async ({ page }) => { + await page.goto('/'); + // About + await page.getByRole('link', { name: 'About' }).click(); + await expect(page.getByText('The secret sauce')).toBeVisible(); + await expect(page.getByRole('img').nth(3)).toBeVisible(); + + // History + await page.getByRole('link', { name: 'History' }).click(); + await expect(page.getByText('Mama Rucci, my my')).toBeVisible(); + await expect(page.getByRole('main').getByRole('img')).toBeVisible(); + + // Docs + await page.goto('/docs'); + await expect(page.getByText('JWT Pizza API')).toBeVisible(); +}); diff --git a/tests/user.spec.ts b/tests/user.spec.ts new file mode 100644 index 000000000..9d7fb3daa --- /dev/null +++ b/tests/user.spec.ts @@ -0,0 +1,91 @@ +import { test, expect } from 'playwright-test-coverage'; +import { Page } from '@playwright/test'; +import { basicInit } from './helpers'; + +async function registerDiner(page: Page, email: string, password: string = 'diner') { + await page.goto('/'); + await page.getByRole('link', { name: 'Register' }).click(); + await page.getByRole('textbox', { name: 'Full name' }).fill('pizza diner'); + await page.getByRole('textbox', { name: 'Email address' }).fill(email); + await page.getByRole('textbox', { name: 'Password' }).fill(password); + await page.getByRole('button', { name: 'Register' }).click(); +} + +async function openUserEditModal(page: Page) { + await page.getByRole('link', { name: 'pd' }).click(); + await page.getByRole('button', { name: 'Edit' }).click(); +} + +async function logoutAndOpenLogin(page: Page) { + await page.getByRole('link', { name: 'Logout' }).click(); + await page.getByRole('link', { name: 'Login' }).click(); +} + +test('updateUser', async ({ page }) => { + await basicInit(page); + const email = `user${Math.floor(Math.random() * 10000)}@jwt.com`; + await registerDiner(page, email); + + await openUserEditModal(page); + await page.locator('#hs-jwt-modal input').first().fill('pizza dinerx'); + await page.getByRole('button', { name: 'Update' }).click(); + + await page.waitForSelector('[role="dialog"].hidden', { state: 'attached' }); + + await expect(page.getByRole('main')).toContainText('pizza dinerx'); + + await logoutAndOpenLogin(page); + + await page.getByRole('textbox', { name: 'Email address' }).fill(email); + await page.getByRole('textbox', { name: 'Password' }).fill('diner'); + await page.getByRole('button', { name: 'Login' }).click(); + + await page.getByRole('link', { name: 'pd' }).click(); + + await expect(page.getByRole('main')).toContainText('pizza dinerx'); +}); + +test('updateUser email persists across logout/login', async ({ page }) => { + await basicInit(page); + const email = `user${Math.floor(Math.random() * 10000)}@jwt.com`; + const updatedEmail = `updated${Math.floor(Math.random() * 10000)}@jwt.com`; + await registerDiner(page, email); + + await openUserEditModal(page); + await page.locator('#hs-jwt-modal input').nth(1).fill(updatedEmail); + await page.getByRole('button', { name: 'Update' }).click(); + await page.waitForSelector('[role="dialog"].hidden', { state: 'attached' }); + + await expect(page.getByRole('main')).toContainText(updatedEmail); + + await logoutAndOpenLogin(page); + await page.getByRole('textbox', { name: 'Email address' }).fill(updatedEmail); + await page.getByRole('textbox', { name: 'Password' }).fill('diner'); + await page.getByRole('button', { name: 'Login' }).click(); + await page.getByRole('link', { name: 'pd' }).click(); + + await expect(page.getByRole('main')).toContainText(updatedEmail); +}); + +test('updateUser password invalidates old password', async ({ page }) => { + await basicInit(page); + const email = `user${Math.floor(Math.random() * 10000)}@jwt.com`; + const newPassword = 'newDinerPass'; + await registerDiner(page, email); + + await openUserEditModal(page); + await page.locator('#hs-jwt-modal input').nth(2).fill(newPassword); + await page.getByRole('button', { name: 'Update' }).click(); + await page.waitForSelector('[role="dialog"].hidden', { state: 'attached' }); + + await logoutAndOpenLogin(page); + await page.getByRole('textbox', { name: 'Email address' }).fill(email); + await page.getByRole('textbox', { name: 'Password' }).fill('diner'); + await page.getByRole('button', { name: 'Login' }).click(); + await expect(page.getByText('Invalid email or password')).toBeVisible(); + + await page.getByRole('textbox', { name: 'Password' }).fill(newPassword); + await page.getByRole('button', { name: 'Login' }).click(); + await page.getByRole('link', { name: 'pd' }).click(); + await expect(page.getByRole('main')).toContainText('pizza diner'); +}); diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 000000000..bf46b6ab1 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'vite'; +import istanbul from 'vite-plugin-istanbul'; + +export default defineConfig({ + build: { sourcemap: true }, + plugins: [ + istanbul({ + include: ['src/**/*'], + exclude: ['node_modules'], + requireEnv: false, + }), + ], +}); \ No newline at end of file