From a966185620be334f45beb8d7b3a0dfc6f5889ca9 Mon Sep 17 00:00:00 2001
From: Daniel David
Date: Tue, 10 Mar 2026 10:57:05 +1100
Subject: [PATCH 1/8] feat(docs): add comprehensive project guidelines and
TypeScript instructions
---
.../.copilot-commit-message-instructions.md | 47 ++++++++++++
.github/copilot-instructions.md | 75 +++++++++++++++++++
.github/instructions/client.instructions.md | 43 +++++++++++
.github/instructions/server.instructions.md | 41 ++++++++++
.../instructions/typescript.instructions.md | 27 +++++++
.vscode/settings.json | 7 ++
6 files changed, 240 insertions(+)
create mode 100644 .github/.copilot-commit-message-instructions.md
create mode 100644 .github/copilot-instructions.md
create mode 100644 .github/instructions/client.instructions.md
create mode 100644 .github/instructions/server.instructions.md
create mode 100644 .github/instructions/typescript.instructions.md
create mode 100644 .vscode/settings.json
diff --git a/.github/.copilot-commit-message-instructions.md b/.github/.copilot-commit-message-instructions.md
new file mode 100644
index 0000000..5388958
--- /dev/null
+++ b/.github/.copilot-commit-message-instructions.md
@@ -0,0 +1,47 @@
+# Commit Message Instructions
+
+Follow the **Conventional Commits** specification for every commit.
+
+## Format
+
+```
+():
+
+[optional body]
+
+[optional footer(s)]
+```
+
+## Types
+
+| Type | When to use |
+|---|---|
+| `feat` | New feature or capability |
+| `fix` | Bug fix |
+| `refactor` | Code change that neither fixes a bug nor adds a feature |
+| `test` | Adding or updating tests |
+| `chore` | Tooling, deps, config changes with no production code change |
+| `docs` | Documentation only |
+| `perf` | Performance improvement |
+| `ci` | CI/CD pipeline changes |
+
+## Scope (optional)
+
+Use the package or layer name: `client`, `server`, `auth`, `middleware`, `routes`, `config`, `docker`.
+
+## Rules
+
+- Summary line: imperative mood, lowercase, no trailing period, max 72 chars.
+- Body: wrap at 72 chars; explain *why*, not *what*.
+- Reference issues/PRs in the footer: `Closes #123`, `Refs #456`.
+- Breaking changes: add `BREAKING CHANGE:` footer or append `!` after the type/scope.
+
+## Examples
+
+```
+feat(server): add PUT /api/me endpoint for profile updates
+fix(auth): handle token refresh race condition in AuthContext
+chore(deps): bump firebase-admin to 13.0.0
+test(server): add e2e coverage for DELETE /api/me
+refactor(client): extract axios token interceptor into api/axios.ts
+```
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
new file mode 100644
index 0000000..ac6a63c
--- /dev/null
+++ b/.github/copilot-instructions.md
@@ -0,0 +1,75 @@
+# Project Guidelines
+
+## Project Overview
+
+TypeScript monorepo (npm workspaces) with a **React 18 + Vite** single-page app (`packages/client`) and an **Express** REST API (`packages/server`). Firebase Authentication (Google Sign-In) handles identity; the server validates Firebase ID tokens on every protected route via firebase-admin.
+
+## Architecture
+
+```
+packages/
+ client/ React SPA — Firebase client SDK, React Query, axios, Tailwind
+ server/ Express API — firebase-admin token verification, layered architecture
+```
+
+**Server layers** (strictly top-down, each layer imports only the one below):
+- `routes/` → `controllers/` → `services/` → `repositories/`
+- `repositories/userRepository.ts` wraps all firebase-admin calls — mock this in tests, never firebase-admin directly.
+- Middleware order in `app.ts`: `security (helmet)` → `logging (morgan)` → `rateLimiting` → `cors` → `express.json()` → `routes` → `errorHandler` (always last).
+
+**Client layers:**
+- `features//` — UI components scoped to a feature (auth, login, dashboard, common).
+- `api/services/` — raw axios calls; `api/hooks.ts` — React Query wrappers that resolve the Bearer token before calling a service.
+- `features/auth/AuthContext.tsx` — single source of auth state (`user`, `loading`, `getIdToken`).
+
+## Build and Test
+
+```bash
+# Install (run from repo root)
+npm install
+
+# Dev (starts client :5173 and server :3001 concurrently)
+npm run dev
+
+# Build both packages
+npm run build
+
+# Lint all packages
+npm run lint
+
+# Test client
+npm run test --workspace=packages/client
+
+# Test server
+npm run test --workspace=packages/server
+```
+
+## Code Style
+
+- **TypeScript strict mode** throughout; no `any` unless unavoidable — use `unknown` + type-guard instead.
+- `async/await` everywhere; never mix `.then()` chains. Always wrap async route handlers / service calls in `try/catch` and forward errors with `next(err)` on the server side.
+- Prefer named exports for components and functions; default export only for page-level React components.
+- Use `zod` for all runtime input/environment validation (see `packages/server/src/config.ts` for the pattern).
+- **Conventional Commits**: `feat:`, `fix:`, `chore:`, `test:`, `docs:`, `refactor:`.
+
+## Project Conventions
+
+- **Auth in API calls**: obtain the token via `useAuth().getIdToken()` inside a React Query `queryFn`, not at component level. See `packages/client/src/api/hooks.ts → useMe`.
+- **Firebase Admin mocking**: in server tests always mock `../../src/firebase` and `../../src/repositories/userRepository` before importing `createApp`. See `packages/server/tests/e2e/userRoutes.test.ts`.
+- **Config** is validated once at startup via `packages/server/src/config.ts`; access runtime config only through the exported `config` object, never `process.env` directly in feature code.
+- **CORS origin** is driven by `CORS_ORIGIN` env var (default `http://localhost:5173`); never hard-code origins.
+- **Rate limiting** is global on the Express app; if adding stricter per-route limits, apply them at the router level before the global one.
+- **Error responses**: always `res.status(xxx).json({ error: 'message' })` — no custom error shape per route.
+- **Route mounting**: all routes live under `/api` in `routes/index.ts`; add a new feature router there.
+
+## Integration Points
+
+- `packages/client/vite.config.ts` proxies `/api/*` → `http://localhost:3001` in dev; the same path hits the real Express server in production (nginx reverse-proxy or equivalent).
+- Firebase credentials: client reads `VITE_FIREBASE_*` env vars; server reads `FIREBASE_SERVICE_ACCOUNT_JSON` (preferred) or individual `FIREBASE_PROJECT_ID / FIREBASE_CLIENT_EMAIL / FIREBASE_PRIVATE_KEY` vars.
+
+## Security
+
+- Never log the raw `Authorization` header or Firebase ID tokens.
+- Server-side — all protected routes must use `authMiddleware` before any controller; `req.user` is a `DecodedIdToken` set by that middleware.
+- Do not expose the firebase-admin service account JSON in client bundles or version control.
+- `helmet()` is already applied globally; do not remove or override its defaults without justification.
diff --git a/.github/instructions/client.instructions.md b/.github/instructions/client.instructions.md
new file mode 100644
index 0000000..3266905
--- /dev/null
+++ b/.github/instructions/client.instructions.md
@@ -0,0 +1,43 @@
+---
+applyTo: "packages/client/**"
+---
+
+# Client Conventions
+
+## Feature Structure
+
+Each feature lives in `packages/client/src/features//`. Add new UI concerns as a new folder here; do not scatter components in `src/` root.
+
+Current features: `auth/`, `common/`, `dashboard/`, `login/`.
+
+## Auth
+
+- Single source of truth: `features/auth/AuthContext.tsx` — exposes `user`, `loading`, `signInWithGoogle`, `signOut`, `getIdToken`.
+- Use `useAuth()` to access auth state; throw/redirect if used outside ``.
+- Gate every private page with the `` wrapper (see `features/common/ProtectedRoute.tsx`).
+- Obtain Firebase ID tokens inside a React Query `queryFn`, never at component render time.
+
+## API Layer
+
+- Raw axios calls belong in `api/services/Service.ts`.
+- React Query wrappers belong in `api/hooks.ts`; fetch the token there before calling the service.
+- The axios instance (`api/axios.ts`) has `baseURL: '/api'`; never hard-code the API base URL elsewhere.
+- Response types live in `api/types.ts`; keep them aligned with server response shapes.
+
+## State Management
+
+- Server state: React Query (`@tanstack/react-query`) only.
+- Local UI state: `useState` / `useReducer`.
+- No global client-side state library needed; avoid adding one unless the need is compelling.
+
+## Styling
+
+- Tailwind CSS utility classes directly in JSX; no CSS modules or styled components.
+- Responsive and accessible by default — include `aria-*` attributes for interactive elements.
+
+## Testing
+
+- Test files co-located under `src/` matching the source file name (`*.test.tsx`).
+- Mock `useAuth` by spying on `AuthContext`: `vi.spyOn(AuthContext, 'useAuth').mockReturnValue(...)`.
+- Use `@testing-library/react` + `@testing-library/user-event`; query by accessible role/text, not by class or test-id unless unavoidable.
+- Wrap renders that need routing in ``.
diff --git a/.github/instructions/server.instructions.md b/.github/instructions/server.instructions.md
new file mode 100644
index 0000000..1459ec1
--- /dev/null
+++ b/.github/instructions/server.instructions.md
@@ -0,0 +1,41 @@
+---
+applyTo: "packages/server/**"
+---
+
+# Server Conventions
+
+## Adding a New Feature Route
+
+1. Create `packages/server/src/routes/.ts` — mount the router; protect every non-public endpoint with `authMiddleware`.
+2. Create `packages/server/src/controllers/Controller.ts` — handle `req`/`res` only; delegate logic to the service.
+3. Create `packages/server/src/services/Service.ts` — pure business logic; no Express imports.
+4. If persistence is needed, add `packages/server/src/repositories/Repository.ts` — only file allowed to import firebase-admin.
+5. Register the new router in `packages/server/src/routes/index.ts` under `/api`.
+
+## Middleware
+
+- Middleware order (defined in `app.ts`) must stay: `security` → `logging` → `rateLimiting` → `cors` → `express.json()` → routes → `errorHandler`.
+- Never place `errorHandler` before any route or it won't catch errors.
+- Per-route rate limits go on the specific router, not the app.
+
+## Error Handling
+
+- All errors forwarded via `next(err)` are caught by `errorHandler` in `middleware/errorHandler.ts`.
+- Return `res.status(xxx).json({ error: 'descriptive message' })` — do not invent custom shapes per route.
+- Log structured contexts in the service layer; never log the raw `Authorization` header or token strings.
+
+## Testing
+
+- Test files live in `packages/server/tests/` (e2e under `e2e/`, unit under `unit/`).
+- **Always** mock `../../src/firebase` and `../../src/repositories/userRepository` at the top of every test file before importing `createApp`. See `tests/e2e/userRoutes.test.ts` for the canonical pattern:
+ ```ts
+ vi.mock('../../src/firebase', () => ({ default: {} }));
+ vi.mock('../../src/repositories/userRepository', () => ({ verifyIdToken: vi.fn() }));
+ ```
+- Use `supertest` for e2e route tests; use plain `vitest` for unit tests of services and controllers.
+- Call `vi.resetAllMocks()` in `beforeEach` to prevent mock bleed between tests.
+
+## Environment / Config
+
+- All env vars are validated at startup in `src/config.ts` using `zod`. Add new vars to the `EnvSchema` there; export them on the `config` object.
+- Never access `process.env` outside `config.ts`.
diff --git a/.github/instructions/typescript.instructions.md b/.github/instructions/typescript.instructions.md
new file mode 100644
index 0000000..07ec145
--- /dev/null
+++ b/.github/instructions/typescript.instructions.md
@@ -0,0 +1,27 @@
+---
+applyTo: "**/*.ts,**/*.tsx"
+---
+
+# TypeScript Guidelines
+
+- Enable and respect `strict` mode — no implicit `any`, no non-null assertions (`!`) unless the null/undefined case is provably impossible.
+- Use `unknown` + type-guard (`instanceof`, `typeof`, Zod `.safeParse`) instead of casting to `any`.
+- Prefer interfaces for object shapes that will be extended; use `type` aliases for unions, intersections, and mapped types.
+- All async functions must be fully `await`-ed — never fire-and-forget unless a deliberate side-effect comment explains why.
+- Public functions and exported types must have JSDoc `/** */` comments on the `why`, not just `what`.
+- Avoid re-exporting everything with barrel `index.ts` files; import directly from the source file to keep tree-shaking effective.
+- Use `satisfies` or explicit return types on exported functions so callers get precise inference.
+
+## Client (`packages/client/src`)
+
+- Components: named export for utilities/hooks; **default export only** for page/route-level components.
+- Hooks (`useXxx`): one concern per hook; keep side-effects (`useEffect`) inside the hook, not in the component.
+- Never call `getIdToken()` at component render time — call it inside a React Query `queryFn` only (see `api/hooks.ts → useMe`).
+- Tailwind: use utility classes directly; avoid `@apply` in CSS unless reusing a multi-class pattern 3+ times.
+
+## Server (`packages/server/src`)
+
+- Every async Express handler must end in `try/catch` → `next(err)` or be wrapped in an async error-forwarding helper.
+- Import config exclusively from `config.ts`; never read `process.env` directly in feature code.
+- Validate all incoming request bodies with `zod` before touching `req.body`.
+- Layer boundary: `controllers` import from `services`; `services` import from `repositories`; cross-layer imports are forbidden.
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..8966ccb
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,7 @@
+{
+ "github.copilot.chat.commitMessageGeneration.instructions": [
+ {
+ "file": "./.github/.copilot-commit-message-instructions.md"
+ }
+ ]
+}
From 533547ebbfac8db8c1f2199269df49dd274078a9 Mon Sep 17 00:00:00 2001
From: Daniel David
Date: Tue, 10 Mar 2026 11:10:16 +1100
Subject: [PATCH 2/8] feat(client): add profile update and account deletion
hooks
- Implemented useUpdateProfile and useDeleteAccount hooks for managing user profile updates and account deletion.
- Enhanced userService with corresponding updateMe and deleteMe functions.
- Updated Dashboard component to utilize new hooks for profile management.
feat(server): add user profile update and deletion endpoints
- Introduced PUT and DELETE /api/me endpoints for updating user profiles and deleting accounts.
- Added validation for profile updates using zod.
- Enhanced userController to handle new endpoints and responses.
---
.github/workflows/ci.yml | 35 +++++++++++
package.json | 4 +-
packages/client/.env.example | 8 +++
packages/client/src/api/hooks.ts | 24 ++++++++
.../src/api/services/userService.test.ts | 24 ++++----
.../client/src/api/services/userService.ts | 19 +++---
.../src/features/common/ErrorBoundary.tsx | 4 +-
.../src/features/dashboard/Dashboard.test.tsx | 4 +-
.../src/features/dashboard/Dashboard.tsx | 10 +---
packages/client/src/firebase.ts | 17 ++++++
packages/client/src/main.tsx | 19 +++++-
packages/server/.env.example | 19 ++++++
packages/server/package.json | 1 +
packages/server/src/app.ts | 7 ++-
.../server/src/controllers/userController.ts | 58 ++++++++++++++++---
packages/server/src/index.ts | 3 +-
.../server/src/middleware/authMiddleware.ts | 2 +-
packages/server/src/routes/user.ts | 7 ++-
packages/server/src/services/userService.ts | 25 +++++++-
packages/server/tests/e2e/userRoutes.test.ts | 57 ++++++++++++++----
.../unit/controllers/userController.test.ts | 53 +++++++++++++++--
21 files changed, 333 insertions(+), 67 deletions(-)
create mode 100644 .github/workflows/ci.yml
create mode 100644 packages/client/.env.example
create mode 100644 packages/server/.env.example
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..76491bf
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,35 @@
+name: CI
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+jobs:
+ ci:
+ name: Lint · Test · Build
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ cache: npm
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Lint
+ run: npm run lint
+
+ - name: Test client
+ run: npm run test --workspace=packages/client
+
+ - name: Test server
+ run: npm run test --workspace=packages/server
+
+ - name: Build
+ run: npm run build
diff --git a/package.json b/package.json
index ff0a856..c18824d 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,9 @@
"scripts": {
"dev": "concurrently --kill-others-on-fail \"npm run dev --workspace=packages/client\" \"npm run dev --workspace=packages/server\"",
"build": "npm run build --workspace=packages/client && npm run build --workspace=packages/server",
- "lint": "npm run lint --workspaces --if-present"
+ "lint": "npm run lint --workspaces --if-present",
+ "test:client": "npm run test --workspace=packages/client",
+ "test:server": "npm run test --workspace=packages/server"
},
"devDependencies": {
"concurrently": "^8.2.2"
diff --git a/packages/client/.env.example b/packages/client/.env.example
new file mode 100644
index 0000000..3419623
--- /dev/null
+++ b/packages/client/.env.example
@@ -0,0 +1,8 @@
+# Firebase Web App config
+# Found under: Firebase Console → Project Settings → Your apps → Web app → SDK snippet
+VITE_FIREBASE_API_KEY=
+VITE_FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com
+VITE_FIREBASE_PROJECT_ID=your-project
+VITE_FIREBASE_STORAGE_BUCKET=your-project.appspot.com
+VITE_FIREBASE_MESSAGING_SENDER_ID=
+VITE_FIREBASE_APP_ID=
diff --git a/packages/client/src/api/hooks.ts b/packages/client/src/api/hooks.ts
index 2e5a6e0..465caac 100644
--- a/packages/client/src/api/hooks.ts
+++ b/packages/client/src/api/hooks.ts
@@ -29,3 +29,27 @@ export function useMe() {
},
});
}
+
+export function useUpdateProfile() {
+ const { getIdToken } = useAuth();
+
+ return useMutationHook>(
+ {
+ mutationFn: async (vars) => {
+ const token = await getIdToken();
+ return userService.updateProfile(vars, token);
+ },
+ },
+ );
+}
+
+export function useDeleteAccount() {
+ const { getIdToken } = useAuth();
+
+ return useMutationHook({
+ mutationFn: async () => {
+ const token = await getIdToken();
+ await userService.deleteAccount(token);
+ },
+ });
+}
diff --git a/packages/client/src/api/services/userService.test.ts b/packages/client/src/api/services/userService.test.ts
index 4580f14..835a285 100644
--- a/packages/client/src/api/services/userService.test.ts
+++ b/packages/client/src/api/services/userService.test.ts
@@ -13,7 +13,7 @@ describe('userService', () => {
beforeEach(() => vi.clearAllMocks());
describe('getMe', () => {
- it('sends Authorization header when a token is provided', async () => {
+ it('sends Authorization header with the provided token', async () => {
vi.mocked(axiosInstance.get).mockResolvedValue({ data: { uid: 'abc' } });
const result = await userService.getMe('my-token');
expect(axiosInstance.get).toHaveBeenCalledWith('/me', {
@@ -21,29 +21,27 @@ describe('userService', () => {
});
expect(result).toEqual({ uid: 'abc' });
});
-
- it('omits the Authorization header when no token is provided', async () => {
- vi.mocked(axiosInstance.get).mockResolvedValue({ data: { uid: 'abc' } });
- await userService.getMe();
- expect(axiosInstance.get).toHaveBeenCalledWith('/me', { headers: undefined });
- });
});
describe('updateProfile', () => {
- it('calls PUT /me with the provided data and returns the response', async () => {
+ it('calls PUT /me with data and Authorization header', async () => {
const updated = { uid: 'abc', name: 'New Name' };
vi.mocked(axiosInstance.put).mockResolvedValue({ data: updated });
- const result = await userService.updateProfile({ name: 'New Name' });
- expect(axiosInstance.put).toHaveBeenCalledWith('/me', { name: 'New Name' });
+ const result = await userService.updateProfile({ name: 'New Name' }, 'my-token');
+ expect(axiosInstance.put).toHaveBeenCalledWith('/me', { name: 'New Name' }, {
+ headers: { Authorization: 'Bearer my-token' },
+ });
expect(result).toEqual(updated);
});
});
describe('deleteAccount', () => {
- it('calls DELETE /me', async () => {
+ it('calls DELETE /me with Authorization header', async () => {
vi.mocked(axiosInstance.delete).mockResolvedValue({ data: {} });
- await userService.deleteAccount();
- expect(axiosInstance.delete).toHaveBeenCalledWith('/me');
+ await userService.deleteAccount('my-token');
+ expect(axiosInstance.delete).toHaveBeenCalledWith('/me', {
+ headers: { Authorization: 'Bearer my-token' },
+ });
});
});
});
diff --git a/packages/client/src/api/services/userService.ts b/packages/client/src/api/services/userService.ts
index 5bbec48..dde4af9 100644
--- a/packages/client/src/api/services/userService.ts
+++ b/packages/client/src/api/services/userService.ts
@@ -1,18 +1,21 @@
import axios from '../axios';
import { MeResponse } from '../types';
-export async function getMe(token?: string) {
- const headers = token ? { Authorization: `Bearer ${token}` } : undefined;
- const res = await axios.get('/me', { headers });
+export async function getMe(token: string) {
+ const res = await axios.get('/me', { headers: { Authorization: `Bearer ${token}` } });
return res.data;
}
-export async function updateProfile(data: Partial<{name:string;picture:string}>) {
- const res = await axios.put('/me', data);
+export async function updateProfile(
+ data: Partial<{ name: string; picture: string }>,
+ token: string,
+) {
+ const res = await axios.put('/me', data, {
+ headers: { Authorization: `Bearer ${token}` },
+ });
return res.data;
}
-export async function deleteAccount() {
- const res = await axios.delete('/me');
- return res.data;
+export async function deleteAccount(token: string) {
+ await axios.delete('/me', { headers: { Authorization: `Bearer ${token}` } });
}
diff --git a/packages/client/src/features/common/ErrorBoundary.tsx b/packages/client/src/features/common/ErrorBoundary.tsx
index 43c66b3..a2e78e6 100644
--- a/packages/client/src/features/common/ErrorBoundary.tsx
+++ b/packages/client/src/features/common/ErrorBoundary.tsx
@@ -51,10 +51,10 @@ export default class ErrorBoundary extends React.Component<
An unexpected error occurred. Please try refreshing the page.
);
diff --git a/packages/client/src/features/dashboard/Dashboard.test.tsx b/packages/client/src/features/dashboard/Dashboard.test.tsx
index da247a0..2d3f895 100644
--- a/packages/client/src/features/dashboard/Dashboard.test.tsx
+++ b/packages/client/src/features/dashboard/Dashboard.test.tsx
@@ -9,7 +9,7 @@ import type { User } from 'firebase/auth';
vi.mock('../../api/hooks', () => ({
useMe: vi.fn(),
- useMutationHook: vi.fn(),
+ useUpdateProfile: vi.fn(),
}));
const mockUser = {
@@ -40,7 +40,7 @@ describe('Dashboard', () => {
error: null,
refetch: vi.fn(),
} as any);
- vi.mocked(hooks.useMutationHook).mockReturnValue({
+ vi.mocked(hooks.useUpdateProfile).mockReturnValue({
mutate: vi.fn(),
isPending: false,
} as any);
diff --git a/packages/client/src/features/dashboard/Dashboard.tsx b/packages/client/src/features/dashboard/Dashboard.tsx
index a3ba3f5..56128ce 100644
--- a/packages/client/src/features/dashboard/Dashboard.tsx
+++ b/packages/client/src/features/dashboard/Dashboard.tsx
@@ -1,12 +1,12 @@
-import { useState } from 'react';
import { useAuth } from '../auth/AuthContext';
-import { useMe, useMutationHook } from '../../api/hooks';
-import * as userService from '../../api/services/userService';
+import { useMe, useUpdateProfile } from '../../api/hooks';
export default function Dashboard() {
const { user, signOut } = useAuth();
const { data, isLoading, error, refetch } = useMe();
+ const updateMutation = useUpdateProfile();
+
const apiResult = error ? String(error) : data ? JSON.stringify(data, null, 2) : null;
const fetching = isLoading;
@@ -14,10 +14,6 @@ export default function Dashboard() {
refetch();
};
- // example mutation: update profile with dummy name
- const updateMutation = useMutationHook({
- mutationFn: (vars: Partial<{ name: string; picture: string }>) => userService.updateProfile(vars),
- });
const callUpdate = () => updateMutation.mutate({ name: 'New Name' });
return (
diff --git a/packages/client/src/firebase.ts b/packages/client/src/firebase.ts
index bcb5dc6..8dcd48c 100644
--- a/packages/client/src/firebase.ts
+++ b/packages/client/src/firebase.ts
@@ -1,6 +1,23 @@
import { initializeApp } from 'firebase/app';
import { getAuth, GoogleAuthProvider } from 'firebase/auth';
+const requiredVars = [
+ 'VITE_FIREBASE_API_KEY',
+ 'VITE_FIREBASE_AUTH_DOMAIN',
+ 'VITE_FIREBASE_PROJECT_ID',
+ 'VITE_FIREBASE_STORAGE_BUCKET',
+ 'VITE_FIREBASE_MESSAGING_SENDER_ID',
+ 'VITE_FIREBASE_APP_ID',
+] as const;
+
+const missingVars = requiredVars.filter((key) => !import.meta.env[key]);
+if (missingVars.length > 0) {
+ throw new Error(
+ `Missing required Firebase environment variables: ${missingVars.join(', ')}\n` +
+ 'Copy packages/client/.env.example to packages/client/.env and fill in your Firebase config.',
+ );
+}
+
const firebaseConfig = {
apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
diff --git a/packages/client/src/main.tsx b/packages/client/src/main.tsx
index b10a888..15c3cbc 100644
--- a/packages/client/src/main.tsx
+++ b/packages/client/src/main.tsx
@@ -4,7 +4,24 @@ import './index.css';
import App from './App';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
-const queryClient = new QueryClient();
+const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ staleTime: 1000 * 60 * 5, // 5 minutes — avoids refetching on every mount
+ retry: (failureCount, error: unknown) => {
+ // Don't retry auth errors; they won't self-heal without user action
+ const status =
+ typeof error === 'object' &&
+ error !== null &&
+ 'response' in error
+ ? (error as { response?: { status?: number } }).response?.status
+ : undefined;
+ if (status === 401 || status === 403) return false;
+ return failureCount < 2;
+ },
+ },
+ },
+});
createRoot(document.getElementById('root')!).render(
diff --git a/packages/server/.env.example b/packages/server/.env.example
new file mode 100644
index 0000000..5e4ae8a
--- /dev/null
+++ b/packages/server/.env.example
@@ -0,0 +1,19 @@
+# Server port (default: 3001)
+PORT=3001
+
+# Allowed CORS origin — must match the client URL exactly (no trailing slash)
+CORS_ORIGIN=http://localhost:5173
+
+# Rate limiting
+RATE_LIMIT_WINDOW_MINUTES=15
+RATE_LIMIT_MAX=100
+
+# ── Firebase Admin credentials ───────────────────────────────────────────────
+# Option A (preferred): paste the full service account JSON as a single string
+# Found under: Firebase Console → Project Settings → Service accounts → Generate new private key
+FIREBASE_SERVICE_ACCOUNT_JSON=
+
+# Option B: individual env vars (used if FIREBASE_SERVICE_ACCOUNT_JSON is absent)
+FIREBASE_PROJECT_ID=your-project
+FIREBASE_CLIENT_EMAIL=firebase-adminsdk-xxxxx@your-project.iam.gserviceaccount.com
+FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
diff --git a/packages/server/package.json b/packages/server/package.json
index 69d33e3..5dc33f8 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -6,6 +6,7 @@
"dev": "tsx watch src/index.ts",
"build": "tsc --outDir dist",
"start": "node dist/index.js",
+ "lint": "tsc --noEmit",
"test": "vitest run --config vitest.config.ts",
"test:watch": "vitest --config vitest.config.ts"
},
diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts
index a25bd50..89385a9 100644
--- a/packages/server/src/app.ts
+++ b/packages/server/src/app.ts
@@ -10,14 +10,17 @@ import { config } from './config';
export function createApp() {
const app = express();
+ // trust first proxy hop so express-rate-limit uses the real client IP
+ app.set('trust proxy', 1);
+
// apply middleware layers
applySecurity(app);
applyLogging(app);
applyRateLimiting(app, config.rateLimit);
applyCors(app, config.corsOrigin);
- // built-in middleware
- app.use(express.json());
+ // built-in middleware — 10 kb cap prevents trivial payload-flood attacks
+ app.use(express.json({ limit: '10kb' }));
// mount API router
app.use('/api', apiRouter);
diff --git a/packages/server/src/controllers/userController.ts b/packages/server/src/controllers/userController.ts
index 9d2a93e..88f2e5f 100644
--- a/packages/server/src/controllers/userController.ts
+++ b/packages/server/src/controllers/userController.ts
@@ -1,10 +1,54 @@
-import { Response } from 'express';
-import { getUserProfile } from '../services/userService';
+import { Response, NextFunction } from 'express';
+import { z } from 'zod';
+import { getUserProfile, applyProfileUpdate } from '../services/userService';
import { AuthenticatedRequest } from '../middleware/authMiddleware';
-// controller layer handles express req/res and delegates to service
-export async function getMe(req: AuthenticatedRequest, res: Response) {
- const decoded = req.user!;
- const profile = getUserProfile(decoded);
- res.json(profile);
+const UpdateProfileSchema = z.object({
+ name: z.string().min(1).max(100).optional(),
+ picture: z.string().url().optional(),
+});
+
+/** GET /api/me — returns the authenticated user's profile. */
+export async function getMe(
+ req: AuthenticatedRequest,
+ res: Response,
+ next: NextFunction,
+): Promise {
+ try {
+ res.json(getUserProfile(req.user!));
+ } catch (err) {
+ next(err);
+ }
+}
+
+/** PUT /api/me — merges validated fields onto the current profile. */
+export async function updateMe(
+ req: AuthenticatedRequest,
+ res: Response,
+ next: NextFunction,
+): Promise {
+ try {
+ const parsed = UpdateProfileSchema.safeParse(req.body);
+ if (!parsed.success) {
+ res.status(400).json({ error: parsed.error.issues[0]?.message ?? 'Invalid request body' });
+ return;
+ }
+ res.json(applyProfileUpdate(req.user!, parsed.data));
+ } catch (err) {
+ next(err);
+ }
+}
+
+/** DELETE /api/me — removes the authenticated user's account. */
+export async function deleteMe(
+ req: AuthenticatedRequest,
+ res: Response,
+ next: NextFunction,
+): Promise {
+ try {
+ // In production: revoke Firebase session + delete from database here.
+ res.status(204).send();
+ } catch (err) {
+ next(err);
+ }
}
diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts
index 867458f..e0271fe 100644
--- a/packages/server/src/index.ts
+++ b/packages/server/src/index.ts
@@ -1,7 +1,8 @@
import 'dotenv/config';
import { createApp } from './app';
+import { config } from './config';
-const PORT = process.env.PORT ?? 3001;
+const PORT = config.port;
const app = createApp();
// ── Start ─────────────────────────────────────────────────────────────────────
diff --git a/packages/server/src/middleware/authMiddleware.ts b/packages/server/src/middleware/authMiddleware.ts
index d200a3a..7f40764 100644
--- a/packages/server/src/middleware/authMiddleware.ts
+++ b/packages/server/src/middleware/authMiddleware.ts
@@ -18,7 +18,7 @@ export async function authMiddleware(
return;
}
- const idToken = authHeader.split('Bearer ')[1];
+ const idToken = authHeader.slice(7);
try {
const decodedToken = await verifyIdToken(idToken);
diff --git a/packages/server/src/routes/user.ts b/packages/server/src/routes/user.ts
index ffdcdc7..164bdc4 100644
--- a/packages/server/src/routes/user.ts
+++ b/packages/server/src/routes/user.ts
@@ -1,10 +1,11 @@
import { Router } from 'express';
-import { getMe } from '../controllers/userController';
+import { getMe, updateMe, deleteMe } from '../controllers/userController';
import { authMiddleware } from '../middleware/authMiddleware';
const router = Router();
-// `GET /api/me`
-router.get('/', authMiddleware, getMe);
+router.get('/', authMiddleware, getMe); // GET /api/me
+router.put('/', authMiddleware, updateMe); // PUT /api/me
+router.delete('/', authMiddleware, deleteMe); // DELETE /api/me
export default router;
diff --git a/packages/server/src/services/userService.ts b/packages/server/src/services/userService.ts
index e5e0024..f1695c9 100644
--- a/packages/server/src/services/userService.ts
+++ b/packages/server/src/services/userService.ts
@@ -1,7 +1,26 @@
-import admin from '../firebase';
+import type { DecodedIdToken } from 'firebase-admin/auth';
-// business logic layer – for now it simply formats the decoded token
-export function getUserProfile(decoded: admin.auth.DecodedIdToken) {
+/** Shape returned by all /me endpoints. */
+export interface UserProfile {
+ uid: string;
+ email?: string;
+ name?: string;
+ picture?: string;
+}
+
+/** Extracts public profile fields from a decoded Firebase ID token. */
+export function getUserProfile(decoded: DecodedIdToken): UserProfile {
const { uid, email, name, picture } = decoded;
return { uid, email, name, picture };
}
+
+/**
+ * Merges validated update data onto the token-derived profile.
+ * Replace with a real persistence call once a database is added.
+ */
+export function applyProfileUpdate(
+ decoded: DecodedIdToken,
+ data: { name?: string; picture?: string },
+): UserProfile {
+ return { ...getUserProfile(decoded), ...data };
+}
diff --git a/packages/server/tests/e2e/userRoutes.test.ts b/packages/server/tests/e2e/userRoutes.test.ts
index bc0a35e..4bf3594 100644
--- a/packages/server/tests/e2e/userRoutes.test.ts
+++ b/packages/server/tests/e2e/userRoutes.test.ts
@@ -11,35 +11,70 @@ import request from 'supertest';
import { createApp } from '../../src/app';
import { verifyIdToken } from '../../src/repositories/userRepository';
+const decoded: any = { uid: 'user123', email: 'a@b.com', name: 'Alice', picture: null };
+
describe('e2e /api/me', () => {
beforeEach(() => {
vi.resetAllMocks();
});
- it('returns 401 when no token', async () => {
+ // ── GET /api/me ────────────────────────────────────────────────────────────
+ it('GET returns 401 when no token', async () => {
const app = createApp();
await request(app).get('/api/me').expect(401);
});
- it('returns 401 when token invalid', async () => {
+ it('GET returns 401 when token invalid', async () => {
vi.mocked(verifyIdToken).mockRejectedValue(new Error('invalid'));
const app = createApp();
- await request(app)
- .get('/api/me')
- .set('Authorization', 'Bearer bad')
- .expect(401);
+ await request(app).get('/api/me').set('Authorization', 'Bearer bad').expect(401);
});
- it('returns 200 and user profile when token valid', async () => {
- const decoded = { uid: 'user123', email: 'a@b.com', name: 'Alice', picture: null } as any;
+ it('GET returns 200 and user profile when token valid', async () => {
vi.mocked(verifyIdToken).mockResolvedValue(decoded);
+ const app = createApp();
+ const res = await request(app).get('/api/me').set('Authorization', 'Bearer good');
+ expect(res.status).toBe(200);
+ expect(res.body).toMatchObject({ uid: 'user123', email: 'a@b.com' });
+ });
+ // ── PUT /api/me ─────────────────────────────────────────────────────────────
+ it('PUT returns 401 without token', async () => {
+ const app = createApp();
+ await request(app).put('/api/me').send({ name: 'Bob' }).expect(401);
+ });
+
+ it('PUT returns 400 on invalid body', async () => {
+ vi.mocked(verifyIdToken).mockResolvedValue(decoded);
const app = createApp();
const res = await request(app)
- .get('/api/me')
- .set('Authorization', 'Bearer good');
+ .put('/api/me')
+ .set('Authorization', 'Bearer good')
+ .send({ picture: 'not-a-url' });
+ expect(res.status).toBe(400);
+ expect(res.body).toHaveProperty('error');
+ });
+ it('PUT returns updated profile on valid body', async () => {
+ vi.mocked(verifyIdToken).mockResolvedValue(decoded);
+ const app = createApp();
+ const res = await request(app)
+ .put('/api/me')
+ .set('Authorization', 'Bearer good')
+ .send({ name: 'Bob' });
expect(res.status).toBe(200);
- expect(res.body).toMatchObject({ uid: 'user123', email: 'a@b.com' });
+ expect(res.body).toMatchObject({ uid: 'user123', name: 'Bob' });
+ });
+
+ // ── DELETE /api/me ───────────────────────────────────────────────────────────
+ it('DELETE returns 401 without token', async () => {
+ const app = createApp();
+ await request(app).delete('/api/me').expect(401);
+ });
+
+ it('DELETE returns 204 with valid token', async () => {
+ vi.mocked(verifyIdToken).mockResolvedValue(decoded);
+ const app = createApp();
+ await request(app).delete('/api/me').set('Authorization', 'Bearer good').expect(204);
});
});
diff --git a/packages/server/tests/unit/controllers/userController.test.ts b/packages/server/tests/unit/controllers/userController.test.ts
index b5cc3a0..3864a4b 100644
--- a/packages/server/tests/unit/controllers/userController.test.ts
+++ b/packages/server/tests/unit/controllers/userController.test.ts
@@ -3,20 +3,63 @@ import { describe, it, expect, vi } from 'vitest';
vi.mock('../../../src/services/userService', () => ({
getUserProfile: vi.fn(),
+ applyProfileUpdate: vi.fn(),
}));
-import { getMe } from '../../../src/controllers/userController';
-import { getUserProfile } from '../../../src/services/userService';
+import { getMe, updateMe, deleteMe } from '../../../src/controllers/userController';
+import { getUserProfile, applyProfileUpdate } from '../../../src/services/userService';
+
+const fakeDecoded: any = { uid: 'abc', email: 'test@example.com', name: 'Test User', picture: undefined };
describe('userController.getMe', () => {
it('responds with profile JSON', async () => {
- const fakeReq: any = { user: { uid: 'abc', email: 'test@example.com', name: 'Test User', picture: undefined } };
const fakeProfile = { uid: 'abc', email: 'test@example.com', name: 'Test User', picture: undefined };
vi.mocked(getUserProfile).mockReturnValue(fakeProfile);
+ const req: any = { user: fakeDecoded };
const res: any = { json: vi.fn() };
- await getMe(fakeReq, res);
- expect(getUserProfile).toHaveBeenCalledWith(fakeReq.user);
+ const next = vi.fn();
+
+ await getMe(req, res, next);
+ expect(getUserProfile).toHaveBeenCalledWith(fakeDecoded);
expect(res.json).toHaveBeenCalledWith(fakeProfile);
+ expect(next).not.toHaveBeenCalled();
+ });
+});
+
+describe('userController.updateMe', () => {
+ it('returns 400 when body is invalid', async () => {
+ const req: any = { user: fakeDecoded, body: { picture: 'not-a-url' } };
+ const res: any = { status: vi.fn().mockReturnThis(), json: vi.fn() };
+ const next = vi.fn();
+
+ await updateMe(req, res, next);
+ expect(res.status).toHaveBeenCalledWith(400);
+ expect(res.json).toHaveBeenCalledWith(expect.objectContaining({ error: expect.any(String) }));
+ });
+
+ it('returns updated profile on valid body', async () => {
+ const updated = { uid: 'abc', name: 'New Name' };
+ vi.mocked(applyProfileUpdate).mockReturnValue(updated as any);
+
+ const req: any = { user: fakeDecoded, body: { name: 'New Name' } };
+ const res: any = { json: vi.fn() };
+ const next = vi.fn();
+
+ await updateMe(req, res, next);
+ expect(applyProfileUpdate).toHaveBeenCalledWith(fakeDecoded, { name: 'New Name' });
+ expect(res.json).toHaveBeenCalledWith(updated);
+ });
+});
+
+describe('userController.deleteMe', () => {
+ it('returns 204 with no body', async () => {
+ const req: any = { user: fakeDecoded };
+ const res: any = { status: vi.fn().mockReturnThis(), send: vi.fn() };
+ const next = vi.fn();
+
+ await deleteMe(req, res, next);
+ expect(res.status).toHaveBeenCalledWith(204);
+ expect(res.send).toHaveBeenCalled();
});
});
From e0f7a66766e7ef5446873fdb7a359570d407d414 Mon Sep 17 00:00:00 2001
From: Daniel David
Date: Tue, 10 Mar 2026 11:26:57 +1100
Subject: [PATCH 3/8] feat(client): enhance login flow with error handling and
refactor hooks
Added error handling to the login component for Google sign-in failures.
Refactored authentication hooks to remove token parameters and utilize
the user context directly. Updated tests to reflect these changes and
improve overall code structure.
---
packages/client/src/App.test.tsx | 23 ++++++++
packages/client/src/App.tsx | 8 +--
packages/client/src/api/hooks.test.tsx | 14 ++++-
packages/client/src/api/hooks.ts | 55 +++++++------------
.../src/api/services/userService.test.ts | 32 ++++++-----
.../client/src/api/services/userService.ts | 17 ++----
.../client/src/features/auth/AuthContext.tsx | 5 ++
packages/client/src/features/login/Login.tsx | 18 +++++-
8 files changed, 104 insertions(+), 68 deletions(-)
diff --git a/packages/client/src/App.test.tsx b/packages/client/src/App.test.tsx
index 0db93ab..d720177 100644
--- a/packages/client/src/App.test.tsx
+++ b/packages/client/src/App.test.tsx
@@ -7,6 +7,7 @@ import * as AuthContext from './features/auth/AuthContext';
const routerProps = { future: { v7_startTransition: true, v7_relativeSplatPath: true } };
describe('Login screen', () => {
+ // wrapping App usage doesn't change these behaviors
let signInWithGoogle: () => Promise;
beforeEach(() => {
@@ -40,4 +41,26 @@ describe('Login screen', () => {
await userEvent.click(screen.getByRole('button', { name: /sign in with google/i }));
expect(signInWithGoogle).toHaveBeenCalledOnce();
});
+
+ it('displays an error message when sign-in fails', async () => {
+ const error = new Error('popup closed');
+ signInWithGoogle = vi.fn<() => Promise>().mockRejectedValue(error);
+ vi.spyOn(AuthContext, 'useAuth').mockReturnValue({
+ user: null,
+ loading: false,
+ signInWithGoogle,
+ signOut: vi.fn<() => Promise>().mockResolvedValue(),
+ getIdToken: vi.fn<() => Promise>().mockResolvedValue(''),
+ });
+
+ render(
+
+
+
+ );
+
+ await userEvent.click(screen.getByRole('button', { name: /sign in with google/i }));
+ expect(signInWithGoogle).toHaveBeenCalledOnce();
+ expect(await screen.findByText(/popup closed/i)).toBeInTheDocument();
+ });
});
diff --git a/packages/client/src/App.tsx b/packages/client/src/App.tsx
index 856ac85..8c4d790 100644
--- a/packages/client/src/App.tsx
+++ b/packages/client/src/App.tsx
@@ -8,8 +8,8 @@ import Dashboard from './features/dashboard/Dashboard';
export default function App() {
return (
-
-
+
+
} />
} />
-
-
+
+
);
}
diff --git a/packages/client/src/api/hooks.test.tsx b/packages/client/src/api/hooks.test.tsx
index 4eb9b96..bc90f6b 100644
--- a/packages/client/src/api/hooks.test.tsx
+++ b/packages/client/src/api/hooks.test.tsx
@@ -28,6 +28,10 @@ const mockUseAuth = (overrides: Partial>
});
describe('useMe', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
afterEach(() => vi.restoreAllMocks());
it('calls getIdToken then passes the token to getMe', async () => {
@@ -39,10 +43,18 @@ describe('useMe', () => {
await waitFor(() => expect(result.current.isSuccess).toBe(true));
- expect(userService.getMe).toHaveBeenCalledWith('test-token');
+ expect(userService.getMe).toHaveBeenCalled();
expect(result.current.data).toEqual(mockData);
});
+ it('does not call the service when there is no authenticated user', () => {
+ mockUseAuth({ user: null });
+ const { result } = renderHook(() => useMe(), { wrapper: makeWrapper() });
+ expect(userService.getMe).not.toHaveBeenCalled();
+ // query should not be in error state either
+ expect(result.current.isError).toBe(false);
+ });
+
it('exposes isLoading true before the query resolves', () => {
mockUseAuth();
// never resolves — keeps the hook in loading state
diff --git a/packages/client/src/api/hooks.ts b/packages/client/src/api/hooks.ts
index 465caac..39409ba 100644
--- a/packages/client/src/api/hooks.ts
+++ b/packages/client/src/api/hooks.ts
@@ -1,55 +1,40 @@
-import { useQuery, useMutation, UseQueryOptions, UseMutationOptions, UseMutationResult } from '@tanstack/react-query';
+import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useAuth } from '../features/auth/AuthContext';
import * as userService from './services/userService';
import { MeResponse } from './types';
-// generic fetch hook
-export function useFetch(
- options: UseQueryOptions,
-) {
- return useQuery(options);
-}
-
-// generic mutation hook
-export function useMutationHook(
- options: UseMutationOptions,
-): UseMutationResult {
- return useMutation(options);
-}
+// helpers have been removed; useQuery/useMutation object form below
export function useMe() {
- const { getIdToken } = useAuth();
+ const { user } = useAuth();
- return useFetch({
+ return useQuery({
queryKey: ['me'],
- queryFn: async () => {
- const token = await getIdToken();
- const res = await userService.getMe(token);
- return res;
- },
+ queryFn: userService.getMe,
+ enabled: !!user,
});
}
export function useUpdateProfile() {
- const { getIdToken } = useAuth();
+ const queryClient = useQueryClient();
- return useMutationHook>(
- {
- mutationFn: async (vars) => {
- const token = await getIdToken();
- return userService.updateProfile(vars, token);
- },
+ return useMutation>({
+ mutationFn: userService.updateProfile,
+ onSuccess(data) {
+ queryClient.setQueryData(['me'], data);
},
- );
+ });
}
export function useDeleteAccount() {
- const { getIdToken } = useAuth();
-
- return useMutationHook({
- mutationFn: async () => {
- const token = await getIdToken();
- await userService.deleteAccount(token);
+ const queryClient = useQueryClient();
+ const { signOut } = useAuth();
+
+ return useMutation({
+ mutationFn: userService.deleteAccount,
+ onSuccess() {
+ queryClient.removeQueries(['me']);
+ signOut();
},
});
}
diff --git a/packages/client/src/api/services/userService.test.ts b/packages/client/src/api/services/userService.test.ts
index 835a285..3f2509e 100644
--- a/packages/client/src/api/services/userService.test.ts
+++ b/packages/client/src/api/services/userService.test.ts
@@ -6,42 +6,44 @@ vi.mock('../axios', () => ({
get: vi.fn(),
put: vi.fn(),
delete: vi.fn(),
+ interceptors: { request: { use: vi.fn() } },
},
}));
+// token store imported dynamically in interceptor test
+
describe('userService', () => {
beforeEach(() => vi.clearAllMocks());
describe('getMe', () => {
- it('sends Authorization header with the provided token', async () => {
+ it('calls GET /me and returns response data', async () => {
vi.mocked(axiosInstance.get).mockResolvedValue({ data: { uid: 'abc' } });
- const result = await userService.getMe('my-token');
- expect(axiosInstance.get).toHaveBeenCalledWith('/me', {
- headers: { Authorization: 'Bearer my-token' },
- });
+ const result = await userService.getMe();
+ expect(vi.mocked(axiosInstance.get)).toHaveBeenCalled();
+ expect(vi.mocked(axiosInstance.get).mock.calls[0][0]).toBe('/me');
expect(result).toEqual({ uid: 'abc' });
});
});
describe('updateProfile', () => {
- it('calls PUT /me with data and Authorization header', async () => {
+ it('calls PUT /me with provided data and returns response', async () => {
const updated = { uid: 'abc', name: 'New Name' };
vi.mocked(axiosInstance.put).mockResolvedValue({ data: updated });
- const result = await userService.updateProfile({ name: 'New Name' }, 'my-token');
- expect(axiosInstance.put).toHaveBeenCalledWith('/me', { name: 'New Name' }, {
- headers: { Authorization: 'Bearer my-token' },
- });
+ const result = await userService.updateProfile({ name: 'New Name' });
+ expect(vi.mocked(axiosInstance.put)).toHaveBeenCalled();
+ expect(vi.mocked(axiosInstance.put).mock.calls[0][0]).toBe('/me');
expect(result).toEqual(updated);
});
});
describe('deleteAccount', () => {
- it('calls DELETE /me with Authorization header', async () => {
+ it('calls DELETE /me', async () => {
vi.mocked(axiosInstance.delete).mockResolvedValue({ data: {} });
- await userService.deleteAccount('my-token');
- expect(axiosInstance.delete).toHaveBeenCalledWith('/me', {
- headers: { Authorization: 'Bearer my-token' },
- });
+ await userService.deleteAccount();
+ expect(vi.mocked(axiosInstance.delete)).toHaveBeenCalled();
+ expect(vi.mocked(axiosInstance.delete).mock.calls[0][0]).toBe('/me');
});
});
});
+
+
diff --git a/packages/client/src/api/services/userService.ts b/packages/client/src/api/services/userService.ts
index dde4af9..338bb62 100644
--- a/packages/client/src/api/services/userService.ts
+++ b/packages/client/src/api/services/userService.ts
@@ -1,21 +1,16 @@
import axios from '../axios';
import { MeResponse } from '../types';
-export async function getMe(token: string) {
- const res = await axios.get('/me', { headers: { Authorization: `Bearer ${token}` } });
+export async function getMe() {
+ const res = await axios.get('/me');
return res.data;
}
-export async function updateProfile(
- data: Partial<{ name: string; picture: string }>,
- token: string,
-) {
- const res = await axios.put('/me', data, {
- headers: { Authorization: `Bearer ${token}` },
- });
+export async function updateProfile(data: Partial<{ name: string; picture: string }>) {
+ const res = await axios.put('/me', data);
return res.data;
}
-export async function deleteAccount(token: string) {
- await axios.delete('/me', { headers: { Authorization: `Bearer ${token}` } });
+export async function deleteAccount() {
+ await axios.delete('/me');
}
diff --git a/packages/client/src/features/auth/AuthContext.tsx b/packages/client/src/features/auth/AuthContext.tsx
index c6fdacd..3000cd7 100644
--- a/packages/client/src/features/auth/AuthContext.tsx
+++ b/packages/client/src/features/auth/AuthContext.tsx
@@ -5,6 +5,7 @@ import {
useState,
ReactNode,
} from 'react';
+import { useQueryClient } from '@tanstack/react-query';
import {
User,
onAuthStateChanged,
@@ -40,8 +41,12 @@ export function AuthProvider({ children }: { children: ReactNode }) {
await signInWithPopup(auth, googleProvider);
};
+ const queryClient = useQueryClient();
+
const signOut = async () => {
await firebaseSignOut(auth);
+ // clear any cached server data when user signs out
+ queryClient.clear();
};
const getIdToken = async (): Promise => {
diff --git a/packages/client/src/features/login/Login.tsx b/packages/client/src/features/login/Login.tsx
index 0c54562..b02ce97 100644
--- a/packages/client/src/features/login/Login.tsx
+++ b/packages/client/src/features/login/Login.tsx
@@ -1,22 +1,33 @@
-import { useEffect } from 'react';
+import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useAuth } from '../auth/AuthContext';
export default function Login() {
const { user, signInWithGoogle } = useAuth();
const navigate = useNavigate();
+ const [error, setError] = useState(null);
useEffect(() => {
if (user) navigate('/', { replace: true });
}, [user, navigate]);
+ const handleSignIn = async () => {
+ setError(null);
+ try {
+ await signInWithGoogle();
+ } catch (err) {
+ console.error('Google sign-in failed', err);
+ setError((err as Error).message || 'Authentication failed');
+ }
+ };
+
return (
Welcome
Sign in to continue
+ {error && (
+
{error}
+ )}
);
From fa7e7e92bef0a6bfc2fda58fba0565fca4453ce7 Mon Sep 17 00:00:00 2001
From: Daniel David
Date: Tue, 10 Mar 2026 11:47:54 +1100
Subject: [PATCH 4/8] fix(client): update removeQueries to use filters object
for compatibility
---
packages/client/src/api/hooks.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/client/src/api/hooks.ts b/packages/client/src/api/hooks.ts
index 39409ba..004dbb9 100644
--- a/packages/client/src/api/hooks.ts
+++ b/packages/client/src/api/hooks.ts
@@ -33,7 +33,8 @@ export function useDeleteAccount() {
return useMutation({
mutationFn: userService.deleteAccount,
onSuccess() {
- queryClient.removeQueries(['me']);
+ // `removeQueries` expects a filters object in v4+; provide the queryKey explicitly
+ queryClient.removeQueries({ queryKey: ['me'] });
signOut();
},
});
From 3483bf164b5a78fe74c932f112f2020710ffa571 Mon Sep 17 00:00:00 2001
From: Daniel David
Date: Tue, 10 Mar 2026 11:52:50 +1100
Subject: [PATCH 5/8] feat(client): enhance hooks to handle token for user
service requests
Updated useMe, useUpdateProfile, and useDeleteAccount hooks to fetch
and utilize ID tokens for making authenticated requests. Added
support for token handling in userService methods and corresponding
tests to ensure proper header usage.
---
packages/client/src/api/hooks.test.tsx | 71 +++++++++++++++++--
packages/client/src/api/hooks.ts | 20 ++++--
.../src/api/services/userService.test.ts | 40 +++++++----
.../client/src/api/services/userService.ts | 31 ++++++--
4 files changed, 133 insertions(+), 29 deletions(-)
diff --git a/packages/client/src/api/hooks.test.tsx b/packages/client/src/api/hooks.test.tsx
index bc90f6b..00f93d1 100644
--- a/packages/client/src/api/hooks.test.tsx
+++ b/packages/client/src/api/hooks.test.tsx
@@ -1,7 +1,7 @@
-import { renderHook, waitFor } from '@testing-library/react';
+import { renderHook, waitFor, act } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactNode } from 'react';
-import { useMe } from './hooks';
+import { useMe, useUpdateProfile, useDeleteAccount } from './hooks';
import * as AuthContext from '../features/auth/AuthContext';
import * as userService from './services/userService';
import type { User } from 'firebase/auth';
@@ -35,7 +35,8 @@ describe('useMe', () => {
afterEach(() => vi.restoreAllMocks());
it('calls getIdToken then passes the token to getMe', async () => {
- mockUseAuth();
+ const getIdToken = vi.fn<() => Promise>().mockResolvedValue('test-token');
+ mockUseAuth({ getIdToken });
const mockData = { uid: '123', email: 'test@example.com' };
vi.mocked(userService.getMe).mockResolvedValue(mockData);
@@ -43,7 +44,8 @@ describe('useMe', () => {
await waitFor(() => expect(result.current.isSuccess).toBe(true));
- expect(userService.getMe).toHaveBeenCalled();
+ expect(getIdToken).toHaveBeenCalled();
+ expect(userService.getMe).toHaveBeenCalledWith('test-token');
expect(result.current.data).toEqual(mockData);
});
@@ -56,7 +58,8 @@ describe('useMe', () => {
});
it('exposes isLoading true before the query resolves', () => {
- mockUseAuth();
+ const getIdToken = vi.fn<() => Promise>().mockResolvedValue('foo');
+ mockUseAuth({ getIdToken });
// never resolves — keeps the hook in loading state
vi.mocked(userService.getMe).mockReturnValue(new Promise(() => {}));
@@ -66,7 +69,8 @@ describe('useMe', () => {
});
it('exposes error when getMe rejects', async () => {
- mockUseAuth();
+ const getIdToken = vi.fn<() => Promise>().mockResolvedValue('foo');
+ mockUseAuth({ getIdToken });
vi.mocked(userService.getMe).mockRejectedValue(new Error('network error'));
const { result } = renderHook(() => useMe(), { wrapper: makeWrapper() });
@@ -75,3 +79,58 @@ describe('useMe', () => {
expect((result.current.error as Error).message).toBe('network error');
});
});
+
+// additional hook tests for token-handling mutations
+
+describe('useUpdateProfile', () => {
+ beforeEach(() => vi.clearAllMocks());
+
+ it('fetches token and calls updateProfile, then updates cache', async () => {
+ const getIdToken = vi.fn<() => Promise>().mockResolvedValue('up-token');
+ mockUseAuth({ getIdToken });
+ const updated = { uid: 'abc', name: 'New Name' };
+ vi.mocked(userService.updateProfile).mockResolvedValue(updated);
+ const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
+ const wrapper = ({ children }: { children: ReactNode }) => (
+ {children}
+ );
+
+ const { result } = renderHook(() => useUpdateProfile(), { wrapper });
+
+ await act(async () => {
+ result.current.mutateAsync({ name: 'New Name' });
+ });
+
+ expect(getIdToken).toHaveBeenCalled();
+ expect(userService.updateProfile).toHaveBeenCalledWith({ name: 'New Name' }, 'up-token');
+ // cache populated via onSuccess
+ expect(queryClient.getQueryData(['me'])).toEqual(updated);
+ });
+});
+
+describe('useDeleteAccount', () => {
+ beforeEach(() => vi.clearAllMocks());
+
+ it('fetches token and calls deleteAccount, then clears cache and signs out', async () => {
+ const getIdToken = vi.fn<() => Promise>().mockResolvedValue('del-token');
+ const signOut = vi.fn<() => Promise>().mockResolvedValue();
+ mockUseAuth({ getIdToken, signOut });
+ vi.mocked(userService.deleteAccount).mockResolvedValue(undefined);
+
+ const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
+ const wrapper = ({ children }: { children: ReactNode }) => (
+ {children}
+ );
+
+ const { result } = renderHook(() => useDeleteAccount(), { wrapper });
+
+ await act(async () => {
+ result.current.mutateAsync();
+ });
+
+ expect(getIdToken).toHaveBeenCalled();
+ expect(userService.deleteAccount).toHaveBeenCalledWith('del-token');
+ expect(queryClient.getQueryData(['me'])).toBeUndefined();
+ expect(signOut).toHaveBeenCalled();
+ });
+});
diff --git a/packages/client/src/api/hooks.ts b/packages/client/src/api/hooks.ts
index 004dbb9..d3fb4ca 100644
--- a/packages/client/src/api/hooks.ts
+++ b/packages/client/src/api/hooks.ts
@@ -6,20 +6,27 @@ import { MeResponse } from './types';
// helpers have been removed; useQuery/useMutation object form below
export function useMe() {
- const { user } = useAuth();
+ const { user, getIdToken } = useAuth();
return useQuery({
queryKey: ['me'],
- queryFn: userService.getMe,
+ queryFn: async () => {
+ const token = await getIdToken();
+ return userService.getMe(token);
+ },
enabled: !!user,
});
}
export function useUpdateProfile() {
const queryClient = useQueryClient();
+ const { getIdToken } = useAuth();
return useMutation>({
- mutationFn: userService.updateProfile,
+ mutationFn: async (data) => {
+ const token = await getIdToken();
+ return userService.updateProfile(data, token);
+ },
onSuccess(data) {
queryClient.setQueryData(['me'], data);
},
@@ -28,10 +35,13 @@ export function useUpdateProfile() {
export function useDeleteAccount() {
const queryClient = useQueryClient();
- const { signOut } = useAuth();
+ const { signOut, getIdToken } = useAuth();
return useMutation({
- mutationFn: userService.deleteAccount,
+ mutationFn: async () => {
+ const token = await getIdToken();
+ return userService.deleteAccount(token);
+ },
onSuccess() {
// `removeQueries` expects a filters object in v4+; provide the queryKey explicitly
queryClient.removeQueries({ queryKey: ['me'] });
diff --git a/packages/client/src/api/services/userService.test.ts b/packages/client/src/api/services/userService.test.ts
index 3f2509e..0e2b4f1 100644
--- a/packages/client/src/api/services/userService.test.ts
+++ b/packages/client/src/api/services/userService.test.ts
@@ -10,38 +10,54 @@ vi.mock('../axios', () => ({
},
}));
-// token store imported dynamically in interceptor test
+// service methods now accept an optional `token` argument which is turned into
+// an `Authorization` header. The tests only care that the header is passed when
+// a token is supplied.
describe('userService', () => {
beforeEach(() => vi.clearAllMocks());
describe('getMe', () => {
- it('calls GET /me and returns response data', async () => {
+ it('calls GET /me with Authorization header when token provided', async () => {
+ vi.mocked(axiosInstance.get).mockResolvedValue({ data: { uid: 'abc' } });
+ const result = await userService.getMe('my-token');
+ expect(vi.mocked(axiosInstance.get)).toHaveBeenCalledWith('/me', {
+ headers: { Authorization: 'Bearer my-token' },
+ });
+ expect(result).toEqual({ uid: 'abc' });
+ });
+
+ it('works without token (headers undefined)', async () => {
vi.mocked(axiosInstance.get).mockResolvedValue({ data: { uid: 'abc' } });
const result = await userService.getMe();
- expect(vi.mocked(axiosInstance.get)).toHaveBeenCalled();
- expect(vi.mocked(axiosInstance.get).mock.calls[0][0]).toBe('/me');
+ expect(vi.mocked(axiosInstance.get)).toHaveBeenCalledWith('/me', {
+ headers: undefined,
+ });
expect(result).toEqual({ uid: 'abc' });
});
});
describe('updateProfile', () => {
- it('calls PUT /me with provided data and returns response', async () => {
+ it('calls PUT /me with provided data and token header', async () => {
const updated = { uid: 'abc', name: 'New Name' };
vi.mocked(axiosInstance.put).mockResolvedValue({ data: updated });
- const result = await userService.updateProfile({ name: 'New Name' });
- expect(vi.mocked(axiosInstance.put)).toHaveBeenCalled();
- expect(vi.mocked(axiosInstance.put).mock.calls[0][0]).toBe('/me');
+ const result = await userService.updateProfile({ name: 'New Name' }, 'tok');
+ expect(vi.mocked(axiosInstance.put)).toHaveBeenCalledWith(
+ '/me',
+ { name: 'New Name' },
+ { headers: { Authorization: 'Bearer tok' } },
+ );
expect(result).toEqual(updated);
});
});
describe('deleteAccount', () => {
- it('calls DELETE /me', async () => {
+ it('calls DELETE /me with token header', async () => {
vi.mocked(axiosInstance.delete).mockResolvedValue({ data: {} });
- await userService.deleteAccount();
- expect(vi.mocked(axiosInstance.delete)).toHaveBeenCalled();
- expect(vi.mocked(axiosInstance.delete).mock.calls[0][0]).toBe('/me');
+ await userService.deleteAccount('xyz');
+ expect(vi.mocked(axiosInstance.delete)).toHaveBeenCalledWith('/me', {
+ headers: { Authorization: 'Bearer xyz' },
+ });
});
});
});
diff --git a/packages/client/src/api/services/userService.ts b/packages/client/src/api/services/userService.ts
index 338bb62..02f274b 100644
--- a/packages/client/src/api/services/userService.ts
+++ b/packages/client/src/api/services/userService.ts
@@ -1,16 +1,35 @@
import axios from '../axios';
import { MeResponse } from '../types';
-export async function getMe() {
- const res = await axios.get('/me');
+/**
+ * Helper to build the headers object when an ID token is available.
+ *
+ * Axios accepts `undefined` headers so callers can omit the token entirely
+ * during unit tests or unauthenticated calls.
+ */
+function makeAuthHeader(token?: string) {
+ return token ? { Authorization: `Bearer ${token}` } : undefined;
+}
+
+export async function getMe(token?: string) {
+ const res = await axios.get('/me', {
+ headers: makeAuthHeader(token),
+ });
return res.data;
}
-export async function updateProfile(data: Partial<{ name: string; picture: string }>) {
- const res = await axios.put('/me', data);
+export async function updateProfile(
+ data: Partial<{ name: string; picture: string }>,
+ token?: string,
+) {
+ const res = await axios.put('/me', data, {
+ headers: makeAuthHeader(token),
+ });
return res.data;
}
-export async function deleteAccount() {
- await axios.delete('/me');
+export async function deleteAccount(token?: string) {
+ await axios.delete('/me', {
+ headers: makeAuthHeader(token),
+ });
}
From da134a9f7762d125cb857f425c2f3fada0b02617 Mon Sep 17 00:00:00 2001
From: Daniel David
Date: Tue, 10 Mar 2026 12:13:37 +1100
Subject: [PATCH 6/8] feat(client): implement user account deletion and enhance
auth handling
- Added delete account functionality in the Dashboard component.
- Integrated axios interceptor to handle 401 responses and sign out users.
- Enhanced AuthContext to register unauthenticated handler for session expiry.
- Updated loading indicators and accessibility attributes in ProtectedRoute.
- Improved error handling in Login component for better user feedback.
- Added tests for new features and updated existing tests for consistency.
---
packages/client/src/api/axios.ts | 26 +++-
.../src/features/auth/AuthContext.test.tsx | 114 ++++++++++++++++++
.../client/src/features/auth/AuthContext.tsx | 14 ++-
.../src/features/common/ProtectedRoute.tsx | 7 +-
.../src/features/dashboard/Dashboard.test.tsx | 5 +
.../src/features/dashboard/Dashboard.tsx | 43 ++++++-
.../client/src/features/login/Login.test.tsx | 70 +++++++++++
packages/client/src/features/login/Login.tsx | 2 +-
packages/client/src/main.tsx | 9 +-
packages/server/src/config.ts | 51 +++++---
.../server/src/controllers/userController.ts | 29 ++++-
packages/server/src/index.ts | 1 +
packages/server/src/middleware/AppError.ts | 15 +++
packages/server/src/middleware/cors.ts | 6 +-
.../server/src/middleware/errorHandler.ts | 19 ++-
packages/server/src/middleware/logger.ts | 6 +-
.../server/src/repositories/userRepository.ts | 10 ++
packages/server/src/services/userService.ts | 10 ++
packages/server/tests/e2e/userRoutes.test.ts | 5 +-
packages/server/tests/unit/config.test.ts | 13 +-
.../unit/controllers/userController.test.ts | 5 +-
.../tests/unit/services/userService.test.ts | 68 +++++++++++
packages/server/vitest.config.ts | 5 +
23 files changed, 488 insertions(+), 45 deletions(-)
create mode 100644 packages/client/src/features/auth/AuthContext.test.tsx
create mode 100644 packages/client/src/features/login/Login.test.tsx
create mode 100644 packages/server/src/middleware/AppError.ts
create mode 100644 packages/server/tests/unit/services/userService.test.ts
diff --git a/packages/client/src/api/axios.ts b/packages/client/src/api/axios.ts
index 2fa8155..10f2e35 100644
--- a/packages/client/src/api/axios.ts
+++ b/packages/client/src/api/axios.ts
@@ -7,5 +7,29 @@ const instance = axios.create({
},
});
-// export type for later interceptors
+/**
+ * Register a callback to be invoked whenever the server returns 401.
+ * Call this from AuthContext so the user is signed out on an expired/revoked session.
+ */
+let _onUnauthenticated: (() => void) | null = null;
+export function setUnauthenticatedHandler(handler: () => void): void {
+ _onUnauthenticated = handler;
+}
+
+instance.interceptors.response.use(
+ (res) => res,
+ (error: unknown) => {
+ const status =
+ typeof error === 'object' &&
+ error !== null &&
+ 'response' in error
+ ? (error as { response?: { status?: number } }).response?.status
+ : undefined;
+ if (status === 401 && _onUnauthenticated) {
+ _onUnauthenticated();
+ }
+ return Promise.reject(error);
+ },
+);
+
export default instance;
diff --git a/packages/client/src/features/auth/AuthContext.test.tsx b/packages/client/src/features/auth/AuthContext.test.tsx
new file mode 100644
index 0000000..e7aea77
--- /dev/null
+++ b/packages/client/src/features/auth/AuthContext.test.tsx
@@ -0,0 +1,114 @@
+import { render, screen, act, waitFor } from '@testing-library/react';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { ReactNode } from 'react';
+import { AuthProvider, useAuth } from './AuthContext';
+import type { User } from 'firebase/auth';
+
+// firebase/auth functions are mocked here; src/firebase (auth, googleProvider) is
+// mocked globally in setupTests.ts.
+const mockOnAuthStateChanged = vi.fn();
+const mockSignInWithPopup = vi.fn();
+const mockFirebaseSignOut = vi.fn();
+
+vi.mock('firebase/auth', () => ({
+ onAuthStateChanged: (
+ _auth: unknown,
+ callback: (user: User | null) => void,
+ ) => {
+ mockOnAuthStateChanged.mockImplementation(callback);
+ return vi.fn(); // unsubscribe
+ },
+ signInWithPopup: (...args: unknown[]) => mockSignInWithPopup(...args),
+ signOut: (...args: unknown[]) => mockFirebaseSignOut(...args),
+ GoogleAuthProvider: class {},
+}));
+
+function wrapper({ children }: { children: ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
+
+/** Helper component that reads auth context and renders key state as text. */
+function AuthConsumer() {
+ const { user, loading } = useAuth();
+ return (
+
+ {String(loading)}
+ {user?.uid ?? 'none'}
+
+ );
+}
+
+describe('AuthProvider', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ mockFirebaseSignOut.mockResolvedValue(undefined);
+ });
+
+ it('is loading initially before onAuthStateChanged fires', () => {
+ // Don't invoke the callback — simulates the async resolution window.
+ mockOnAuthStateChanged.mockImplementation(() => vi.fn());
+ render(, { wrapper });
+ expect(screen.getByTestId('loading').textContent).toBe('true');
+ expect(screen.getByTestId('uid').textContent).toBe('none');
+ });
+
+ it('sets user and loading=false when onAuthStateChanged provides a user', async () => {
+ const fakeUser = { uid: 'u1' } as User;
+ render(, { wrapper });
+ act(() => mockOnAuthStateChanged(fakeUser));
+ await waitFor(() => expect(screen.getByTestId('loading').textContent).toBe('false'));
+ expect(screen.getByTestId('uid').textContent).toBe('u1');
+ });
+
+ it('sets user=null and loading=false when onAuthStateChanged fires with null', async () => {
+ render(, { wrapper });
+ act(() => mockOnAuthStateChanged(null));
+ await waitFor(() => expect(screen.getByTestId('loading').textContent).toBe('false'));
+ expect(screen.getByTestId('uid').textContent).toBe('none');
+ });
+
+ it('signInWithGoogle delegates to signInWithPopup', async () => {
+ mockSignInWithPopup.mockResolvedValue({});
+
+ let capturedCtx!: ReturnType;
+ function CtxCapture() {
+ capturedCtx = useAuth();
+ return null;
+ }
+ render(, { wrapper });
+ await act(() => capturedCtx.signInWithGoogle());
+ expect(mockSignInWithPopup).toHaveBeenCalled();
+ });
+
+ it('getIdToken throws when no user is logged in', async () => {
+ render(, { wrapper });
+ act(() => mockOnAuthStateChanged(null));
+
+ let capturedCtx!: ReturnType;
+ function CtxCapture() {
+ capturedCtx = useAuth();
+ return null;
+ }
+ render(, { wrapper });
+ await act(() => mockOnAuthStateChanged(null));
+ await expect(capturedCtx.getIdToken()).rejects.toThrow('Not authenticated');
+ });
+});
+
+describe('useAuth', () => {
+ it('throws when used outside AuthProvider', () => {
+ // Suppress expected React error boundary noise
+ vi.spyOn(console, 'error').mockImplementation(() => {});
+ expect(() =>
+ render(
+
+
+ ,
+ ),
+ ).toThrow('useAuth must be used inside ');
+ });
+});
diff --git a/packages/client/src/features/auth/AuthContext.tsx b/packages/client/src/features/auth/AuthContext.tsx
index 3000cd7..2335024 100644
--- a/packages/client/src/features/auth/AuthContext.tsx
+++ b/packages/client/src/features/auth/AuthContext.tsx
@@ -1,5 +1,6 @@
import {
createContext,
+ useCallback,
useContext,
useEffect,
useState,
@@ -13,6 +14,7 @@ import {
signOut as firebaseSignOut,
} from 'firebase/auth';
import { auth, googleProvider } from '../../firebase';
+import { setUnauthenticatedHandler } from '../../api/axios';
interface AuthContextValue {
user: User | null;
@@ -43,11 +45,19 @@ export function AuthProvider({ children }: { children: ReactNode }) {
const queryClient = useQueryClient();
- const signOut = async () => {
+ const signOut = useCallback(async () => {
await firebaseSignOut(auth);
// clear any cached server data when user signs out
queryClient.clear();
- };
+ }, [queryClient]);
+
+ // Register the axios interceptor so any 401 response automatically signs the user out.
+ // Re-registers whenever signOut identity changes (i.e. when queryClient changes).
+ useEffect(() => {
+ setUnauthenticatedHandler(() => {
+ signOut().catch(console.error);
+ });
+ }, [signOut]);
const getIdToken = async (): Promise => {
if (!user) throw new Error('Not authenticated');
diff --git a/packages/client/src/features/common/ProtectedRoute.tsx b/packages/client/src/features/common/ProtectedRoute.tsx
index 1de035f..dd2e6b8 100644
--- a/packages/client/src/features/common/ProtectedRoute.tsx
+++ b/packages/client/src/features/common/ProtectedRoute.tsx
@@ -7,7 +7,12 @@ export default function ProtectedRoute({ children }: { children: ReactNode }) {
if (loading) {
return (
-
+
Loading…
);
diff --git a/packages/client/src/features/dashboard/Dashboard.test.tsx b/packages/client/src/features/dashboard/Dashboard.test.tsx
index 2d3f895..df2b52d 100644
--- a/packages/client/src/features/dashboard/Dashboard.test.tsx
+++ b/packages/client/src/features/dashboard/Dashboard.test.tsx
@@ -10,6 +10,7 @@ import type { User } from 'firebase/auth';
vi.mock('../../api/hooks', () => ({
useMe: vi.fn(),
useUpdateProfile: vi.fn(),
+ useDeleteAccount: vi.fn(),
}));
const mockUser = {
@@ -44,6 +45,10 @@ describe('Dashboard', () => {
mutate: vi.fn(),
isPending: false,
} as any);
+ vi.mocked(hooks.useDeleteAccount).mockReturnValue({
+ mutate: vi.fn(),
+ isPending: false,
+ } as any);
});
afterEach(() => vi.restoreAllMocks());
diff --git a/packages/client/src/features/dashboard/Dashboard.tsx b/packages/client/src/features/dashboard/Dashboard.tsx
index 56128ce..2d67450 100644
--- a/packages/client/src/features/dashboard/Dashboard.tsx
+++ b/packages/client/src/features/dashboard/Dashboard.tsx
@@ -1,11 +1,14 @@
+import { useState } from 'react';
import { useAuth } from '../auth/AuthContext';
-import { useMe, useUpdateProfile } from '../../api/hooks';
+import { useMe, useUpdateProfile, useDeleteAccount } from '../../api/hooks';
export default function Dashboard() {
const { user, signOut } = useAuth();
const { data, isLoading, error, refetch } = useMe();
+ const [updateSuccess, setUpdateSuccess] = useState(false);
const updateMutation = useUpdateProfile();
+ const deleteMutation = useDeleteAccount();
const apiResult = error ? String(error) : data ? JSON.stringify(data, null, 2) : null;
const fetching = isLoading;
@@ -14,7 +17,18 @@ export default function Dashboard() {
refetch();
};
- const callUpdate = () => updateMutation.mutate({ name: 'New Name' });
+ const callUpdate = () => {
+ setUpdateSuccess(false);
+ updateMutation.mutate(
+ { name: 'New Name' },
+ { onSuccess: () => setUpdateSuccess(true) },
+ );
+ };
+
+ const handleDeleteAccount = () => {
+ if (!window.confirm('Are you sure? This will permanently delete your account.')) return;
+ deleteMutation.mutate();
+ };
return (
@@ -23,6 +37,7 @@ export default function Dashboard() {
Dashboard
+
+
+
Danger Zone
+
+ Permanently deletes your account and revokes all active sessions. This cannot be undone.
+
+
+ {deleteMutation.isPending ? 'Deleting…' : 'Delete account'}
+
+
);
diff --git a/packages/client/src/features/login/Login.test.tsx b/packages/client/src/features/login/Login.test.tsx
new file mode 100644
index 0000000..e3d0add
--- /dev/null
+++ b/packages/client/src/features/login/Login.test.tsx
@@ -0,0 +1,70 @@
+import { render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { MemoryRouter } from 'react-router-dom';
+import Login from './Login';
+import * as AuthContext from '../auth/AuthContext';
+import type { User } from 'firebase/auth';
+
+const mockNavigate = vi.fn();
+vi.mock('react-router-dom', async (importOriginal) => {
+ const actual = await importOriginal();
+ return { ...actual, useNavigate: () => mockNavigate };
+});
+
+function renderLogin(authOverrides: Partial> = {}) {
+ vi.spyOn(AuthContext, 'useAuth').mockReturnValue({
+ user: null,
+ loading: false,
+ signInWithGoogle: vi.fn<() => Promise>().mockResolvedValue(),
+ signOut: vi.fn<() => Promise>().mockResolvedValue(),
+ getIdToken: vi.fn<() => Promise>().mockResolvedValue(''),
+ ...authOverrides,
+ });
+
+ return render(
+
+
+ ,
+ );
+}
+
+describe('Login', () => {
+ afterEach(() => vi.restoreAllMocks());
+
+ it('renders the sign-in button', () => {
+ renderLogin();
+ expect(screen.getByRole('button', { name: /sign in with google/i })).toBeInTheDocument();
+ });
+
+ it('redirects to / when user is already authenticated', () => {
+ renderLogin({ user: { uid: '1' } as User });
+ expect(mockNavigate).toHaveBeenCalledWith('/', { replace: true });
+ });
+
+ it('calls signInWithGoogle when the button is clicked', async () => {
+ const signInWithGoogle = vi.fn<() => Promise>().mockResolvedValue();
+ renderLogin({ signInWithGoogle });
+ await userEvent.click(screen.getByRole('button', { name: /sign in with google/i }));
+ expect(signInWithGoogle).toHaveBeenCalledOnce();
+ });
+
+ it('displays the error message when sign-in throws an Error', async () => {
+ const signInWithGoogle = vi
+ .fn<() => Promise>()
+ .mockRejectedValue(new Error('popup-closed'));
+ renderLogin({ signInWithGoogle });
+ await userEvent.click(screen.getByRole('button', { name: /sign in with google/i }));
+ await waitFor(() =>
+ expect(screen.getByText('popup-closed')).toBeInTheDocument(),
+ );
+ });
+
+ it('shows a fallback message when sign-in throws a non-Error value', async () => {
+ const signInWithGoogle = vi.fn<() => Promise>().mockRejectedValue('unexpected');
+ renderLogin({ signInWithGoogle });
+ await userEvent.click(screen.getByRole('button', { name: /sign in with google/i }));
+ await waitFor(() =>
+ expect(screen.getByText('Authentication failed')).toBeInTheDocument(),
+ );
+ });
+});
diff --git a/packages/client/src/features/login/Login.tsx b/packages/client/src/features/login/Login.tsx
index b02ce97..976c9a3 100644
--- a/packages/client/src/features/login/Login.tsx
+++ b/packages/client/src/features/login/Login.tsx
@@ -17,7 +17,7 @@ export default function Login() {
await signInWithGoogle();
} catch (err) {
console.error('Google sign-in failed', err);
- setError((err as Error).message || 'Authentication failed');
+ setError(err instanceof Error ? err.message : 'Authentication failed');
}
};
diff --git a/packages/client/src/main.tsx b/packages/client/src/main.tsx
index 15c3cbc..ce993ab 100644
--- a/packages/client/src/main.tsx
+++ b/packages/client/src/main.tsx
@@ -23,7 +23,14 @@ const queryClient = new QueryClient({
},
});
-createRoot(document.getElementById('root')!).render(
+const rootElement = document.getElementById('root');
+if (!rootElement) {
+ throw new Error(
+ 'Root element #root not found. Ensure index.html contains .',
+ );
+}
+
+createRoot(rootElement).render(
diff --git a/packages/server/src/config.ts b/packages/server/src/config.ts
index f200ab1..9a5b37b 100644
--- a/packages/server/src/config.ts
+++ b/packages/server/src/config.ts
@@ -2,32 +2,43 @@ import { z } from 'zod';
// Define schema for all expected environment variables. Any missing/invalid
// values cause the server to crash early with a helpful message.
-const EnvSchema = z.object({
- PORT: z.string().optional(),
- CORS_ORIGIN: z.string().url().default('http://localhost:5173'),
- RATE_LIMIT_WINDOW_MINUTES: z
- .string()
- .transform((s) => parseInt(s, 10))
- .optional()
- .default(15),
- RATE_LIMIT_MAX: z
- .string()
- .transform((s) => parseInt(s, 10))
- .optional()
- .default(100),
+const EnvSchema = z
+ .object({
+ PORT: z.string().optional(),
+ // Accept a comma-separated list of origins so staging/preview envs work without code changes.
+ CORS_ORIGIN: z.string().default('http://localhost:5173'),
+ // z.coerce.number() handles string→number and rejects NaN, avoiding silent rate-limit breakage.
+ RATE_LIMIT_WINDOW_MINUTES: z.coerce.number().int().positive().optional().default(15),
+ RATE_LIMIT_MAX: z.coerce.number().int().positive().optional().default(100),
- // Firebase credentials - one of two options should be provided.
- FIREBASE_PROJECT_ID: z.string().optional(),
- FIREBASE_CLIENT_EMAIL: z.string().email().optional(),
- FIREBASE_PRIVATE_KEY: z.string().optional(),
- FIREBASE_SERVICE_ACCOUNT_JSON: z.string().optional(),
-});
+ // Firebase credentials — either the full JSON blob or all three individual vars must be set.
+ FIREBASE_PROJECT_ID: z.string().optional(),
+ FIREBASE_CLIENT_EMAIL: z.string().email().optional(),
+ FIREBASE_PRIVATE_KEY: z.string().optional(),
+ FIREBASE_SERVICE_ACCOUNT_JSON: z.string().optional(),
+ })
+ .superRefine((env, ctx) => {
+ const hasJson = Boolean(env.FIREBASE_SERVICE_ACCOUNT_JSON);
+ const hasIndividual =
+ Boolean(env.FIREBASE_PROJECT_ID) &&
+ Boolean(env.FIREBASE_CLIENT_EMAIL) &&
+ Boolean(env.FIREBASE_PRIVATE_KEY);
+ if (!hasJson && !hasIndividual) {
+ ctx.addIssue({
+ code: z.ZodIssueCode.custom,
+ message:
+ 'Firebase credentials are missing. Provide FIREBASE_SERVICE_ACCOUNT_JSON or all of ' +
+ 'FIREBASE_PROJECT_ID, FIREBASE_CLIENT_EMAIL, and FIREBASE_PRIVATE_KEY.',
+ });
+ }
+ });
const _env = EnvSchema.parse(process.env);
export const config = {
port: parseInt(_env.PORT ?? '3001', 10),
- corsOrigin: _env.CORS_ORIGIN,
+ // Support comma-separated origins, e.g. "https://app.com,https://staging.app.com"
+ corsOrigin: _env.CORS_ORIGIN.split(',').map((s) => s.trim()),
rateLimit: {
windowMs: _env.RATE_LIMIT_WINDOW_MINUTES * 60 * 1000,
max: _env.RATE_LIMIT_MAX,
diff --git a/packages/server/src/controllers/userController.ts b/packages/server/src/controllers/userController.ts
index 88f2e5f..3888700 100644
--- a/packages/server/src/controllers/userController.ts
+++ b/packages/server/src/controllers/userController.ts
@@ -1,6 +1,6 @@
import { Response, NextFunction } from 'express';
import { z } from 'zod';
-import { getUserProfile, applyProfileUpdate } from '../services/userService';
+import { getUserProfile, applyProfileUpdate, deleteUserAccount } from '../services/userService';
import { AuthenticatedRequest } from '../middleware/authMiddleware';
const UpdateProfileSchema = z.object({
@@ -8,6 +8,21 @@ const UpdateProfileSchema = z.object({
picture: z.string().url().optional(),
});
+/**
+ * Guards that req.user is populated (set by authMiddleware).
+ * Returns true and sends 401 when the guard fails, allowing callers to early-return.
+ */
+function requireUser(
+ req: AuthenticatedRequest,
+ res: Response,
+): req is AuthenticatedRequest & { user: NonNullable } {
+ if (!req.user) {
+ res.status(401).json({ error: 'Unauthorized' });
+ return false;
+ }
+ return true;
+}
+
/** GET /api/me — returns the authenticated user's profile. */
export async function getMe(
req: AuthenticatedRequest,
@@ -15,7 +30,8 @@ export async function getMe(
next: NextFunction,
): Promise {
try {
- res.json(getUserProfile(req.user!));
+ if (!requireUser(req, res)) return;
+ res.json(getUserProfile(req.user));
} catch (err) {
next(err);
}
@@ -28,27 +44,30 @@ export async function updateMe(
next: NextFunction,
): Promise {
try {
+ if (!requireUser(req, res)) return;
const parsed = UpdateProfileSchema.safeParse(req.body);
if (!parsed.success) {
res.status(400).json({ error: parsed.error.issues[0]?.message ?? 'Invalid request body' });
return;
}
- res.json(applyProfileUpdate(req.user!, parsed.data));
+ res.json(applyProfileUpdate(req.user, parsed.data));
} catch (err) {
next(err);
}
}
-/** DELETE /api/me — removes the authenticated user's account. */
+/** DELETE /api/me — revokes Firebase sessions and permanently deletes the account. */
export async function deleteMe(
req: AuthenticatedRequest,
res: Response,
next: NextFunction,
): Promise {
try {
- // In production: revoke Firebase session + delete from database here.
+ if (!requireUser(req, res)) return;
+ await deleteUserAccount(req.user.uid);
res.status(204).send();
} catch (err) {
next(err);
}
}
+
diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts
index e0271fe..ec5b739 100644
--- a/packages/server/src/index.ts
+++ b/packages/server/src/index.ts
@@ -1,4 +1,5 @@
import 'dotenv/config';
+import './firebase'; // eagerly initialise Admin SDK — crashes at startup on bad credentials
import { createApp } from './app';
import { config } from './config';
diff --git a/packages/server/src/middleware/AppError.ts b/packages/server/src/middleware/AppError.ts
new file mode 100644
index 0000000..2d7513a
--- /dev/null
+++ b/packages/server/src/middleware/AppError.ts
@@ -0,0 +1,15 @@
+/**
+ * Typed application error that carries an HTTP status code so the central
+ * errorHandler can forward the correct status instead of always returning 500.
+ */
+export class AppError extends Error {
+ constructor(
+ public readonly statusCode: number,
+ message: string,
+ ) {
+ super(message);
+ this.name = 'AppError';
+ // Restore prototype chain for `instanceof` checks across transpilation targets.
+ Object.setPrototypeOf(this, new.target.prototype);
+ }
+}
diff --git a/packages/server/src/middleware/cors.ts b/packages/server/src/middleware/cors.ts
index 3c1f29a..068ed81 100644
--- a/packages/server/src/middleware/cors.ts
+++ b/packages/server/src/middleware/cors.ts
@@ -1,6 +1,10 @@
import { Express } from 'express';
import cors from 'cors';
-export function applyCors(app: Express, origin: string) {
+/**
+ * Applies CORS middleware. Accepts a single origin string or an array so that
+ * comma-separated values from CORS_ORIGIN env var can be forwarded directly.
+ */
+export function applyCors(app: Express, origin: string | string[]) {
app.use(cors({ origin, credentials: true }));
}
diff --git a/packages/server/src/middleware/errorHandler.ts b/packages/server/src/middleware/errorHandler.ts
index 0430898..43b2763 100644
--- a/packages/server/src/middleware/errorHandler.ts
+++ b/packages/server/src/middleware/errorHandler.ts
@@ -1,13 +1,22 @@
import { NextFunction, Request, Response } from 'express';
+import { AppError } from './AppError';
-// central error handler – should be the last middleware registered
-export function errorHandler(err: unknown, _req: Request, res: Response, _next: NextFunction) {
- console.error(err);
+/** Central error handler – must be the last middleware registered in app.ts. */
+export function errorHandler(err: unknown, req: Request, res: Response, _next: NextFunction) {
+ // Structured log: include method + path for quick triage; never log auth tokens.
+ const context = `${req.method} ${req.path}`;
+ if (err instanceof AppError) {
+ // Operational errors are expected — log at warn level, not error.
+ console.warn(`[${context}] AppError ${err.statusCode}: ${err.message}`);
+ } else {
+ console.error(`[${context}] Unhandled error:`, err);
+ }
if (res.headersSent) {
return;
}
- // customize further based on error type if desired
- res.status(500).json({ error: 'Internal Server Error' });
+ const statusCode = err instanceof AppError ? err.statusCode : 500;
+ const message = err instanceof AppError ? err.message : 'Internal Server Error';
+ res.status(statusCode).json({ error: message });
}
diff --git a/packages/server/src/middleware/logger.ts b/packages/server/src/middleware/logger.ts
index 28981ef..a32b7bf 100644
--- a/packages/server/src/middleware/logger.ts
+++ b/packages/server/src/middleware/logger.ts
@@ -2,6 +2,8 @@ import { Express } from 'express';
import morgan from 'morgan';
export function applyLogging(app: Express) {
- // simple request logger using morgan
- app.use(morgan('tiny'));
+ // 'combined' in production includes user-agent, referrer, and response time;
+ // 'dev' in development gives colour-coded concise output.
+ const format = process.env.NODE_ENV === 'production' ? 'combined' : 'dev';
+ app.use(morgan(format));
}
diff --git a/packages/server/src/repositories/userRepository.ts b/packages/server/src/repositories/userRepository.ts
index 3b25926..ed5f895 100644
--- a/packages/server/src/repositories/userRepository.ts
+++ b/packages/server/src/repositories/userRepository.ts
@@ -5,3 +5,13 @@ import admin from '../firebase';
export function verifyIdToken(idToken: string): Promise {
return admin.auth().verifyIdToken(idToken);
}
+
+/**
+ * Revokes all refresh tokens and permanently deletes the Firebase Auth user.
+ * Must be called before any database cleanup in the service layer.
+ */
+export async function deleteUser(uid: string): Promise {
+ await admin.auth().revokeRefreshTokens(uid);
+ await admin.auth().deleteUser(uid);
+}
+
diff --git a/packages/server/src/services/userService.ts b/packages/server/src/services/userService.ts
index f1695c9..ac1fc80 100644
--- a/packages/server/src/services/userService.ts
+++ b/packages/server/src/services/userService.ts
@@ -1,4 +1,5 @@
import type { DecodedIdToken } from 'firebase-admin/auth';
+import { deleteUser } from '../repositories/userRepository';
/** Shape returned by all /me endpoints. */
export interface UserProfile {
@@ -24,3 +25,12 @@ export function applyProfileUpdate(
): UserProfile {
return { ...getUserProfile(decoded), ...data };
}
+
+/**
+ * Permanently removes the user from Firebase Auth.
+ * Extend this function to purge any database records before the Auth deletion.
+ */
+export async function deleteUserAccount(uid: string): Promise {
+ await deleteUser(uid);
+}
+
diff --git a/packages/server/tests/e2e/userRoutes.test.ts b/packages/server/tests/e2e/userRoutes.test.ts
index 4bf3594..e98b520 100644
--- a/packages/server/tests/e2e/userRoutes.test.ts
+++ b/packages/server/tests/e2e/userRoutes.test.ts
@@ -5,11 +5,12 @@ import { describe, it, expect, beforeEach, vi } from 'vitest';
vi.mock('../../src/firebase', () => ({ default: {} }));
vi.mock('../../src/repositories/userRepository', () => ({
verifyIdToken: vi.fn(),
+ deleteUser: vi.fn(),
}));
import request from 'supertest';
import { createApp } from '../../src/app';
-import { verifyIdToken } from '../../src/repositories/userRepository';
+import { verifyIdToken, deleteUser } from '../../src/repositories/userRepository';
const decoded: any = { uid: 'user123', email: 'a@b.com', name: 'Alice', picture: null };
@@ -74,7 +75,9 @@ describe('e2e /api/me', () => {
it('DELETE returns 204 with valid token', async () => {
vi.mocked(verifyIdToken).mockResolvedValue(decoded);
+ vi.mocked(deleteUser).mockResolvedValue();
const app = createApp();
await request(app).delete('/api/me').set('Authorization', 'Bearer good').expect(204);
+ expect(deleteUser).toHaveBeenCalledWith(decoded.uid);
});
});
diff --git a/packages/server/tests/unit/config.test.ts b/packages/server/tests/unit/config.test.ts
index a279bc5..1b9c083 100644
--- a/packages/server/tests/unit/config.test.ts
+++ b/packages/server/tests/unit/config.test.ts
@@ -4,6 +4,8 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
describe('config parsing', () => {
beforeEach(() => {
vi.resetModules();
+ // Provide minimal Firebase credentials so superRefine passes in every test.
+ vi.stubEnv('FIREBASE_SERVICE_ACCOUNT_JSON', '{}');
});
afterEach(() => {
@@ -15,7 +17,8 @@ describe('config parsing', () => {
vi.stubEnv('CORS_ORIGIN', 'http://localhost:5173');
const { config } = await import('../../src/config');
expect(config.port).toBe(3001);
- expect(config.corsOrigin).toBe('http://localhost:5173');
+ // corsOrigin is now always an array (supports comma-separated values)
+ expect(config.corsOrigin).toEqual(['http://localhost:5173']);
expect(config.rateLimit.windowMs).toBe(15 * 60 * 1000);
expect(config.rateLimit.max).toBe(100);
});
@@ -25,7 +28,13 @@ describe('config parsing', () => {
vi.stubEnv('CORS_ORIGIN', 'https://example.com');
const { config } = await import('../../src/config');
expect(config.port).toBe(4000);
- expect(config.corsOrigin).toBe('https://example.com');
+ expect(config.corsOrigin).toEqual(['https://example.com']);
+ });
+
+ it('supports comma-separated CORS_ORIGIN values', async () => {
+ vi.stubEnv('CORS_ORIGIN', 'https://app.com,https://staging.app.com');
+ const { config } = await import('../../src/config');
+ expect(config.corsOrigin).toEqual(['https://app.com', 'https://staging.app.com']);
});
it('config exports expected shape', async () => {
diff --git a/packages/server/tests/unit/controllers/userController.test.ts b/packages/server/tests/unit/controllers/userController.test.ts
index 3864a4b..568e363 100644
--- a/packages/server/tests/unit/controllers/userController.test.ts
+++ b/packages/server/tests/unit/controllers/userController.test.ts
@@ -4,10 +4,11 @@ import { describe, it, expect, vi } from 'vitest';
vi.mock('../../../src/services/userService', () => ({
getUserProfile: vi.fn(),
applyProfileUpdate: vi.fn(),
+ deleteUserAccount: vi.fn(),
}));
import { getMe, updateMe, deleteMe } from '../../../src/controllers/userController';
-import { getUserProfile, applyProfileUpdate } from '../../../src/services/userService';
+import { getUserProfile, applyProfileUpdate, deleteUserAccount } from '../../../src/services/userService';
const fakeDecoded: any = { uid: 'abc', email: 'test@example.com', name: 'Test User', picture: undefined };
@@ -54,11 +55,13 @@ describe('userController.updateMe', () => {
describe('userController.deleteMe', () => {
it('returns 204 with no body', async () => {
+ vi.mocked(deleteUserAccount).mockResolvedValue();
const req: any = { user: fakeDecoded };
const res: any = { status: vi.fn().mockReturnThis(), send: vi.fn() };
const next = vi.fn();
await deleteMe(req, res, next);
+ expect(deleteUserAccount).toHaveBeenCalledWith(fakeDecoded.uid);
expect(res.status).toHaveBeenCalledWith(204);
expect(res.send).toHaveBeenCalled();
});
diff --git a/packages/server/tests/unit/services/userService.test.ts b/packages/server/tests/unit/services/userService.test.ts
new file mode 100644
index 0000000..e99e0e0
--- /dev/null
+++ b/packages/server/tests/unit/services/userService.test.ts
@@ -0,0 +1,68 @@
+///
+import { describe, it, expect, vi, beforeEach } from 'vitest';
+
+vi.mock('../../../src/repositories/userRepository', () => ({
+ deleteUser: vi.fn(),
+}));
+
+import { getUserProfile, applyProfileUpdate, deleteUserAccount } from '../../../src/services/userService';
+import { deleteUser } from '../../../src/repositories/userRepository';
+import type { DecodedIdToken } from 'firebase-admin/auth';
+
+const fakeToken = {
+ uid: 'uid-1',
+ email: 'alice@example.com',
+ name: 'Alice',
+ picture: 'https://example.com/alice.jpg',
+} as unknown as DecodedIdToken;
+
+describe('getUserProfile', () => {
+ it('extracts the expected fields from a decoded token', () => {
+ expect(getUserProfile(fakeToken)).toEqual({
+ uid: 'uid-1',
+ email: 'alice@example.com',
+ name: 'Alice',
+ picture: 'https://example.com/alice.jpg',
+ });
+ });
+
+ it('omits fields that are undefined on the token', () => {
+ const sparse = { uid: 'uid-2' } as unknown as DecodedIdToken;
+ const profile = getUserProfile(sparse);
+ expect(profile.uid).toBe('uid-2');
+ expect(profile.email).toBeUndefined();
+ });
+});
+
+describe('applyProfileUpdate', () => {
+ it('merges update data onto the base profile', () => {
+ const result = applyProfileUpdate(fakeToken, { name: 'Bob' });
+ expect(result).toEqual({
+ uid: 'uid-1',
+ email: 'alice@example.com',
+ name: 'Bob',
+ picture: 'https://example.com/alice.jpg',
+ });
+ });
+
+ it('does not mutate update data; missing keys leave original values intact', () => {
+ const result = applyProfileUpdate(fakeToken, {});
+ expect(result.name).toBe('Alice');
+ });
+});
+
+describe('deleteUserAccount', () => {
+ beforeEach(() => vi.resetAllMocks());
+
+ it('delegates to the userRepository deleteUser with the correct uid', async () => {
+ vi.mocked(deleteUser).mockResolvedValue();
+ await deleteUserAccount('uid-1');
+ expect(deleteUser).toHaveBeenCalledOnce();
+ expect(deleteUser).toHaveBeenCalledWith('uid-1');
+ });
+
+ it('propagates errors thrown by the repository', async () => {
+ vi.mocked(deleteUser).mockRejectedValue(new Error('Firebase error'));
+ await expect(deleteUserAccount('uid-1')).rejects.toThrow('Firebase error');
+ });
+});
diff --git a/packages/server/vitest.config.ts b/packages/server/vitest.config.ts
index 685063c..4156fcb 100644
--- a/packages/server/vitest.config.ts
+++ b/packages/server/vitest.config.ts
@@ -5,6 +5,11 @@ export default defineConfig({
globals: true,
environment: 'node',
include: ['tests/**/*.test.ts'],
+ // Provide minimal Firebase credentials so config.ts passes validation in
+ // all test files without requiring individual stubEnv calls.
+ env: {
+ FIREBASE_SERVICE_ACCOUNT_JSON: '{}',
+ },
coverage: {
provider: 'istanbul',
reporter: ['text', 'html'],
From 0caebcfa6a4ae46b67b2e44c45d501e290a62250 Mon Sep 17 00:00:00 2001
From: Daniel David
Date: Tue, 10 Mar 2026 13:00:05 +1100
Subject: [PATCH 7/8] chore: update ESLint configuration and add TypeScript
support
- Added ESLint configuration to enforce TypeScript and React best practices.
- Introduced .eslintignore to exclude certain files and directories from linting.
- Updated package.json files to include ESLint as a dev dependency.
- Refactored various components to specify return types for better type safety.
- Enhanced API service functions with explicit return types.
- Updated tests to align with new type definitions and improve type safety.
---
.eslintignore | 7 +
.eslintrc.cjs | 67 +
docs/eslint.md | 121 +
package-lock.json | 5041 +++++++++++++----
package.json | 9 +-
packages/client/src/App.tsx | 2 +-
packages/client/src/api/hooks.ts | 7 +-
.../client/src/api/services/userService.ts | 8 +-
.../client/src/features/auth/AuthContext.tsx | 6 +-
.../src/features/common/ErrorBoundary.tsx | 13 +-
.../src/features/common/ProtectedRoute.tsx | 2 +-
.../src/features/dashboard/Dashboard.test.tsx | 18 +-
.../src/features/dashboard/Dashboard.tsx | 2 +-
packages/client/src/features/login/Login.tsx | 2 +-
packages/client/vitest.config.ts | 2 +-
packages/server/package.json | 2 +-
packages/server/src/app.ts | 4 +-
packages/server/src/middleware/cors.ts | 2 +-
.../server/src/middleware/errorHandler.ts | 2 +-
packages/server/src/middleware/logger.ts | 2 +-
packages/server/src/middleware/rateLimiter.ts | 2 +-
packages/server/src/middleware/security.ts | 2 +-
packages/server/tests/e2e/health.test.ts | 1 -
packages/server/tests/e2e/userRoutes.test.ts | 4 +-
packages/server/tests/unit/config.test.ts | 1 -
.../unit/controllers/userController.test.ts | 33 +-
.../unit/middleware/authMiddleware.test.ts | 15 +-
.../tests/unit/services/userService.test.ts | 1 -
28 files changed, 4343 insertions(+), 1035 deletions(-)
create mode 100644 .eslintignore
create mode 100644 .eslintrc.cjs
create mode 100644 docs/eslint.md
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..bc06c97
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,7 @@
+node_modules
+dist
+build
+coverage
+.venv
+.vscode
+.DS_Store
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
new file mode 100644
index 0000000..8f28ec3
--- /dev/null
+++ b/.eslintrc.cjs
@@ -0,0 +1,67 @@
+module.exports = {
+ root: true,
+ env: { es2021: true },
+ parser: '@typescript-eslint/parser',
+ parserOptions: {
+ tsconfigRootDir: __dirname,
+ sourceType: 'module'
+ },
+ plugins: ['@typescript-eslint', 'import', 'react', 'react-hooks'],
+ extends: [
+ 'eslint:recommended',
+ 'plugin:@typescript-eslint/recommended',
+ 'plugin:import/recommended',
+ 'plugin:import/typescript',
+ 'plugin:react/recommended'
+ ],
+ settings: {
+ react: { version: 'detect' },
+ 'import/resolver': {
+ typescript: {
+ project: ['./tsconfig.json', './packages/*/tsconfig.json']
+ }
+ }
+ },
+ rules: {
+ 'import/no-unresolved': 'error',
+ '@typescript-eslint/explicit-module-boundary-types': 'error',
+ '@typescript-eslint/no-explicit-any': 'error',
+ '@typescript-eslint/triple-slash-reference': 'error',
+ 'no-console': ['warn', { allow: ['warn', 'error'] }],
+ '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
+ 'import/default': 'off',
+ 'react/react-in-jsx-scope': 'off'
+ },
+ overrides: [
+ {
+ files: ['packages/client/src/**/*.{ts,tsx}'],
+ env: { browser: true, node: true, es2021: true },
+ parserOptions: { project: ['./packages/client/tsconfig.json'] },
+ rules: { 'import/no-named-as-default-member': 'off' }
+ },
+ {
+ files: ['packages/server/src/**/*.{ts,tsx}'],
+ env: { node: true, es2021: true },
+ parserOptions: { project: ['./packages/server/tsconfig.json'] },
+ rules: {
+ '@typescript-eslint/no-var-requires': 'off',
+ 'import/no-named-as-default-member': 'off',
+ 'import/no-named-as-default': 'off',
+ 'no-console': 'off'
+ }
+ },
+ {
+ files: ['**/*.test.ts', '**/*.test.tsx', 'tests/**', 'packages/**/tests/**'],
+ env: { es2021: true },
+ rules: {
+ 'import/no-extraneous-dependencies': 'off',
+ 'react/display-name': 'off'
+ }
+ },
+ {
+ files: ['**/*.config.ts', '**/*vite*.ts', '**/*vitest*.ts', '**/*tailwind*.ts'],
+ parserOptions: { tsconfigRootDir: __dirname },
+ rules: { '@typescript-eslint/no-explicit-any': 'off' }
+ }
+ ]
+};
diff --git a/docs/eslint.md b/docs/eslint.md
new file mode 100644
index 0000000..1f13432
--- /dev/null
+++ b/docs/eslint.md
@@ -0,0 +1,121 @@
+# ESLint — configuration and usage
+
+This repository uses a single, root ESLint configuration to enforce TypeScript
+and React best-practices across both the client and server packages.
+
+See the active config at [.eslintrc.cjs](.eslintrc.cjs) and the ignore
+patterns at [.eslintignore](.eslintignore).
+
+## Overview
+
+- Parser: `@typescript-eslint/parser` with `tsconfigRootDir` set to the repo root.
+- Plugins: `@typescript-eslint`, `import`, `react`, `react-hooks`.
+- Extends: `eslint:recommended`, `plugin:@typescript-eslint/recommended`,
+ `plugin:import/recommended`, `plugin:import/typescript`, `plugin:react/recommended`.
+- Import resolver is configured for TypeScript projects so path resolution
+ works across `packages/*/tsconfig.json`.
+
+## Important rules enforced
+
+- `@typescript-eslint/explicit-module-boundary-types: error`
+ - Requires explicit return types on exported functions/exports. This aligns
+ with the repo's TypeScript guideline to keep public API types explicit.
+- `@typescript-eslint/no-explicit-any: error`
+ - Disallows `any` except where explicitly permitted by overrides (config files
+ and some test helpers). Prefer `unknown` + type-guards instead.
+- `@typescript-eslint/triple-slash-reference: error`
+ - Enforces using `import` style typing for tests (Vitest) rather than
+ triple-slash references.
+- `@typescript-eslint/no-unused-vars` with `_`-prefix ignore
+ - Allows intentionally unused args/vars when prefixed with `_`.
+- `no-console: ['warn', { allow: ['warn','error'] }]`
+ - Console is warned by default but allowed for `warn`/`error`. The server
+ override further relaxes this to support logging middleware.
+- `import/no-unresolved: error`
+ - Ensures imports resolve correctly; some import checks are pragmatically
+ disabled in overrides where false-positives occurred.
+
+## Overrides (high level)
+
+- `packages/client/src/**/*.{ts,tsx}`
+ - `env`: browser/node. Type-aware linting uses `packages/client/tsconfig.json`.
+ - Disables `import/no-named-as-default-member` where it produced false
+ positives for certain imports.
+
+- `packages/server/src/**/*.{ts,tsx}`
+ - `env`: node. Type-aware linting uses `packages/server/tsconfig.json`.
+ - Pragmatic relaxations: allow CommonJS `require` usage in a few places,
+ disable some `import/*` checks that produced false positives, and allow
+ `console` for structured logging in middleware/services.
+
+- Tests: `**/*.test.ts`, `**/*.test.tsx`, `tests/**`
+ - Test environment uses standard ES2021 globals; `import/no-extraneous-dependencies`
+ is turned off for test files.
+
+- Config files (`**/*.config.ts`, `**/*vite*.ts`, `**/*vitest*.ts`,
+ `**/*tailwind*.ts`)
+ - These files are excluded from the strict `no-explicit-any` rule to ease
+ interop with tooling configs (the codebase still prefers typed configs
+ where practical).
+
+## Files of interest
+
+- Root ESLint config: [.eslintrc.cjs](.eslintrc.cjs)
+- Ignore file: [.eslintignore](.eslintignore)
+- Root dev dependencies: [package.json](package.json)
+- Client lint script: [packages/client/package.json](packages/client/package.json)
+- Server lint script: [packages/server/package.json](packages/server/package.json)
+
+## How to run linting locally
+
+Install deps (if not already installed):
+
+```bash
+npm install
+```
+
+Run the workspace lint scripts (this runs per-package lint commands):
+
+```bash
+npm run lint
+```
+
+Autofix common issues across the repo:
+
+```bash
+npx eslint . --ext .ts,.tsx --fix
+```
+
+If you want to lint only a package (faster iteration):
+
+```bash
+npm run lint --workspace=packages/client
+npm run lint --workspace=packages/server
+```
+
+## Notes & recommendations
+
+- You may see a warning from `@typescript-eslint` about TypeScript version
+ compatibility (this repo uses a newer TypeScript than the parser's tested
+ range). To silence that and ensure deterministic behaviour, either:
+ - Align `typescript` with the tested versions for the installed
+ `@typescript-eslint/*` packages, or
+ - Upgrade `@typescript-eslint/*` to a release that officially supports
+ your TypeScript version.
+
+- The config enforces strict rules (no `any`, explicit module boundaries). For
+ quick progress, there are scoped overrides for tests and configuration files.
+ Prefer fixing code to conform to the strict rules rather than widening
+ overrides where possible.
+
+- Some `import/*` rules were temporarily relaxed in server/client overrides
+ due to TypeScript resolver mismatches; if you add new path aliases or change
+ `tsconfig` layouts, update the `import/resolver` settings in
+ `.eslintrc.cjs` so stricter import rules can be re-enabled.
+
+## How to change rules
+
+1. Edit the root configuration at [.eslintrc.cjs](.eslintrc.cjs).
+2. Run `npx eslint . --ext .ts,.tsx --fix` to apply automatic fixes.
+3. Run `npm run lint` to ensure both packages pass the rules (server runs
+ `tsc --noEmit` after linting).
diff --git a/package-lock.json b/package-lock.json
index ffd7253..9c182e8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,14 @@
"packages/*"
],
"devDependencies": {
- "concurrently": "^8.2.2"
+ "@typescript-eslint/eslint-plugin": "^6.3.0",
+ "@typescript-eslint/parser": "^6.3.0",
+ "concurrently": "^8.2.2",
+ "eslint": "^8.43.0",
+ "eslint-import-resolver-typescript": "^3.8.0",
+ "eslint-plugin-import": "^2.29.1",
+ "eslint-plugin-react": "^7.34.0",
+ "eslint-plugin-react-hooks": "^7.0.1"
}
},
"node_modules/@adobe/css-tools": {
@@ -332,6 +339,40 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@emnapi/core": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz",
+ "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.1.0",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
+ "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz",
+ "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
@@ -774,6 +815,93 @@
"node": ">=18"
}
},
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "8.57.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
+ "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
"node_modules/@fastify/busboy": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz",
@@ -1462,6 +1590,68 @@
"node": ">=6"
}
},
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
+ "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==",
+ "deprecated": "Use @eslint/config-array instead",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^2.0.3",
+ "debug": "^4.3.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
+ "deprecated": "Use @eslint/object-schema instead",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
"node_modules/@istanbuljs/schema": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
@@ -1533,6 +1723,19 @@
"url": "https://opencollective.com/js-sdsl"
}
},
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "0.2.12",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
+ "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.4.3",
+ "@emnapi/runtime": "^1.4.3",
+ "@tybys/wasm-util": "^0.10.0"
+ }
+ },
"node_modules/@noble/hashes": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
@@ -1584,6 +1787,16 @@
"node": ">= 8"
}
},
+ "node_modules/@nolyfill/is-core-module": {
+ "version": "1.0.39",
+ "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz",
+ "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.4.0"
+ }
+ },
"node_modules/@opentelemetry/api": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
@@ -2034,6 +2247,13 @@
"win32"
]
},
+ "node_modules/@rtsao/scc": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
+ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@standard-schema/spec": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
@@ -2188,6 +2408,17 @@
"node": ">= 10"
}
},
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
+ "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@types/aria-query": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
@@ -2343,6 +2574,20 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/jsonwebtoken": {
"version": "9.0.10",
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz",
@@ -2454,6 +2699,13 @@
"form-data": "^2.5.5"
}
},
+ "node_modules/@types/semver": {
+ "version": "7.7.1",
+ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz",
+ "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/send": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",
@@ -2535,575 +2787,1864 @@
"license": "MIT",
"optional": true
},
- "node_modules/@vitejs/plugin-react": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
- "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz",
+ "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/core": "^7.28.0",
- "@babel/plugin-transform-react-jsx-self": "^7.27.1",
- "@babel/plugin-transform-react-jsx-source": "^7.27.1",
- "@rolldown/pluginutils": "1.0.0-beta.27",
- "@types/babel__core": "^7.20.5",
- "react-refresh": "^0.17.0"
+ "@eslint-community/regexpp": "^4.5.1",
+ "@typescript-eslint/scope-manager": "6.21.0",
+ "@typescript-eslint/type-utils": "6.21.0",
+ "@typescript-eslint/utils": "6.21.0",
+ "@typescript-eslint/visitor-keys": "6.21.0",
+ "debug": "^4.3.4",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.4",
+ "natural-compare": "^1.4.0",
+ "semver": "^7.5.4",
+ "ts-api-utils": "^1.0.1"
},
"engines": {
- "node": "^14.18.0 || >=16.0.0"
+ "node": "^16.0.0 || >=18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+ "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha",
+ "eslint": "^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
}
},
- "node_modules/@vitest/coverage-istanbul": {
- "version": "4.0.18",
- "resolved": "https://registry.npmjs.org/@vitest/coverage-istanbul/-/coverage-istanbul-4.0.18.tgz",
- "integrity": "sha512-0OhjP30owEDihYTZGWuq20rNtV1RjjJs1Mv4MaZIKcFBmiLUXX7HJLX4fU7wE+Mrc3lQxI2HKq6WrSXi5FGuCQ==",
+ "node_modules/@typescript-eslint/eslint-plugin/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": "MIT",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz",
+ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
"dependencies": {
- "@istanbuljs/schema": "^0.1.3",
- "@jridgewell/gen-mapping": "^0.3.13",
- "@jridgewell/trace-mapping": "0.3.31",
- "istanbul-lib-coverage": "^3.2.2",
- "istanbul-lib-instrument": "^6.0.3",
- "istanbul-lib-report": "^3.0.1",
- "istanbul-reports": "^3.2.0",
- "magicast": "^0.5.1",
- "obug": "^2.1.1",
- "tinyrainbow": "^3.0.3"
+ "@typescript-eslint/scope-manager": "6.21.0",
+ "@typescript-eslint/types": "6.21.0",
+ "@typescript-eslint/typescript-estree": "6.21.0",
+ "@typescript-eslint/visitor-keys": "6.21.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^16.0.0 || >=18.0.0"
},
"funding": {
- "url": "https://opencollective.com/vitest"
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "vitest": "4.0.18"
+ "eslint": "^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
}
},
- "node_modules/@vitest/expect": {
- "version": "4.0.18",
- "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz",
- "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==",
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz",
+ "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@standard-schema/spec": "^1.0.0",
- "@types/chai": "^5.2.2",
- "@vitest/spy": "4.0.18",
- "@vitest/utils": "4.0.18",
- "chai": "^6.2.1",
- "tinyrainbow": "^3.0.3"
+ "@typescript-eslint/types": "6.21.0",
+ "@typescript-eslint/visitor-keys": "6.21.0"
+ },
+ "engines": {
+ "node": "^16.0.0 || >=18.0.0"
},
"funding": {
- "url": "https://opencollective.com/vitest"
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
}
},
- "node_modules/@vitest/pretty-format": {
- "version": "4.0.18",
- "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz",
- "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==",
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz",
+ "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==",
"dev": true,
"license": "MIT",
"dependencies": {
- "tinyrainbow": "^3.0.3"
+ "@typescript-eslint/typescript-estree": "6.21.0",
+ "@typescript-eslint/utils": "6.21.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^1.0.1"
+ },
+ "engines": {
+ "node": "^16.0.0 || >=18.0.0"
},
"funding": {
- "url": "https://opencollective.com/vitest"
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
}
},
- "node_modules/@vitest/runner": {
- "version": "4.0.18",
- "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz",
- "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==",
+ "node_modules/@typescript-eslint/types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz",
+ "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "@vitest/utils": "4.0.18",
- "pathe": "^2.0.3"
+ "engines": {
+ "node": "^16.0.0 || >=18.0.0"
},
"funding": {
- "url": "https://opencollective.com/vitest"
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
}
},
- "node_modules/@vitest/snapshot": {
- "version": "4.0.18",
- "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz",
- "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==",
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz",
+ "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==",
"dev": true,
- "license": "MIT",
+ "license": "BSD-2-Clause",
"dependencies": {
- "@vitest/pretty-format": "4.0.18",
- "magic-string": "^0.30.21",
- "pathe": "^2.0.3"
+ "@typescript-eslint/types": "6.21.0",
+ "@typescript-eslint/visitor-keys": "6.21.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "minimatch": "9.0.3",
+ "semver": "^7.5.4",
+ "ts-api-utils": "^1.0.1"
+ },
+ "engines": {
+ "node": "^16.0.0 || >=18.0.0"
},
"funding": {
- "url": "https://opencollective.com/vitest"
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
}
},
- "node_modules/@vitest/spy": {
- "version": "4.0.18",
- "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz",
- "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==",
+ "node_modules/@typescript-eslint/typescript-estree/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/@typescript-eslint/utils": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz",
+ "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@types/json-schema": "^7.0.12",
+ "@types/semver": "^7.5.0",
+ "@typescript-eslint/scope-manager": "6.21.0",
+ "@typescript-eslint/types": "6.21.0",
+ "@typescript-eslint/typescript-estree": "6.21.0",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": "^16.0.0 || >=18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/utils/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/@typescript-eslint/visitor-keys": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz",
+ "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "6.21.0",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^16.0.0 || >=18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
+ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/@unrs/resolver-binding-android-arm-eabi": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz",
+ "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-android-arm64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz",
+ "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-darwin-arm64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz",
+ "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-darwin-x64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz",
+ "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-freebsd-x64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz",
+ "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz",
+ "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz",
+ "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz",
+ "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-musl": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz",
+ "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz",
+ "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz",
+ "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-musl": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz",
+ "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-s390x-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz",
+ "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz",
+ "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-musl": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz",
+ "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-wasm32-wasi": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz",
+ "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@napi-rs/wasm-runtime": "^0.2.11"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz",
+ "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-ia32-msvc": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz",
+ "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-x64-msvc": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz",
+ "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
+ "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.28.0",
+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
+ "@rolldown/pluginutils": "1.0.0-beta.27",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.17.0"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+ }
+ },
+ "node_modules/@vitest/coverage-istanbul": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/coverage-istanbul/-/coverage-istanbul-4.0.18.tgz",
+ "integrity": "sha512-0OhjP30owEDihYTZGWuq20rNtV1RjjJs1Mv4MaZIKcFBmiLUXX7HJLX4fU7wE+Mrc3lQxI2HKq6WrSXi5FGuCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@istanbuljs/schema": "^0.1.3",
+ "@jridgewell/gen-mapping": "^0.3.13",
+ "@jridgewell/trace-mapping": "0.3.31",
+ "istanbul-lib-coverage": "^3.2.2",
+ "istanbul-lib-instrument": "^6.0.3",
+ "istanbul-lib-report": "^3.0.1",
+ "istanbul-reports": "^3.2.0",
+ "magicast": "^0.5.1",
+ "obug": "^2.1.1",
+ "tinyrainbow": "^3.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "vitest": "4.0.18"
+ }
+ },
+ "node_modules/@vitest/expect": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz",
+ "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@standard-schema/spec": "^1.0.0",
+ "@types/chai": "^5.2.2",
+ "@vitest/spy": "4.0.18",
+ "@vitest/utils": "4.0.18",
+ "chai": "^6.2.1",
+ "tinyrainbow": "^3.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/pretty-format": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz",
+ "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyrainbow": "^3.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/runner": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz",
+ "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/utils": "4.0.18",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/snapshot": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz",
+ "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.0.18",
+ "magic-string": "^0.30.21",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/spy": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz",
+ "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/utils": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz",
+ "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.0.18",
+ "tinyrainbow": "^3.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/abab": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
+ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
+ "deprecated": "Use your platform's native atob() and btoa() methods instead",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "event-target-shim": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6.5"
+ }
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "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",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.14.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
+ "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
+ "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"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "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,
+ "license": "MIT"
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/aria-query": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
+ "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "dequal": "^2.0.3"
+ }
+ },
+ "node_modules/array-buffer-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
+ "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "is-array-buffer": "^3.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+ "license": "MIT"
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.9",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
+ "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.24.0",
+ "es-object-atoms": "^1.1.1",
+ "get-intrinsic": "^1.3.0",
+ "is-string": "^1.1.1",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/array.prototype.findlast": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
+ "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlastindex": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz",
+ "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-shim-unscopables": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
+ "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flatmap": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz",
+ "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.tosorted": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
+ "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3",
+ "es-errors": "^1.3.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/arraybuffer.prototype.slice": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
+ "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "is-array-buffer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/arrify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
+ "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/assertion-error": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/async-function": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
+ "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/async-retry": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz",
+ "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "retry": "0.13.1"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "license": "MIT"
+ },
+ "node_modules/autoprefixer": {
+ "version": "10.4.27",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz",
+ "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.28.1",
+ "caniuse-lite": "^1.0.30001774",
+ "fraction.js": "^5.3.4",
+ "picocolors": "^1.1.1",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/axios": {
+ "version": "1.13.6",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz",
+ "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.15.11",
+ "form-data": "^4.0.5",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "node_modules/axios/node_modules/form-data": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "optional": true
+ },
+ "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/basic-auth": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
+ "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/basic-auth/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "license": "MIT"
+ },
+ "node_modules/bignumber.js": {
+ "version": "9.3.1",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
+ "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "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"
+ },
"funding": {
- "url": "https://opencollective.com/vitest"
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@vitest/utils": {
- "version": "4.0.18",
- "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz",
- "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==",
+ "node_modules/body-parser": {
+ "version": "1.20.4",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
+ "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "content-type": "~1.0.5",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "~1.2.0",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "on-finished": "~2.4.1",
+ "qs": "~6.14.0",
+ "raw-body": "~2.5.3",
+ "type-is": "~1.6.18",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/body-parser/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/body-parser/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "license": "MIT"
+ },
+ "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": {
- "@vitest/pretty-format": "4.0.18",
- "tinyrainbow": "^3.0.3"
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/braces": {
+ "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.1.1"
},
- "funding": {
- "url": "https://opencollective.com/vitest"
+ "engines": {
+ "node": ">=8"
}
},
- "node_modules/abab": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
- "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
- "deprecated": "Use your platform's native atob() and btoa() methods instead",
+ "node_modules/browserslist": {
+ "version": "4.28.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
"dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
"license": "BSD-3-Clause"
},
- "node_modules/abort-controller": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
- "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "dev": true,
"license": "MIT",
- "optional": true,
"dependencies": {
- "event-target-shim": "^5.0.0"
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
},
"engines": {
- "node": ">=6.5"
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/accepts": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
- "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
- "mime-types": "~2.1.34",
- "negotiator": "0.6.3"
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
},
"engines": {
- "node": ">= 0.6"
+ "node": ">= 0.4"
}
},
- "node_modules/agent-base": {
- "version": "7.1.4",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
- "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "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.30001775",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001775.tgz",
+ "integrity": "sha512-s3Qv7Lht9zbVKE9XoTyRG6wVDCKdtOFIjBGg3+Yhn6JaytuNKPIjBMTMIY1AnOH3seL5mvF+x33oGAyK3hVt3A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chai": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
+ "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
"license": "MIT",
- "optional": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
"engines": {
- "node": ">= 14"
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
}
},
- "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/chalk/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/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/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": {
- "color-convert": "^2.0.1"
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
},
"engines": {
- "node": ">=8"
+ "node": ">= 8.10.0"
},
"funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
}
},
- "node_modules/any-promise": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
- "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
- "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==",
+ "node_modules/chokidar/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"license": "ISC",
"dependencies": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
+ "is-glob": "^4.0.1"
},
"engines": {
- "node": ">= 8"
+ "node": ">= 6"
}
},
- "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,
- "license": "MIT"
- },
- "node_modules/aria-query": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
- "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
- "dev": true,
- "license": "Apache-2.0",
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "license": "ISC",
"dependencies": {
- "dequal": "^2.0.3"
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
}
},
- "node_modules/array-buffer-byte-length": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
- "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
- "dev": true,
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"license": "MIT",
"dependencies": {
- "call-bound": "^1.0.3",
- "is-array-buffer": "^3.0.5"
+ "color-name": "~1.1.4"
},
"engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
+ "node": ">=7.0.0"
}
},
- "node_modules/array-flatten": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
- "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT"
},
- "node_modules/arrify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
- "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
- "optional": true,
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
"engines": {
- "node": ">=8"
+ "node": ">= 0.8"
}
},
- "node_modules/asap": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
- "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/assertion-error": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
- "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "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": ">=12"
+ "node": ">= 6"
}
},
- "node_modules/async-retry": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz",
- "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==",
+ "node_modules/component-emitter": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz",
+ "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==",
+ "dev": true,
"license": "MIT",
- "optional": true,
- "dependencies": {
- "retry": "0.13.1"
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/asynckit": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "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/autoprefixer": {
- "version": "10.4.27",
- "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz",
- "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==",
+ "node_modules/concurrently": {
+ "version": "8.2.2",
+ "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz",
+ "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==",
"dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/autoprefixer"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
"license": "MIT",
"dependencies": {
- "browserslist": "^4.28.1",
- "caniuse-lite": "^1.0.30001774",
- "fraction.js": "^5.3.4",
- "picocolors": "^1.1.1",
- "postcss-value-parser": "^4.2.0"
+ "chalk": "^4.1.2",
+ "date-fns": "^2.30.0",
+ "lodash": "^4.17.21",
+ "rxjs": "^7.8.1",
+ "shell-quote": "^1.8.1",
+ "spawn-command": "0.0.2",
+ "supports-color": "^8.1.1",
+ "tree-kill": "^1.2.2",
+ "yargs": "^17.7.2"
},
"bin": {
- "autoprefixer": "bin/autoprefixer"
+ "conc": "dist/bin/concurrently.js",
+ "concurrently": "dist/bin/concurrently.js"
},
"engines": {
- "node": "^10 || ^12 || >=14"
+ "node": "^14.13.0 || >=16.0.0"
},
- "peerDependencies": {
- "postcss": "^8.1.0"
+ "funding": {
+ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
}
},
- "node_modules/available-typed-arrays": {
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/convert-source-map": {
+ "version": "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/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
"version": "1.0.7",
- "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
- "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
+ "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
+ "license": "MIT"
+ },
+ "node_modules/cookiejar": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz",
+ "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==",
"dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cors": {
+ "version": "2.8.6",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
+ "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
"license": "MIT",
"dependencies": {
- "possible-typed-array-names": "^1.0.0"
+ "object-assign": "^4",
+ "vary": "^1"
},
"engines": {
- "node": ">= 0.4"
+ "node": ">= 0.10"
},
"funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/axios": {
- "version": "1.13.6",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz",
- "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==",
- "license": "MIT",
- "dependencies": {
- "follow-redirects": "^1.15.11",
- "form-data": "^4.0.5",
- "proxy-from-env": "^1.1.0"
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
}
},
- "node_modules/axios/node_modules/form-data": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
- "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "node_modules/cross-spawn": {
+ "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": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.8",
- "es-set-tostringtag": "^2.1.0",
- "hasown": "^2.0.2",
- "mime-types": "^2.1.12"
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
},
"engines": {
- "node": ">= 6"
+ "node": ">= 8"
}
},
- "node_modules/base64-js": {
+ "node_modules/css.escape": {
"version": "1.5.1",
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
- "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT",
- "optional": true
+ "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
+ "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
+ "dev": true,
+ "license": "MIT"
},
- "node_modules/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==",
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
"dev": true,
- "license": "Apache-2.0",
+ "license": "MIT",
"bin": {
- "baseline-browser-mapping": "dist/cli.cjs"
+ "cssesc": "bin/cssesc"
},
"engines": {
- "node": ">=6.0.0"
+ "node": ">=4"
}
},
- "node_modules/basic-auth": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
- "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
+ "node_modules/cssstyle": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz",
+ "integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
- "safe-buffer": "5.1.2"
+ "rrweb-cssom": "^0.6.0"
},
"engines": {
- "node": ">= 0.8"
+ "node": ">=14"
}
},
- "node_modules/basic-auth/node_modules/safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "node_modules/csstype": {
+ "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/bignumber.js": {
- "version": "9.3.1",
- "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
- "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
+ "node_modules/data-urls": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz",
+ "integrity": "sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==",
+ "dev": true,
"license": "MIT",
- "optional": true,
+ "dependencies": {
+ "abab": "^2.0.6",
+ "whatwg-mimetype": "^3.0.0",
+ "whatwg-url": "^12.0.0"
+ },
"engines": {
- "node": "*"
+ "node": ">=14"
}
},
- "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==",
+ "node_modules/data-urls/node_modules/tr46": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz",
+ "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==",
"dev": true,
"license": "MIT",
- "engines": {
- "node": ">=8"
+ "dependencies": {
+ "punycode": "^2.3.0"
},
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "engines": {
+ "node": ">=14"
}
},
- "node_modules/body-parser": {
- "version": "1.20.4",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
- "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
+ "node_modules/data-urls/node_modules/webidl-conversions": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+ "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/data-urls/node_modules/whatwg-url": {
+ "version": "12.0.1",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz",
+ "integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
- "bytes": "~3.1.2",
- "content-type": "~1.0.5",
- "debug": "2.6.9",
- "depd": "2.0.0",
- "destroy": "~1.2.0",
- "http-errors": "~2.0.1",
- "iconv-lite": "~0.4.24",
- "on-finished": "~2.4.1",
- "qs": "~6.14.0",
- "raw-body": "~2.5.3",
- "type-is": "~1.6.18",
- "unpipe": "~1.0.0"
+ "tr46": "^4.1.1",
+ "webidl-conversions": "^7.0.0"
},
"engines": {
- "node": ">= 0.8",
- "npm": "1.2.8000 || >= 1.4.16"
+ "node": ">=14"
}
},
- "node_modules/body-parser/node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "node_modules/data-view-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
+ "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
- "ms": "2.0.0"
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/body-parser/node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
- "license": "MIT"
- },
- "node_modules/braces": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
- "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "node_modules/data-view-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
+ "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "fill-range": "^7.1.1"
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
},
"engines": {
- "node": ">=8"
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/inspect-js"
}
},
- "node_modules/browserslist": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
- "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+ "node_modules/data-view-byte-offset": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
+ "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
"dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/browserslist"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
"license": "MIT",
"dependencies": {
- "baseline-browser-mapping": "^2.9.0",
- "caniuse-lite": "^1.0.30001759",
- "electron-to-chromium": "^1.5.263",
- "node-releases": "^2.0.27",
- "update-browserslist-db": "^1.2.0"
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
},
- "bin": {
- "browserslist": "cli.js"
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/date-fns": {
+ "version": "2.30.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
+ "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.21.0"
},
"engines": {
- "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ "node": ">=0.11"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/date-fns"
}
},
- "node_modules/buffer-equal-constant-time": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
- "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
- "license": "BSD-3-Clause"
- },
- "node_modules/bytes": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
- "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
"engines": {
- "node": ">= 0.8"
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
}
},
- "node_modules/call-bind": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
- "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "node_modules/decimal.js": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
+ "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/deep-equal": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz",
+ "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "call-bind-apply-helpers": "^1.0.0",
- "es-define-property": "^1.0.0",
- "get-intrinsic": "^1.2.4",
- "set-function-length": "^1.2.2"
+ "array-buffer-byte-length": "^1.0.0",
+ "call-bind": "^1.0.5",
+ "es-get-iterator": "^1.1.3",
+ "get-intrinsic": "^1.2.2",
+ "is-arguments": "^1.1.1",
+ "is-array-buffer": "^3.0.2",
+ "is-date-object": "^1.0.5",
+ "is-regex": "^1.1.4",
+ "is-shared-array-buffer": "^1.0.2",
+ "isarray": "^2.0.5",
+ "object-is": "^1.1.5",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.4",
+ "regexp.prototype.flags": "^1.5.1",
+ "side-channel": "^1.0.4",
+ "which-boxed-primitive": "^1.0.2",
+ "which-collection": "^1.0.1",
+ "which-typed-array": "^1.1.13"
},
"engines": {
"node": ">= 0.4"
@@ -3112,27 +4653,41 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/call-bind-apply-helpers": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
- "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
"license": "MIT",
"dependencies": {
+ "es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
- "function-bind": "^1.1.2"
+ "gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/call-bound": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
- "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
- "call-bind-apply-helpers": "^1.0.2",
- "get-intrinsic": "^1.3.0"
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
},
"engines": {
"node": ">= 0.4"
@@ -3141,799 +4696,941 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "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,
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
- "node": ">= 6"
+ "node": ">=0.4.0"
}
},
- "node_modules/caniuse-lite": {
- "version": "1.0.30001775",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001775.tgz",
- "integrity": "sha512-s3Qv7Lht9zbVKE9XoTyRG6wVDCKdtOFIjBGg3+Yhn6JaytuNKPIjBMTMIY1AnOH3seL5mvF+x33oGAyK3hVt3A==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "CC-BY-4.0"
- },
- "node_modules/chai": {
- "version": "6.2.2",
- "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
- "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
- "dev": true,
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"license": "MIT",
"engines": {
- "node": ">=18"
+ "node": ">= 0.8"
}
},
- "node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
"engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
+ "node": ">=6"
}
},
- "node_modules/chalk/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,
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
"license": "MIT",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
"engines": {
- "node": ">=8"
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
}
},
- "node_modules/chokidar": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
- "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "node_modules/dezalgo": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
+ "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
"dev": true,
- "license": "MIT",
+ "license": "ISC",
"dependencies": {
- "anymatch": "~3.1.2",
- "braces": "~3.0.2",
- "glob-parent": "~5.1.2",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.6.0"
- },
- "engines": {
- "node": ">= 8.10.0"
- },
- "funding": {
- "url": "https://paulmillr.com/funding/"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.2"
+ "asap": "^2.0.0",
+ "wrappy": "1"
}
},
- "node_modules/chokidar/node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "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,
- "license": "ISC",
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
+ "license": "Apache-2.0"
},
- "node_modules/cliui": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
- "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
- "license": "ISC",
+ "node_modules/dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.1",
- "wrap-ansi": "^7.0.0"
+ "path-type": "^4.0.0"
},
"engines": {
- "node": ">=12"
+ "node": ">=8"
}
},
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "license": "MIT",
+ "node_modules/dlv": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "license": "Apache-2.0",
"dependencies": {
- "color-name": "~1.1.4"
+ "esutils": "^2.0.2"
},
"engines": {
- "node": ">=7.0.0"
+ "node": ">=6.0.0"
}
},
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "node_modules/dom-accessibility-api": {
+ "version": "0.5.16",
+ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
+ "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
+ "dev": true,
"license": "MIT"
},
- "node_modules/combined-stream": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
- "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "node_modules/domexception": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
+ "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==",
+ "deprecated": "Use your platform's native DOMException instead",
+ "dev": true,
"license": "MIT",
"dependencies": {
- "delayed-stream": "~1.0.0"
+ "webidl-conversions": "^7.0.0"
},
"engines": {
- "node": ">= 0.8"
+ "node": ">=12"
}
},
- "node_modules/commander": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
- "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+ "node_modules/domexception/node_modules/webidl-conversions": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+ "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
"dev": true,
- "license": "MIT",
+ "license": "BSD-2-Clause",
"engines": {
- "node": ">= 6"
+ "node": ">=12"
}
},
- "node_modules/component-emitter": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz",
- "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==",
- "dev": true,
- "license": "MIT",
+ "node_modules/dotenv": {
+ "version": "16.6.1",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
+ "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://dotenvx.com"
}
},
- "node_modules/concurrently": {
- "version": "8.2.2",
- "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz",
- "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==",
- "dev": true,
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
- "chalk": "^4.1.2",
- "date-fns": "^2.30.0",
- "lodash": "^4.17.21",
- "rxjs": "^7.8.1",
- "shell-quote": "^1.8.1",
- "spawn-command": "0.0.2",
- "supports-color": "^8.1.1",
- "tree-kill": "^1.2.2",
- "yargs": "^17.7.2"
- },
- "bin": {
- "conc": "dist/bin/concurrently.js",
- "concurrently": "dist/bin/concurrently.js"
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
},
"engines": {
- "node": "^14.13.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
+ "node": ">= 0.4"
}
},
- "node_modules/content-disposition": {
- "version": "0.5.4",
- "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
- "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "node_modules/duplexify": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz",
+ "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==",
"license": "MIT",
+ "optional": true,
"dependencies": {
- "safe-buffer": "5.2.1"
- },
- "engines": {
- "node": ">= 0.6"
+ "end-of-stream": "^1.4.1",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1",
+ "stream-shift": "^1.0.2"
}
},
- "node_modules/content-type": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
- "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
}
},
- "node_modules/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==",
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "license": "MIT"
+ },
+ "node_modules/electron-to-chromium": {
+ "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": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT"
},
- "node_modules/cookie": {
- "version": "0.7.2",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
- "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
"license": "MIT",
"engines": {
- "node": ">= 0.6"
+ "node": ">= 0.8"
}
},
- "node_modules/cookie-signature": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
- "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
- "license": "MIT"
+ "node_modules/end-of-stream": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "once": "^1.4.0"
+ }
},
- "node_modules/cookiejar": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz",
- "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==",
+ "node_modules/entities": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
"dev": true,
- "license": "MIT"
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
},
- "node_modules/cors": {
- "version": "2.8.6",
- "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
- "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
+ "node_modules/es-abstract": {
+ "version": "1.24.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz",
+ "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
- "object-assign": "^4",
- "vary": "^1"
+ "array-buffer-byte-length": "^1.0.2",
+ "arraybuffer.prototype.slice": "^1.0.4",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "data-view-buffer": "^1.0.2",
+ "data-view-byte-length": "^1.0.2",
+ "data-view-byte-offset": "^1.0.1",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-set-tostringtag": "^2.1.0",
+ "es-to-primitive": "^1.3.0",
+ "function.prototype.name": "^1.1.8",
+ "get-intrinsic": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "get-symbol-description": "^1.1.0",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "internal-slot": "^1.1.0",
+ "is-array-buffer": "^3.0.5",
+ "is-callable": "^1.2.7",
+ "is-data-view": "^1.0.2",
+ "is-negative-zero": "^2.0.3",
+ "is-regex": "^1.2.1",
+ "is-set": "^2.0.3",
+ "is-shared-array-buffer": "^1.0.4",
+ "is-string": "^1.1.1",
+ "is-typed-array": "^1.1.15",
+ "is-weakref": "^1.1.1",
+ "math-intrinsics": "^1.1.0",
+ "object-inspect": "^1.13.4",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.7",
+ "own-keys": "^1.0.1",
+ "regexp.prototype.flags": "^1.5.4",
+ "safe-array-concat": "^1.1.3",
+ "safe-push-apply": "^1.0.0",
+ "safe-regex-test": "^1.1.0",
+ "set-proto": "^1.0.0",
+ "stop-iteration-iterator": "^1.1.0",
+ "string.prototype.trim": "^1.2.10",
+ "string.prototype.trimend": "^1.0.9",
+ "string.prototype.trimstart": "^1.0.8",
+ "typed-array-buffer": "^1.0.3",
+ "typed-array-byte-length": "^1.0.3",
+ "typed-array-byte-offset": "^1.0.4",
+ "typed-array-length": "^1.0.7",
+ "unbox-primitive": "^1.1.0",
+ "which-typed-array": "^1.1.19"
},
"engines": {
- "node": ">= 0.10"
+ "node": ">= 0.4"
},
"funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/css.escape": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
- "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
- "dev": true,
- "license": "MIT"
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
},
- "node_modules/cssesc": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
- "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-get-iterator": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz",
+ "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==",
"dev": true,
"license": "MIT",
- "bin": {
- "cssesc": "bin/cssesc"
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.3",
+ "has-symbols": "^1.0.3",
+ "is-arguments": "^1.1.1",
+ "is-map": "^2.0.2",
+ "is-set": "^2.0.2",
+ "is-string": "^1.0.7",
+ "isarray": "^2.0.5",
+ "stop-iteration-iterator": "^1.0.0"
},
- "engines": {
- "node": ">=4"
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/cssstyle": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz",
- "integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==",
+ "node_modules/es-iterator-helpers": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz",
+ "integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==",
"dev": true,
"license": "MIT",
"dependencies": {
- "rrweb-cssom": "^0.6.0"
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.24.1",
+ "es-errors": "^1.3.0",
+ "es-set-tostringtag": "^2.1.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.3.0",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "iterator.prototype": "^1.1.5",
+ "safe-array-concat": "^1.1.3"
},
"engines": {
- "node": ">=14"
+ "node": ">= 0.4"
}
},
- "node_modules/csstype": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
- "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "node_modules/es-module-lexer": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
+ "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
"dev": true,
"license": "MIT"
},
- "node_modules/data-urls": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz",
- "integrity": "sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==",
- "dev": true,
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
- "abab": "^2.0.6",
- "whatwg-mimetype": "^3.0.0",
- "whatwg-url": "^12.0.0"
+ "es-errors": "^1.3.0"
},
"engines": {
- "node": ">=14"
+ "node": ">= 0.4"
}
},
- "node_modules/data-urls/node_modules/tr46": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz",
- "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==",
- "dev": true,
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
- "punycode": "^2.3.0"
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
},
"engines": {
- "node": ">=14"
- }
- },
- "node_modules/data-urls/node_modules/webidl-conversions": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
- "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
- "dev": true,
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=12"
+ "node": ">= 0.4"
}
},
- "node_modules/data-urls/node_modules/whatwg-url": {
- "version": "12.0.1",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz",
- "integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==",
+ "node_modules/es-shim-unscopables": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
+ "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "tr46": "^4.1.1",
- "webidl-conversions": "^7.0.0"
+ "hasown": "^2.0.2"
},
"engines": {
- "node": ">=14"
+ "node": ">= 0.4"
}
},
- "node_modules/date-fns": {
- "version": "2.30.0",
- "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
- "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
+ "node_modules/es-to-primitive": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
+ "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/runtime": "^7.21.0"
+ "is-callable": "^1.2.7",
+ "is-date-object": "^1.0.5",
+ "is-symbol": "^1.0.4"
},
"engines": {
- "node": ">=0.11"
+ "node": ">= 0.4"
},
"funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/date-fns"
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/debug": {
- "version": "4.4.3",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
- "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "node_modules/esbuild": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
+ "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
+ "dev": true,
+ "hasInstallScript": true,
"license": "MIT",
- "dependencies": {
- "ms": "^2.1.3"
+ "bin": {
+ "esbuild": "bin/esbuild"
},
"engines": {
- "node": ">=6.0"
+ "node": ">=18"
},
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.27.3",
+ "@esbuild/android-arm": "0.27.3",
+ "@esbuild/android-arm64": "0.27.3",
+ "@esbuild/android-x64": "0.27.3",
+ "@esbuild/darwin-arm64": "0.27.3",
+ "@esbuild/darwin-x64": "0.27.3",
+ "@esbuild/freebsd-arm64": "0.27.3",
+ "@esbuild/freebsd-x64": "0.27.3",
+ "@esbuild/linux-arm": "0.27.3",
+ "@esbuild/linux-arm64": "0.27.3",
+ "@esbuild/linux-ia32": "0.27.3",
+ "@esbuild/linux-loong64": "0.27.3",
+ "@esbuild/linux-mips64el": "0.27.3",
+ "@esbuild/linux-ppc64": "0.27.3",
+ "@esbuild/linux-riscv64": "0.27.3",
+ "@esbuild/linux-s390x": "0.27.3",
+ "@esbuild/linux-x64": "0.27.3",
+ "@esbuild/netbsd-arm64": "0.27.3",
+ "@esbuild/netbsd-x64": "0.27.3",
+ "@esbuild/openbsd-arm64": "0.27.3",
+ "@esbuild/openbsd-x64": "0.27.3",
+ "@esbuild/openharmony-arm64": "0.27.3",
+ "@esbuild/sunos-x64": "0.27.3",
+ "@esbuild/win32-arm64": "0.27.3",
+ "@esbuild/win32-ia32": "0.27.3",
+ "@esbuild/win32-x64": "0.27.3"
}
},
- "node_modules/decimal.js": {
- "version": "10.6.0",
- "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
- "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
- "dev": true,
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"license": "MIT"
},
- "node_modules/deep-equal": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz",
- "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==",
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "array-buffer-byte-length": "^1.0.0",
- "call-bind": "^1.0.5",
- "es-get-iterator": "^1.1.3",
- "get-intrinsic": "^1.2.2",
- "is-arguments": "^1.1.1",
- "is-array-buffer": "^3.0.2",
- "is-date-object": "^1.0.5",
- "is-regex": "^1.1.4",
- "is-shared-array-buffer": "^1.0.2",
- "isarray": "^2.0.5",
- "object-is": "^1.1.5",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.4",
- "regexp.prototype.flags": "^1.5.1",
- "side-channel": "^1.0.4",
- "which-boxed-primitive": "^1.0.2",
- "which-collection": "^1.0.1",
- "which-typed-array": "^1.1.13"
- },
"engines": {
- "node": ">= 0.4"
+ "node": ">=10"
},
"funding": {
- "url": "https://github.com/sponsors/ljharb"
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/define-data-property": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
- "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-define-property": "^1.0.0",
- "es-errors": "^1.3.0",
- "gopd": "^1.0.1"
+ "node_modules/eslint": {
+ "version": "8.57.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
+ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
+ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.57.1",
+ "@humanwhocodes/config-array": "^0.13.0",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "@ungap/structured-clone": "^1.2.0",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
},
"engines": {
- "node": ">= 0.4"
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
- "url": "https://github.com/sponsors/ljharb"
+ "url": "https://opencollective.com/eslint"
}
},
- "node_modules/define-properties": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
- "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "node_modules/eslint-import-resolver-node": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
+ "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "define-data-property": "^1.0.1",
- "has-property-descriptors": "^1.0.0",
- "object-keys": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
+ "debug": "^3.2.7",
+ "is-core-module": "^2.13.0",
+ "resolve": "^1.22.4"
}
},
- "node_modules/delayed-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "node_modules/eslint-import-resolver-node/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
"license": "MIT",
- "engines": {
- "node": ">=0.4.0"
+ "dependencies": {
+ "ms": "^2.1.1"
}
},
- "node_modules/depd": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
- "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
- "license": "MIT",
+ "node_modules/eslint-import-resolver-typescript": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz",
+ "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@nolyfill/is-core-module": "1.0.39",
+ "debug": "^4.4.0",
+ "get-tsconfig": "^4.10.0",
+ "is-bun-module": "^2.0.0",
+ "stable-hash": "^0.0.5",
+ "tinyglobby": "^0.2.13",
+ "unrs-resolver": "^1.6.2"
+ },
"engines": {
- "node": ">= 0.8"
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-import-resolver-typescript"
+ },
+ "peerDependencies": {
+ "eslint": "*",
+ "eslint-plugin-import": "*",
+ "eslint-plugin-import-x": "*"
+ },
+ "peerDependenciesMeta": {
+ "eslint-plugin-import": {
+ "optional": true
+ },
+ "eslint-plugin-import-x": {
+ "optional": true
+ }
}
},
- "node_modules/dequal": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
- "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "node_modules/eslint-module-utils": {
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz",
+ "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7"
+ },
"engines": {
- "node": ">=6"
+ "node": ">=4"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
}
},
- "node_modules/destroy": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
- "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "node_modules/eslint-module-utils/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
"license": "MIT",
- "engines": {
- "node": ">= 0.8",
- "npm": "1.2.8000 || >= 1.4.16"
+ "dependencies": {
+ "ms": "^2.1.1"
}
},
- "node_modules/dezalgo": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
- "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
+ "node_modules/eslint-plugin-import": {
+ "version": "2.32.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
+ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
"dev": true,
- "license": "ISC",
+ "license": "MIT",
"dependencies": {
- "asap": "^2.0.0",
- "wrappy": "1"
+ "@rtsao/scc": "^1.1.0",
+ "array-includes": "^3.1.9",
+ "array.prototype.findlastindex": "^1.2.6",
+ "array.prototype.flat": "^1.3.3",
+ "array.prototype.flatmap": "^1.3.3",
+ "debug": "^3.2.7",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.9",
+ "eslint-module-utils": "^2.12.1",
+ "hasown": "^2.0.2",
+ "is-core-module": "^2.16.1",
+ "is-glob": "^4.0.3",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.8",
+ "object.groupby": "^1.0.3",
+ "object.values": "^1.2.1",
+ "semver": "^6.3.1",
+ "string.prototype.trimend": "^1.0.9",
+ "tsconfig-paths": "^3.15.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
}
},
- "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,
- "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==",
+ "node_modules/eslint-plugin-import/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
- "license": "MIT"
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
},
- "node_modules/dom-accessibility-api": {
- "version": "0.5.16",
- "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
- "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
+ "node_modules/eslint-plugin-import/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dev": true,
- "license": "MIT"
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
},
- "node_modules/domexception": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
- "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==",
- "deprecated": "Use your platform's native DOMException instead",
+ "node_modules/eslint-plugin-import/node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
"dev": true,
- "license": "MIT",
+ "license": "Apache-2.0",
"dependencies": {
- "webidl-conversions": "^7.0.0"
+ "esutils": "^2.0.2"
},
"engines": {
- "node": ">=12"
+ "node": ">=0.10.0"
}
},
- "node_modules/domexception/node_modules/webidl-conversions": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
- "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+ "node_modules/eslint-plugin-import/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"dev": true,
- "license": "BSD-2-Clause",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
"engines": {
- "node": ">=12"
+ "node": "*"
}
},
- "node_modules/dotenv": {
- "version": "16.6.1",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
- "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
- "license": "BSD-2-Clause",
+ "node_modules/eslint-plugin-react": {
+ "version": "7.37.5",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
+ "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.8",
+ "array.prototype.findlast": "^1.2.5",
+ "array.prototype.flatmap": "^1.3.3",
+ "array.prototype.tosorted": "^1.1.4",
+ "doctrine": "^2.1.0",
+ "es-iterator-helpers": "^1.2.1",
+ "estraverse": "^5.3.0",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+ "minimatch": "^3.1.2",
+ "object.entries": "^1.1.9",
+ "object.fromentries": "^2.0.8",
+ "object.values": "^1.2.1",
+ "prop-types": "^15.8.1",
+ "resolve": "^2.0.0-next.5",
+ "semver": "^6.3.1",
+ "string.prototype.matchall": "^4.0.12",
+ "string.prototype.repeat": "^1.0.0"
+ },
"engines": {
- "node": ">=12"
+ "node": ">=4"
},
- "funding": {
- "url": "https://dotenvx.com"
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
}
},
- "node_modules/dunder-proto": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
- "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz",
+ "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
- "call-bind-apply-helpers": "^1.0.1",
- "es-errors": "^1.3.0",
- "gopd": "^1.2.0"
+ "@babel/core": "^7.24.4",
+ "@babel/parser": "^7.24.4",
+ "hermes-parser": "^0.25.1",
+ "zod": "^3.25.0 || ^4.0.0",
+ "zod-validation-error": "^3.5.0 || ^4.0.0"
},
"engines": {
- "node": ">= 0.4"
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
}
},
- "node_modules/duplexify": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz",
- "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==",
+ "node_modules/eslint-plugin-react/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
"license": "MIT",
- "optional": true,
"dependencies": {
- "end-of-stream": "^1.4.1",
- "inherits": "^2.0.3",
- "readable-stream": "^3.1.1",
- "stream-shift": "^1.0.2"
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
}
},
- "node_modules/ecdsa-sig-formatter": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
- "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "node_modules/eslint-plugin-react/node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
"license": "Apache-2.0",
"dependencies": {
- "safe-buffer": "^5.0.1"
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "node_modules/ee-first": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
- "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
- "license": "MIT"
- },
- "node_modules/electron-to-chromium": {
- "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==",
+ "node_modules/eslint-plugin-react/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"dev": true,
- "license": "ISC"
- },
- "node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "license": "MIT"
- },
- "node_modules/encodeurl": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
- "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
- "license": "MIT",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
"engines": {
- "node": ">= 0.8"
+ "node": "*"
}
},
- "node_modules/end-of-stream": {
- "version": "1.4.5",
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
- "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+ "node_modules/eslint-plugin-react/node_modules/resolve": {
+ "version": "2.0.0-next.6",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz",
+ "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==",
+ "dev": true,
"license": "MIT",
- "optional": true,
"dependencies": {
- "once": "^1.4.0"
+ "es-errors": "^1.3.0",
+ "is-core-module": "^2.16.1",
+ "node-exports-info": "^1.6.0",
+ "object-keys": "^1.1.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/entities": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
- "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
"dev": true,
"license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
"engines": {
- "node": ">=0.12"
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
- "url": "https://github.com/fb55/entities?sponsor=1"
+ "url": "https://opencollective.com/eslint"
}
},
- "node_modules/es-define-property": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
- "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
- "license": "MIT",
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
"engines": {
- "node": ">= 0.4"
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
- "node_modules/es-errors": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
- "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "node_modules/eslint/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
"license": "MIT",
- "engines": {
- "node": ">= 0.4"
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
}
},
- "node_modules/es-get-iterator": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz",
- "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==",
+ "node_modules/eslint/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"dev": true,
- "license": "MIT",
+ "license": "ISC",
"dependencies": {
- "call-bind": "^1.0.2",
- "get-intrinsic": "^1.1.3",
- "has-symbols": "^1.0.3",
- "is-arguments": "^1.1.1",
- "is-map": "^2.0.2",
- "is-set": "^2.0.2",
- "is-string": "^1.0.7",
- "isarray": "^2.0.5",
- "stop-iteration-iterator": "^1.0.0"
+ "brace-expansion": "^1.1.7"
},
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
+ "engines": {
+ "node": "*"
}
},
- "node_modules/es-module-lexer": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
- "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+ "node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
"dev": true,
- "license": "MIT"
- },
- "node_modules/es-object-atoms": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
- "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
- "license": "MIT",
+ "license": "BSD-2-Clause",
"dependencies": {
- "es-errors": "^1.3.0"
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
},
"engines": {
- "node": ">= 0.4"
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
- "node_modules/es-set-tostringtag": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
- "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
- "license": "MIT",
+ "node_modules/esquery": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
"dependencies": {
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.6",
- "has-tostringtag": "^1.0.2",
- "hasown": "^2.0.2"
+ "estraverse": "^5.1.0"
},
"engines": {
- "node": ">= 0.4"
+ "node": ">=0.10"
}
},
- "node_modules/esbuild": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
- "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
"dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "bin": {
- "esbuild": "bin/esbuild"
- },
- "engines": {
- "node": ">=18"
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
},
- "optionalDependencies": {
- "@esbuild/aix-ppc64": "0.27.3",
- "@esbuild/android-arm": "0.27.3",
- "@esbuild/android-arm64": "0.27.3",
- "@esbuild/android-x64": "0.27.3",
- "@esbuild/darwin-arm64": "0.27.3",
- "@esbuild/darwin-x64": "0.27.3",
- "@esbuild/freebsd-arm64": "0.27.3",
- "@esbuild/freebsd-x64": "0.27.3",
- "@esbuild/linux-arm": "0.27.3",
- "@esbuild/linux-arm64": "0.27.3",
- "@esbuild/linux-ia32": "0.27.3",
- "@esbuild/linux-loong64": "0.27.3",
- "@esbuild/linux-mips64el": "0.27.3",
- "@esbuild/linux-ppc64": "0.27.3",
- "@esbuild/linux-riscv64": "0.27.3",
- "@esbuild/linux-s390x": "0.27.3",
- "@esbuild/linux-x64": "0.27.3",
- "@esbuild/netbsd-arm64": "0.27.3",
- "@esbuild/netbsd-x64": "0.27.3",
- "@esbuild/openbsd-arm64": "0.27.3",
- "@esbuild/openbsd-x64": "0.27.3",
- "@esbuild/openharmony-arm64": "0.27.3",
- "@esbuild/sunos-x64": "0.27.3",
- "@esbuild/win32-arm64": "0.27.3",
- "@esbuild/win32-ia32": "0.27.3",
- "@esbuild/win32-x64": "0.27.3"
+ "engines": {
+ "node": ">=4.0"
}
},
- "node_modules/escalade": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
- "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
- "license": "MIT",
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
"engines": {
- "node": ">=6"
+ "node": ">=4.0"
}
},
- "node_modules/escape-html": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
- "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
- "license": "MIT"
- },
"node_modules/estree-walker": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
@@ -3944,6 +5641,16 @@
"@types/estree": "^1.0.0"
}
},
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
@@ -4072,8 +5779,8 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "license": "MIT",
- "optional": true
+ "devOptional": true,
+ "license": "MIT"
},
"node_modules/fast-glob": {
"version": "3.3.3",
@@ -4105,6 +5812,20 @@
"node": ">= 6"
}
},
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/fast-safe-stringify": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
@@ -4167,6 +5888,19 @@
"node": ">=0.8.0"
}
},
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -4213,6 +5947,23 @@
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/firebase": {
"version": "10.14.1",
"resolved": "https://registry.npmjs.org/firebase/-/firebase-10.14.1.tgz",
@@ -4304,6 +6055,28 @@
}
}
},
+ "node_modules/flat-cache": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
+ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz",
+ "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/follow-redirects": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
@@ -4408,6 +6181,13 @@
"node": ">= 0.6"
}
},
+ "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",
@@ -4432,6 +6212,27 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/function.prototype.name": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
+ "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "functions-have-names": "^1.2.3",
+ "hasown": "^2.0.2",
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/functional-red-black-tree": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
@@ -4495,6 +6296,16 @@
"node": ">=14"
}
},
+ "node_modules/generator-function": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",
+ "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -4551,6 +6362,24 @@
"node": ">= 0.4"
}
},
+ "node_modules/get-symbol-description": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
+ "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/get-tsconfig": {
"version": "4.13.6",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz",
@@ -4564,6 +6393,28 @@
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
},
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -4577,6 +6428,84 @@
"node": ">=10.13.0"
}
},
+ "node_modules/glob/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/google-auth-library": {
"version": "9.15.1",
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz",
@@ -4688,6 +6617,13 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/gtoken": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz",
@@ -4738,6 +6674,22 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/has-proto": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",
+ "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
@@ -4786,6 +6738,23 @@
"node": ">=18.0.0"
}
},
+ "node_modules/hermes-estree": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
+ "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hermes-parser": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
+ "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hermes-estree": "0.25.1"
+ }
+ },
"node_modules/html-encoding-sniffer": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
@@ -4909,6 +6878,43 @@
"integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==",
"license": "ISC"
},
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
"node_modules/indent-string": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
@@ -4919,6 +6925,18 @@
"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",
@@ -4993,6 +7011,26 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-async-function": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
+ "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "async-function": "^1.0.0",
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-bigint": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",
@@ -5039,6 +7077,29 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-bun-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz",
+ "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.7.1"
+ }
+ },
+ "node_modules/is-bun-module/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/is-callable": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
@@ -5068,6 +7129,24 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-data-view": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",
+ "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-date-object": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
@@ -5095,6 +7174,22 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-finalizationregistry": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
+ "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
@@ -5104,6 +7199,26 @@
"node": ">=8"
}
},
+ "node_modules/is-generator-function": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
+ "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.4",
+ "generator-function": "^2.0.0",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -5130,6 +7245,19 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-negative-zero": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -5157,6 +7285,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-potential-custom-element-name": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
@@ -5260,6 +7398,22 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-weakmap": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
@@ -5273,6 +7427,22 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-weakref": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
+ "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-weakset": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",
@@ -5297,6 +7467,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/istanbul-lib-coverage": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
@@ -5379,6 +7556,24 @@
"node": ">=8"
}
},
+ "node_modules/iterator.prototype": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
+ "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "get-proto": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/jiti": {
"version": "1.21.7",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
@@ -5404,6 +7599,19 @@
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"license": "MIT"
},
+ "node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
"node_modules/jsdom": {
"version": "22.1.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz",
@@ -5551,6 +7759,27 @@
"bignumber.js": "^9.0.0"
}
},
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -5598,6 +7827,22 @@
"node": ">=10"
}
},
+ "node_modules/jsx-ast-utils": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
+ "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.6",
+ "array.prototype.flat": "^1.3.1",
+ "object.assign": "^4.1.4",
+ "object.values": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
"node_modules/jwa": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
@@ -5635,6 +7880,30 @@
"safe-buffer": "^5.0.1"
}
},
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/lilconfig": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
@@ -5660,6 +7929,22 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/lodash": {
"version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
@@ -5715,6 +8000,13 @@
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
"license": "MIT"
},
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
@@ -5942,6 +8234,32 @@
"node": ">=4"
}
},
+ "node_modules/minimatch": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+ "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/morgan": {
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz",
@@ -6022,6 +8340,29 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
+ "node_modules/napi-postinstall": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz",
+ "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "napi-postinstall": "lib/cli.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/napi-postinstall"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@@ -6031,6 +8372,25 @@
"node": ">= 0.6"
}
},
+ "node_modules/node-exports-info": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz",
+ "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array.prototype.flatmap": "^1.3.3",
+ "es-errors": "^1.3.0",
+ "object.entries": "^1.1.9",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
@@ -6143,19 +8503,88 @@
"node": ">= 0.4"
}
},
- "node_modules/object.assign": {
- "version": "4.1.7",
- "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
- "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+ "node_modules/object.assign": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
+ "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.entries": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz",
+ "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.fromentries": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
+ "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.groupby": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
+ "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
+ "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.8",
"call-bound": "^1.0.3",
"define-properties": "^1.2.1",
- "es-object-atoms": "^1.0.0",
- "has-symbols": "^1.1.0",
- "object-keys": "^1.1.1"
+ "es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
@@ -6206,12 +8635,48 @@
"wrappy": "1"
}
},
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/own-keys": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
+ "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-intrinsic": "^1.2.6",
+ "object-keys": "^1.1.1",
+ "safe-push-apply": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "devOptional": true,
"license": "MIT",
- "optional": true,
"dependencies": {
"yocto-queue": "^0.1.0"
},
@@ -6222,6 +8687,35 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/parse5": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
@@ -6244,6 +8738,36 @@
"node": ">= 0.8"
}
},
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-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"
+ }
+ },
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
@@ -6257,6 +8781,16 @@
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
"license": "MIT"
},
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/pathe": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
@@ -6477,6 +9011,16 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/pretty-format": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
@@ -6505,6 +9049,25 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/prop-types/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/proto3-json-serializer": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz",
@@ -6777,6 +9340,29 @@
"node": ">=8"
}
},
+ "node_modules/reflect.getprototypeof": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
+ "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.7",
+ "get-proto": "^1.0.1",
+ "which-builtin-type": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/regexp.prototype.flags": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
@@ -6835,6 +9421,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/resolve-pkg-maps": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
@@ -6881,6 +9477,23 @@
"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.59.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
@@ -6967,6 +9580,26 @@
"tslib": "^2.1.0"
}
},
+ "node_modules/safe-array-concat": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
+ "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "has-symbols": "^1.1.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -6987,6 +9620,23 @@
],
"license": "MIT"
},
+ "node_modules/safe-push-apply": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
+ "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/safe-regex-test": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
@@ -7143,12 +9793,50 @@
"node": ">= 0.4"
}
},
+ "node_modules/set-proto": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
+ "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"license": "ISC"
},
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/shell-quote": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
@@ -7241,6 +9929,16 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -7257,6 +9955,13 @@
"integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==",
"dev": true
},
+ "node_modules/stable-hash": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz",
+ "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/stackback": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
@@ -7294,45 +9999,143 @@
"node": ">= 0.4"
}
},
- "node_modules/stream-events": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
- "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==",
+ "node_modules/stream-events": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
+ "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "stubs": "^3.0.0"
+ }
+ },
+ "node_modules/stream-shift": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
+ "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string.prototype.matchall": {
+ "version": "4.0.12",
+ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
+ "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.6",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "regexp.prototype.flags": "^1.5.3",
+ "set-function-name": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.repeat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
+ "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
+ "dev": true,
"license": "MIT",
- "optional": true,
"dependencies": {
- "stubs": "^3.0.0"
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
}
},
- "node_modules/stream-shift": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
- "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==",
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.10",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
+ "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
+ "dev": true,
"license": "MIT",
- "optional": true
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-data-property": "^1.1.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-object-atoms": "^1.0.0",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
- "node_modules/string_decoder": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
- "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
+ "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
+ "dev": true,
"license": "MIT",
- "optional": true,
"dependencies": {
- "safe-buffer": "~5.2.0"
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "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/string.prototype.trimstart": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
},
"engines": {
- "node": ">=8"
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/strip-ansi": {
@@ -7347,6 +10150,16 @@
"node": ">=8"
}
},
+ "node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/strip-indent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
@@ -7360,6 +10173,19 @@
"node": ">=8"
}
},
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/strnum": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz",
@@ -7611,6 +10437,13 @@
"uuid": "dist/bin/uuid"
}
},
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/thenify": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
@@ -7764,6 +10597,19 @@
"tree-kill": "cli.js"
}
},
+ "node_modules/ts-api-utils": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz",
+ "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.2.0"
+ }
+ },
"node_modules/ts-interface-checker": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
@@ -7771,6 +10617,32 @@
"dev": true,
"license": "Apache-2.0"
},
+ "node_modules/tsconfig-paths": {
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
+ "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "node_modules/tsconfig-paths/node_modules/json5": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -7797,6 +10669,32 @@
"fsevents": "~2.3.3"
}
},
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@@ -7810,6 +10708,84 @@
"node": ">= 0.6"
}
},
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typed-array-byte-length": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
+ "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-byte-offset": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
+ "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.15",
+ "reflect.getprototypeof": "^1.0.9"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
+ "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "is-typed-array": "^1.1.13",
+ "possible-typed-array-names": "^1.0.0",
+ "reflect.getprototypeof": "^1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
@@ -7824,6 +10800,25 @@
"node": ">=14.17"
}
},
+ "node_modules/unbox-primitive": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
+ "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "which-boxed-primitive": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/undici": {
"version": "6.19.7",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.19.7.tgz",
@@ -7858,6 +10853,41 @@
"node": ">= 0.8"
}
},
+ "node_modules/unrs-resolver": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz",
+ "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "napi-postinstall": "^0.3.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unrs-resolver"
+ },
+ "optionalDependencies": {
+ "@unrs/resolver-binding-android-arm-eabi": "1.11.1",
+ "@unrs/resolver-binding-android-arm64": "1.11.1",
+ "@unrs/resolver-binding-darwin-arm64": "1.11.1",
+ "@unrs/resolver-binding-darwin-x64": "1.11.1",
+ "@unrs/resolver-binding-freebsd-x64": "1.11.1",
+ "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1",
+ "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1",
+ "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-arm64-musl": "1.11.1",
+ "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1",
+ "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-x64-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-x64-musl": "1.11.1",
+ "@unrs/resolver-binding-wasm32-wasi": "1.11.1",
+ "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1",
+ "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1",
+ "@unrs/resolver-binding-win32-x64-msvc": "1.11.1"
+ }
+ },
"node_modules/update-browserslist-db": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
@@ -7889,6 +10919,16 @@
"browserslist": ">= 4.21.0"
}
},
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
"node_modules/url-parse": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
@@ -8730,6 +11770,22 @@
"webidl-conversions": "^3.0.0"
}
},
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/which-boxed-primitive": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
@@ -8750,6 +11806,34 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/which-builtin-type": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
+ "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "function.prototype.name": "^1.1.6",
+ "has-tostringtag": "^1.0.2",
+ "is-async-function": "^2.0.0",
+ "is-date-object": "^1.1.0",
+ "is-finalizationregistry": "^1.1.0",
+ "is-generator-function": "^1.0.10",
+ "is-regex": "^1.2.1",
+ "is-weakref": "^1.0.2",
+ "isarray": "^2.0.5",
+ "which-boxed-primitive": "^1.1.0",
+ "which-collection": "^1.0.2",
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/which-collection": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
@@ -8808,6 +11892,16 @@
"node": ">=8"
}
},
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
@@ -8918,8 +12012,8 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "devOptional": true,
"license": "MIT",
- "optional": true,
"engines": {
"node": ">=10"
},
@@ -8936,6 +12030,19 @@
"url": "https://github.com/sponsors/colinhacks"
}
},
+ "node_modules/zod-validation-error": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
+ "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
+ }
+ },
"packages/client": {
"name": "@app/client",
"version": "0.0.1",
diff --git a/package.json b/package.json
index c18824d..86668ea 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,13 @@
"test:server": "npm run test --workspace=packages/server"
},
"devDependencies": {
- "concurrently": "^8.2.2"
+ "@typescript-eslint/eslint-plugin": "^6.3.0",
+ "@typescript-eslint/parser": "^6.3.0",
+ "concurrently": "^8.2.2",
+ "eslint": "^8.43.0",
+ "eslint-import-resolver-typescript": "^3.8.0",
+ "eslint-plugin-import": "^2.29.1",
+ "eslint-plugin-react": "^7.34.0",
+ "eslint-plugin-react-hooks": "^7.0.1"
}
}
diff --git a/packages/client/src/App.tsx b/packages/client/src/App.tsx
index 8c4d790..158a73e 100644
--- a/packages/client/src/App.tsx
+++ b/packages/client/src/App.tsx
@@ -5,7 +5,7 @@ import ErrorBoundary from './features/common/ErrorBoundary';
import Login from './features/login/Login';
import Dashboard from './features/dashboard/Dashboard';
-export default function App() {
+export default function App(): JSX.Element {
return (
diff --git a/packages/client/src/api/hooks.ts b/packages/client/src/api/hooks.ts
index d3fb4ca..284faee 100644
--- a/packages/client/src/api/hooks.ts
+++ b/packages/client/src/api/hooks.ts
@@ -1,11 +1,12 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
+import type { UseQueryResult, UseMutationResult } from '@tanstack/react-query';
import { useAuth } from '../features/auth/AuthContext';
import * as userService from './services/userService';
import { MeResponse } from './types';
// helpers have been removed; useQuery/useMutation object form below
-export function useMe() {
+export function useMe(): UseQueryResult {
const { user, getIdToken } = useAuth();
return useQuery({
@@ -18,7 +19,7 @@ export function useMe() {
});
}
-export function useUpdateProfile() {
+export function useUpdateProfile(): UseMutationResult> {
const queryClient = useQueryClient();
const { getIdToken } = useAuth();
@@ -33,7 +34,7 @@ export function useUpdateProfile() {
});
}
-export function useDeleteAccount() {
+export function useDeleteAccount(): UseMutationResult {
const queryClient = useQueryClient();
const { signOut, getIdToken } = useAuth();
diff --git a/packages/client/src/api/services/userService.ts b/packages/client/src/api/services/userService.ts
index 02f274b..6ea71aa 100644
--- a/packages/client/src/api/services/userService.ts
+++ b/packages/client/src/api/services/userService.ts
@@ -7,11 +7,11 @@ import { MeResponse } from '../types';
* Axios accepts `undefined` headers so callers can omit the token entirely
* during unit tests or unauthenticated calls.
*/
-function makeAuthHeader(token?: string) {
+function makeAuthHeader(token?: string): Record | undefined {
return token ? { Authorization: `Bearer ${token}` } : undefined;
}
-export async function getMe(token?: string) {
+export async function getMe(token?: string): Promise {
const res = await axios.get('/me', {
headers: makeAuthHeader(token),
});
@@ -21,14 +21,14 @@ export async function getMe(token?: string) {
export async function updateProfile(
data: Partial<{ name: string; picture: string }>,
token?: string,
-) {
+): Promise {
const res = await axios.put('/me', data, {
headers: makeAuthHeader(token),
});
return res.data;
}
-export async function deleteAccount(token?: string) {
+export async function deleteAccount(token?: string): Promise {
await axios.delete('/me', {
headers: makeAuthHeader(token),
});
diff --git a/packages/client/src/features/auth/AuthContext.tsx b/packages/client/src/features/auth/AuthContext.tsx
index 2335024..6eee13a 100644
--- a/packages/client/src/features/auth/AuthContext.tsx
+++ b/packages/client/src/features/auth/AuthContext.tsx
@@ -27,7 +27,7 @@ interface AuthContextValue {
const AuthContext = createContext(null);
-export function AuthProvider({ children }: { children: ReactNode }) {
+export function AuthProvider({ children }: { children: ReactNode }): JSX.Element {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
@@ -39,13 +39,13 @@ export function AuthProvider({ children }: { children: ReactNode }) {
return unsubscribe;
}, []);
- const signInWithGoogle = async () => {
+ const signInWithGoogle = async (): Promise => {
await signInWithPopup(auth, googleProvider);
};
const queryClient = useQueryClient();
- const signOut = useCallback(async () => {
+ const signOut = useCallback(async (): Promise => {
await firebaseSignOut(auth);
// clear any cached server data when user signs out
queryClient.clear();
diff --git a/packages/client/src/features/common/ErrorBoundary.tsx b/packages/client/src/features/common/ErrorBoundary.tsx
index a2e78e6..1830828 100644
--- a/packages/client/src/features/common/ErrorBoundary.tsx
+++ b/packages/client/src/features/common/ErrorBoundary.tsx
@@ -1,4 +1,4 @@
-import React, { ReactNode, ErrorInfo } from 'react';
+import { Component, ReactNode, ErrorInfo } from 'react';
interface ErrorBoundaryProps {
children: ReactNode;
@@ -12,10 +12,7 @@ interface ErrorBoundaryState {
// Generic error boundary component that can be reused throughout the app.
// It captures rendering errors in its subtree and displays a fallback UI.
-export default class ErrorBoundary extends React.Component<
- ErrorBoundaryProps,
- ErrorBoundaryState
-> {
+export default class ErrorBoundary extends Component {
constructor(props: ErrorBoundaryProps) {
super(props);
this.state = { hasError: false, error: null };
@@ -26,16 +23,16 @@ export default class ErrorBoundary extends React.Component<
return { hasError: true, error };
}
- componentDidCatch(error: Error, info: ErrorInfo) {
+ componentDidCatch(error: Error, info: ErrorInfo): void {
// You can log the error to an external service here
console.error('ErrorBoundary caught an error', error, info);
}
- reset = () => {
+ reset = (): void => {
this.setState({ hasError: false, error: null });
};
- render() {
+ render(): ReactNode {
if (this.state.hasError) {
if (this.props.fallback) {
return <>{this.props.fallback}>;
diff --git a/packages/client/src/features/common/ProtectedRoute.tsx b/packages/client/src/features/common/ProtectedRoute.tsx
index dd2e6b8..74c9dc7 100644
--- a/packages/client/src/features/common/ProtectedRoute.tsx
+++ b/packages/client/src/features/common/ProtectedRoute.tsx
@@ -2,7 +2,7 @@ import { Navigate } from 'react-router-dom';
import { useAuth } from '../auth/AuthContext';
import { ReactNode } from 'react';
-export default function ProtectedRoute({ children }: { children: ReactNode }) {
+export default function ProtectedRoute({ children }: { children: ReactNode }): JSX.Element {
const { user, loading } = useAuth();
if (loading) {
diff --git a/packages/client/src/features/dashboard/Dashboard.test.tsx b/packages/client/src/features/dashboard/Dashboard.test.tsx
index df2b52d..e06f614 100644
--- a/packages/client/src/features/dashboard/Dashboard.test.tsx
+++ b/packages/client/src/features/dashboard/Dashboard.test.tsx
@@ -6,6 +6,8 @@ import Dashboard from './Dashboard';
import * as AuthContext from '../auth/AuthContext';
import * as hooks from '../../api/hooks';
import type { User } from 'firebase/auth';
+import type { UseQueryResult, UseMutationResult } from '@tanstack/react-query';
+import type { MeResponse } from '../../api/types';
vi.mock('../../api/hooks', () => ({
useMe: vi.fn(),
@@ -40,15 +42,15 @@ describe('Dashboard', () => {
isLoading: false,
error: null,
refetch: vi.fn(),
- } as any);
+ } as unknown as UseQueryResult);
vi.mocked(hooks.useUpdateProfile).mockReturnValue({
mutate: vi.fn(),
isPending: false,
- } as any);
+ } as unknown as UseMutationResult>);
vi.mocked(hooks.useDeleteAccount).mockReturnValue({
mutate: vi.fn(),
isPending: false,
- } as any);
+ } as unknown as UseMutationResult);
});
afterEach(() => vi.restoreAllMocks());
@@ -70,18 +72,18 @@ describe('Dashboard', () => {
isLoading: true,
error: null,
refetch: vi.fn(),
- } as any);
+ } as unknown as UseQueryResult);
render(, { wrapper });
expect(screen.getByRole('button', { name: /fetching/i })).toBeInTheDocument();
});
it('displays API response data when available', () => {
vi.mocked(hooks.useMe).mockReturnValue({
- data: { uid: '123', email: 'test@example.com' },
+ data: { uid: '123', email: 'test@example.com' } as MeResponse,
isLoading: false,
error: null,
refetch: vi.fn(),
- } as any);
+ } as unknown as UseQueryResult);
render(, { wrapper });
expect(screen.getByText(/"uid": "123"/)).toBeInTheDocument();
});
@@ -92,7 +94,7 @@ describe('Dashboard', () => {
isLoading: false,
error: new Error('Unauthorized'),
refetch: vi.fn(),
- } as any);
+ } as unknown as UseQueryResult);
render(, { wrapper });
expect(screen.getByText(/unauthorized/i)).toBeInTheDocument();
});
@@ -104,7 +106,7 @@ describe('Dashboard', () => {
isLoading: false,
error: null,
refetch,
- } as any);
+ } as unknown as UseQueryResult);
render(, { wrapper });
await userEvent.click(screen.getByRole('button', { name: /call \/api\/me/i }));
expect(refetch).toHaveBeenCalledOnce();
diff --git a/packages/client/src/features/dashboard/Dashboard.tsx b/packages/client/src/features/dashboard/Dashboard.tsx
index 2d67450..bbc3458 100644
--- a/packages/client/src/features/dashboard/Dashboard.tsx
+++ b/packages/client/src/features/dashboard/Dashboard.tsx
@@ -2,7 +2,7 @@ import { useState } from 'react';
import { useAuth } from '../auth/AuthContext';
import { useMe, useUpdateProfile, useDeleteAccount } from '../../api/hooks';
-export default function Dashboard() {
+export default function Dashboard(): JSX.Element {
const { user, signOut } = useAuth();
const { data, isLoading, error, refetch } = useMe();
const [updateSuccess, setUpdateSuccess] = useState(false);
diff --git a/packages/client/src/features/login/Login.tsx b/packages/client/src/features/login/Login.tsx
index 976c9a3..44908b3 100644
--- a/packages/client/src/features/login/Login.tsx
+++ b/packages/client/src/features/login/Login.tsx
@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useAuth } from '../auth/AuthContext';
-export default function Login() {
+export default function Login(): JSX.Element {
const { user, signInWithGoogle } = useAuth();
const navigate = useNavigate();
const [error, setError] = useState(null);
diff --git a/packages/client/vitest.config.ts b/packages/client/vitest.config.ts
index 8e07127..30d58aa 100644
--- a/packages/client/vitest.config.ts
+++ b/packages/client/vitest.config.ts
@@ -4,7 +4,7 @@ import react from '@vitejs/plugin-react';
export default defineConfig({
// cast needed: vitest bundles its own vite internally, causing a Plugin type
// mismatch between the project's vite and vitest's internal copy at runtime.
- plugins: [react() as any],
+ plugins: [react() as unknown as import('vite').Plugin],
test: {
globals: true,
environment: 'jsdom',
diff --git a/packages/server/package.json b/packages/server/package.json
index 5dc33f8..5838468 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -6,7 +6,7 @@
"dev": "tsx watch src/index.ts",
"build": "tsc --outDir dist",
"start": "node dist/index.js",
- "lint": "tsc --noEmit",
+ "lint": "eslint src --ext ts --max-warnings=0 && tsc --noEmit",
"test": "vitest run --config vitest.config.ts",
"test:watch": "vitest --config vitest.config.ts"
},
diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts
index 89385a9..b956452 100644
--- a/packages/server/src/app.ts
+++ b/packages/server/src/app.ts
@@ -1,4 +1,4 @@
-import express from 'express';
+import express, { Express } from 'express';
import apiRouter from './routes';
import { applySecurity } from './middleware/security';
import { applyLogging } from './middleware/logger';
@@ -7,7 +7,7 @@ import { applyCors } from './middleware/cors';
import { errorHandler } from './middleware/errorHandler';
import { config } from './config';
-export function createApp() {
+export function createApp(): Express {
const app = express();
// trust first proxy hop so express-rate-limit uses the real client IP
diff --git a/packages/server/src/middleware/cors.ts b/packages/server/src/middleware/cors.ts
index 068ed81..550908c 100644
--- a/packages/server/src/middleware/cors.ts
+++ b/packages/server/src/middleware/cors.ts
@@ -5,6 +5,6 @@ import cors from 'cors';
* Applies CORS middleware. Accepts a single origin string or an array so that
* comma-separated values from CORS_ORIGIN env var can be forwarded directly.
*/
-export function applyCors(app: Express, origin: string | string[]) {
+export function applyCors(app: Express, origin: string | string[]): void {
app.use(cors({ origin, credentials: true }));
}
diff --git a/packages/server/src/middleware/errorHandler.ts b/packages/server/src/middleware/errorHandler.ts
index 43b2763..e5bb2e9 100644
--- a/packages/server/src/middleware/errorHandler.ts
+++ b/packages/server/src/middleware/errorHandler.ts
@@ -2,7 +2,7 @@ import { NextFunction, Request, Response } from 'express';
import { AppError } from './AppError';
/** Central error handler – must be the last middleware registered in app.ts. */
-export function errorHandler(err: unknown, req: Request, res: Response, _next: NextFunction) {
+export function errorHandler(err: unknown, req: Request, res: Response, _next: NextFunction): void {
// Structured log: include method + path for quick triage; never log auth tokens.
const context = `${req.method} ${req.path}`;
if (err instanceof AppError) {
diff --git a/packages/server/src/middleware/logger.ts b/packages/server/src/middleware/logger.ts
index a32b7bf..0ff9bf1 100644
--- a/packages/server/src/middleware/logger.ts
+++ b/packages/server/src/middleware/logger.ts
@@ -1,7 +1,7 @@
import { Express } from 'express';
import morgan from 'morgan';
-export function applyLogging(app: Express) {
+export function applyLogging(app: Express): void {
// 'combined' in production includes user-agent, referrer, and response time;
// 'dev' in development gives colour-coded concise output.
const format = process.env.NODE_ENV === 'production' ? 'combined' : 'dev';
diff --git a/packages/server/src/middleware/rateLimiter.ts b/packages/server/src/middleware/rateLimiter.ts
index 92d5b63..fe68ffc 100644
--- a/packages/server/src/middleware/rateLimiter.ts
+++ b/packages/server/src/middleware/rateLimiter.ts
@@ -6,7 +6,7 @@ export interface RateLimitOptions {
max: number;
}
-export function applyRateLimiting(app: Express, opts: RateLimitOptions) {
+export function applyRateLimiting(app: Express, opts: RateLimitOptions): void {
const limiter = rateLimit(opts);
app.use(limiter);
}
diff --git a/packages/server/src/middleware/security.ts b/packages/server/src/middleware/security.ts
index 8f42eac..91f675d 100644
--- a/packages/server/src/middleware/security.ts
+++ b/packages/server/src/middleware/security.ts
@@ -1,7 +1,7 @@
import { Express } from 'express';
import helmet from 'helmet';
-export function applySecurity(app: Express) {
+export function applySecurity(app: Express): void {
// set various HTTP headers to secure the app
app.use(helmet());
}
diff --git a/packages/server/tests/e2e/health.test.ts b/packages/server/tests/e2e/health.test.ts
index e5de2a2..b485d12 100644
--- a/packages/server/tests/e2e/health.test.ts
+++ b/packages/server/tests/e2e/health.test.ts
@@ -1,4 +1,3 @@
-///
import { describe, it, expect, vi } from 'vitest';
// prevent firebase-admin from initialising during app import
diff --git a/packages/server/tests/e2e/userRoutes.test.ts b/packages/server/tests/e2e/userRoutes.test.ts
index e98b520..750d534 100644
--- a/packages/server/tests/e2e/userRoutes.test.ts
+++ b/packages/server/tests/e2e/userRoutes.test.ts
@@ -1,5 +1,5 @@
-///
import { describe, it, expect, beforeEach, vi } from 'vitest';
+import type { DecodedIdToken } from 'firebase-admin/auth';
// mock firebase before any app module is loaded
vi.mock('../../src/firebase', () => ({ default: {} }));
@@ -12,7 +12,7 @@ import request from 'supertest';
import { createApp } from '../../src/app';
import { verifyIdToken, deleteUser } from '../../src/repositories/userRepository';
-const decoded: any = { uid: 'user123', email: 'a@b.com', name: 'Alice', picture: null };
+const decoded = { uid: 'user123', email: 'a@b.com', name: 'Alice', picture: null } as unknown as DecodedIdToken;
describe('e2e /api/me', () => {
beforeEach(() => {
diff --git a/packages/server/tests/unit/config.test.ts b/packages/server/tests/unit/config.test.ts
index 1b9c083..519d511 100644
--- a/packages/server/tests/unit/config.test.ts
+++ b/packages/server/tests/unit/config.test.ts
@@ -1,4 +1,3 @@
-///
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
describe('config parsing', () => {
diff --git a/packages/server/tests/unit/controllers/userController.test.ts b/packages/server/tests/unit/controllers/userController.test.ts
index 568e363..9c65bef 100644
--- a/packages/server/tests/unit/controllers/userController.test.ts
+++ b/packages/server/tests/unit/controllers/userController.test.ts
@@ -1,5 +1,6 @@
-///
import { describe, it, expect, vi } from 'vitest';
+import type { DecodedIdToken } from 'firebase-admin/auth';
+import type { Request, Response, NextFunction } from 'express';
vi.mock('../../../src/services/userService', () => ({
getUserProfile: vi.fn(),
@@ -10,18 +11,18 @@ vi.mock('../../../src/services/userService', () => ({
import { getMe, updateMe, deleteMe } from '../../../src/controllers/userController';
import { getUserProfile, applyProfileUpdate, deleteUserAccount } from '../../../src/services/userService';
-const fakeDecoded: any = { uid: 'abc', email: 'test@example.com', name: 'Test User', picture: undefined };
+const fakeDecoded = { uid: 'abc', email: 'test@example.com', name: 'Test User', picture: undefined } as unknown as DecodedIdToken;
describe('userController.getMe', () => {
it('responds with profile JSON', async () => {
const fakeProfile = { uid: 'abc', email: 'test@example.com', name: 'Test User', picture: undefined };
- vi.mocked(getUserProfile).mockReturnValue(fakeProfile);
+ vi.mocked(getUserProfile).mockReturnValue(fakeProfile as unknown as ReturnType);
- const req: any = { user: fakeDecoded };
- const res: any = { json: vi.fn() };
+ const req = { user: fakeDecoded } as unknown as Request & { user: DecodedIdToken };
+ const res = { json: vi.fn() } as unknown as Response;
const next = vi.fn();
- await getMe(req, res, next);
+ await getMe(req, res, next as unknown as NextFunction);
expect(getUserProfile).toHaveBeenCalledWith(fakeDecoded);
expect(res.json).toHaveBeenCalledWith(fakeProfile);
expect(next).not.toHaveBeenCalled();
@@ -30,24 +31,24 @@ describe('userController.getMe', () => {
describe('userController.updateMe', () => {
it('returns 400 when body is invalid', async () => {
- const req: any = { user: fakeDecoded, body: { picture: 'not-a-url' } };
- const res: any = { status: vi.fn().mockReturnThis(), json: vi.fn() };
+ const req = { user: fakeDecoded, body: { picture: 'not-a-url' } } as unknown as Request & { user: DecodedIdToken };
+ const res = { status: vi.fn().mockReturnThis(), json: vi.fn() } as unknown as Response;
const next = vi.fn();
- await updateMe(req, res, next);
+ await updateMe(req, res, next as unknown as NextFunction);
expect(res.status).toHaveBeenCalledWith(400);
expect(res.json).toHaveBeenCalledWith(expect.objectContaining({ error: expect.any(String) }));
});
it('returns updated profile on valid body', async () => {
const updated = { uid: 'abc', name: 'New Name' };
- vi.mocked(applyProfileUpdate).mockReturnValue(updated as any);
+ vi.mocked(applyProfileUpdate).mockReturnValue(updated as unknown as ReturnType);
- const req: any = { user: fakeDecoded, body: { name: 'New Name' } };
- const res: any = { json: vi.fn() };
+ const req = { user: fakeDecoded, body: { name: 'New Name' } } as unknown as Request & { user: DecodedIdToken };
+ const res = { json: vi.fn() } as unknown as Response;
const next = vi.fn();
- await updateMe(req, res, next);
+ await updateMe(req, res, next as unknown as NextFunction);
expect(applyProfileUpdate).toHaveBeenCalledWith(fakeDecoded, { name: 'New Name' });
expect(res.json).toHaveBeenCalledWith(updated);
});
@@ -56,11 +57,11 @@ describe('userController.updateMe', () => {
describe('userController.deleteMe', () => {
it('returns 204 with no body', async () => {
vi.mocked(deleteUserAccount).mockResolvedValue();
- const req: any = { user: fakeDecoded };
- const res: any = { status: vi.fn().mockReturnThis(), send: vi.fn() };
+ const req = { user: fakeDecoded } as unknown as Request & { user: DecodedIdToken };
+ const res = { status: vi.fn().mockReturnThis(), send: vi.fn() } as unknown as Response;
const next = vi.fn();
- await deleteMe(req, res, next);
+ await deleteMe(req, res, next as unknown as NextFunction);
expect(deleteUserAccount).toHaveBeenCalledWith(fakeDecoded.uid);
expect(res.status).toHaveBeenCalledWith(204);
expect(res.send).toHaveBeenCalled();
diff --git a/packages/server/tests/unit/middleware/authMiddleware.test.ts b/packages/server/tests/unit/middleware/authMiddleware.test.ts
index 8376c09..f3e4a45 100644
--- a/packages/server/tests/unit/middleware/authMiddleware.test.ts
+++ b/packages/server/tests/unit/middleware/authMiddleware.test.ts
@@ -1,5 +1,6 @@
-///
import { expect, describe, it, vi, beforeEach } from 'vitest';
+import type { Response, NextFunction } from 'express';
+import type { DecodedIdToken } from 'firebase-admin/auth';
vi.mock('../../../src/repositories/userRepository', () => ({
verifyIdToken: vi.fn(),
@@ -10,8 +11,8 @@ import { verifyIdToken } from '../../../src/repositories/userRepository';
describe('authMiddleware', () => {
let req: Partial;
- let res: any;
- let next: any;
+ let res: Partial;
+ let next: ReturnType;
beforeEach(() => {
req = { headers: {} };
@@ -23,7 +24,7 @@ describe('authMiddleware', () => {
});
it('returns 401 if header missing', async () => {
- await authMiddleware(req as AuthenticatedRequest, res, next);
+ await authMiddleware(req as AuthenticatedRequest, res as Response, next as unknown as NextFunction);
expect(res.status).toHaveBeenCalledWith(401);
expect(res.json).toHaveBeenCalledWith({ error: 'Missing or invalid Authorization header' });
expect(next).not.toHaveBeenCalled();
@@ -32,18 +33,18 @@ describe('authMiddleware', () => {
it('returns 401 if token invalid', async () => {
req.headers = { authorization: 'Bearer bad' };
vi.mocked(verifyIdToken).mockRejectedValue(new Error('invalid'));
- await authMiddleware(req as AuthenticatedRequest, res, next);
+ await authMiddleware(req as AuthenticatedRequest, res as Response, next as unknown as NextFunction);
expect(res.status).toHaveBeenCalledWith(401);
expect(res.json).toHaveBeenCalledWith({ error: 'Invalid or expired token' });
expect(next).not.toHaveBeenCalled();
});
it('attaches decoded token and calls next on success', async () => {
- const decoded = { uid: '123' } as any;
+ const decoded = { uid: '123' } as unknown as DecodedIdToken;
req.headers = { authorization: 'Bearer good' };
vi.mocked(verifyIdToken).mockResolvedValue(decoded);
- await authMiddleware(req as AuthenticatedRequest, res, next);
+ await authMiddleware(req as AuthenticatedRequest, res as Response, next as unknown as NextFunction);
expect(req.user).toBe(decoded);
expect(next).toHaveBeenCalled();
});
diff --git a/packages/server/tests/unit/services/userService.test.ts b/packages/server/tests/unit/services/userService.test.ts
index e99e0e0..1dd151b 100644
--- a/packages/server/tests/unit/services/userService.test.ts
+++ b/packages/server/tests/unit/services/userService.test.ts
@@ -1,4 +1,3 @@
-///
import { describe, it, expect, vi, beforeEach } from 'vitest';
vi.mock('../../../src/repositories/userRepository', () => ({
From 63539018b82db43819f0c02d2505a91fd301f574 Mon Sep 17 00:00:00 2001
From: Daniel David
Date: Tue, 10 Mar 2026 13:01:42 +1100
Subject: [PATCH 8/8] docs: add linting section to README with usage
instructions
---
README.md | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/README.md b/README.md
index de64308..ec45c80 100644
--- a/README.md
+++ b/README.md
@@ -184,6 +184,34 @@ All workspace commands are defined at the root `package.json` and are forwarded
---
+## Linting 🔍
+
+The repository includes a shared ESLint configuration at the repo root. The primary lint command is wired at the root `package.json` and forwards to package-level lint scripts.
+
+Run lint across all packages:
+
+```bash
+npm run lint
+```
+
+Run lint for a single package:
+
+```bash
+npm run lint --workspace=packages/client
+npm run lint --workspace=packages/server
+```
+
+Auto-fix fixable issues across the repository:
+
+```bash
+npx eslint . --ext .ts,.tsx --fix
+```
+
+Notes:
+- The server `lint` script runs `eslint` followed by `tsc --noEmit` so type errors are checked as part of linting.
+- See [docs/eslint.md](docs/eslint.md) for full configuration details, rules and overrides.
+
+
## API Endpoints
| Method | Path | Auth | Description |