From 8af01e8310084df36999fd62b7c7eb16aa19af52 Mon Sep 17 00:00:00 2001
From: jslyonnais <6282845+jslyonnais@users.noreply.github.com>
Date: Fri, 20 Feb 2026 13:13:47 -0500
Subject: [PATCH 1/2] readme + env
---
.env.example | 5 +-
README.md | 298 +++++++--------------------
src/env.ts | 3 +-
src/integrations/clerk/provider.tsx | 3 +-
src/integrations/convex/provider.tsx | 3 +-
5 files changed, 85 insertions(+), 227 deletions(-)
diff --git a/.env.example b/.env.example
index 50a8c82..1b6a5f6 100644
--- a/.env.example
+++ b/.env.example
@@ -1,4 +1,3 @@
-# team: devjs, project: forms
CONVEX_DEPLOYMENT=
# Clerk configuration, get this key from your [Dashboard](dashboard.clerk.com)
@@ -6,6 +5,4 @@ VITE_CLERK_PUBLISHABLE_KEY=
CLERK_JWT_ISSUER_DOMAIN=
VITE_CONVEX_URL=
-VITE_CONVEX_SITE_URL=
-
-VITE_LOG_LEVEL=debug
\ No newline at end of file
+VITE_CONVEX_SITE_URL=
\ No newline at end of file
diff --git a/README.md b/README.md
index ed87589..fee3812 100644
--- a/README.md
+++ b/README.md
@@ -1,240 +1,98 @@
-Welcome to your new TanStack Start app!
-
-# Getting Started
-
-To run this application:
-
-```bash
-npm install
-npm run dev
-```
-
-# Building For Production
-
-To build this application for production:
-
-```bash
-npm run build
-```
-
-## Testing
-
-This project uses [Vitest](https://vitest.dev/) for testing. You can run the tests with:
-
-```bash
-npm run test
-```
-
-## Styling
-
-This project uses [Tailwind CSS](https://tailwindcss.com/) for styling.
-
-### Removing Tailwind CSS
-
-If you prefer not to use Tailwind CSS:
-
-1. Remove the demo pages in `src/routes/demo/`
-2. Replace the Tailwind import in `src/styles.css` with your own styles
-3. Remove `tailwindcss()` from the plugins array in `vite.config.ts`
-4. Uninstall the packages: `npm install @tailwindcss/vite tailwindcss -D`
-
-## Linting & Formatting
-
-This project uses [eslint](https://eslint.org/) and [prettier](https://prettier.io/) for linting and formatting. Eslint is configured using [tanstack/eslint-config](https://tanstack.com/config/latest/docs/eslint). The following scripts are available:
-
-```bash
-npm run lint
-npm run format
-npm run check
+# Tanstack Start - With E2E Encryption Demo
+
+A TanStack Start starter with Convex, Clerk, and end-to-end encryption.
+
+Full-stack React starter with zero-knowledge architecture. Sensitive data is encrypted client-side before it reaches the server; the backend never has access to decryption keys or plaintext.
+
+## Tech Stack
+
+- **Frontend:** TanStack Start, TanStack Router, TanStack Query
+- **Backend:** Convex (real-time database)
+- **Auth:** Clerk
+- **Encryption:** OpenPGP.js (ECC P-256), @scure/bip39 (passphrase generation)
+- **UI:** Tailwind CSS, shadcn, Radix UI
+- **State:** Jotai
+
+## Zero-Knowledge Architecture
+
+```mermaid
+flowchart TD
+ subgraph client [Client]
+ SignUp[Sign Up]
+ GenKeys[Generate PGP Key Pair]
+ EncPass[Encrypt Passphrase with Password]
+ StoreKeys[Store to Convex]
+ SignUp --> GenKeys --> EncPass --> StoreKeys
+ end
+
+ subgraph convex [Convex]
+ UserKeys[(userKeys table)]
+ StoreKeys -->|encryptedPrivateKey, publicKey, encryptedPassphrase| UserKeys
+ end
+
+ subgraph zk [Zero Knowledge]
+ NoPlaintext[Convex never sees plaintext]
+ NoPrivateKey[Private key never leaves client]
+ end
```
-## Setting up Clerk
-
-- Set the `VITE_CLERK_PUBLISHABLE_KEY` in your `.env.local`.
-
-## Setting up Convex
-
-- Set the `VITE_CONVEX_URL` and `CONVEX_DEPLOYMENT` environment variables in your `.env.local`. (Or run `npx -y convex init` to set them automatically.)
-- Run `npx -y convex dev` to start the Convex server.
+- **Key generation:** PGP key pairs (ECC nistP256) generated client-side in `src/lib/crypto.ts` during signup
+- **Passphrase:** 12-word BIP39 mnemonic (128-bit entropy), encrypted with the user's Clerk password before storage
+- **Storage:** Convex stores only encrypted blobs—`encryptedPrivateKey`, `publicKey`, `encryptedPassphrase`
+- **Usage:** `useEncryption` hook (`src/hooks/useEncryption.ts`) encrypts/decrypts data client-side; Convex cannot decrypt
-# Paraglide i18n
+## Prerequisites
-This add-on wires up ParaglideJS for localized routing and message formatting.
+- Node.js 18+
+- [Clerk](https://clerk.com) account
+- [Convex](https://convex.dev) account
-- Messages live in `project.inlang/messages`.
-- URLs are localized through the Paraglide Vite plugin and router `rewrite` hooks.
-- Run the dev server or build to regenerate the `src/paraglide` outputs.
-
-## Shadcn
-
-Add components using the latest version of [Shadcn](https://ui.shadcn.com/).
+## Getting Started
```bash
-pnpm dlx shadcn@latest add button
-```
-
-## T3Env
-
-- You can use T3Env to add type safety to your environment variables.
-- Add Environment variables to the `src/env.mjs` file.
-- Use the environment variables in your code.
-
-### Usage
-
-```ts
-import { env } from '@/env'
-
-console.log(env.VITE_APP_TITLE)
-```
-
-## Routing
-
-This project uses [TanStack Router](https://tanstack.com/router) with file-based routing. Routes are managed as files in `src/routes`.
-
-### Adding A Route
-
-To add a new route to your application just add a new file in the `./src/routes` directory.
-
-TanStack will automatically generate the content of the route file for you.
-
-Now that you have two routes you can use a `Link` component to navigate between them.
-
-### Adding Links
-
-To use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/react-router`.
-
-```tsx
-import { Link } from '@tanstack/react-router'
+npm install # Install dependencies packages
+cp .env.example .env.local # Edit .env.local with your keys
+npm run start # Starts Convex dev + Vite (port 3666)
```
-Then anywhere in your JSX you can use it like so:
+**Required environment variables** (from `.env.example`):
-```tsx
- About
-```
-
-This will create a link that will navigate to the `/about` route.
-
-More information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/react/api/router/linkComponent).
-
-### Using A Layout
-
-In the File Based Routing setup the layout is located in `src/routes/__root.tsx`. Anything you add to the root route will appear in all the routes. The route content will appear in the JSX where you render `{children}` in the `shellComponent`.
-
-Here is an example layout that includes a header:
-
-```tsx
-import { HeadContent, Scripts, createRootRoute } from '@tanstack/react-router'
-
-export const Route = createRootRoute({
- head: () => ({
- meta: [
- { charSet: 'utf-8' },
- { name: 'viewport', content: 'width=device-width, initial-scale=1' },
- { title: 'My App' },
- ],
- }),
- shellComponent: ({ children }) => (
-
-
-
-
-
-
- {children}
-
-
-
- ),
-})
-```
-
-More information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts).
+| Variable | Description |
+| -------------------------------------- | ------------------------------------- |
+| `VITE_CLERK_PUBLISHABLE_KEY` | From Clerk Dashboard |
+| `CLERK_JWT_ISSUER_DOMAIN` | For Convex JWT verification |
+| `VITE_CONVEX_URL`, `CONVEX_DEPLOYMENT` | Run `npx convex init` or set manually |
-## Server Functions
+## Scripts
-TanStack Start provides server functions that allow you to write server-side code that seamlessly integrates with your client components.
+| Script | Description |
+| ---------------- | ---------------------------- |
+| `npm run start` | Convex dev + Vite dev server |
+| `npm run dev` | Vite only (port 3666) |
+| `npm run build` | Production build |
+| `npm run test` | Vitest |
+| `npm run lint` | ESLint |
+| `npm run format` | Prettier |
-```tsx
-import { createServerFn } from '@tanstack/react-start'
+## Project Structure
-const getServerTime = createServerFn({
- method: 'GET',
-}).handler(async () => {
- return new Date().toISOString()
-})
-
-// Use in a component
-function MyComponent() {
- const [time, setTime] = useState('')
-
- useEffect(() => {
- getServerTime().then(setTime)
- }, [])
-
- return Server time: {time}
-}
-```
-
-## API Routes
-
-You can create API routes by using the `server` property in your route definitions:
-
-```tsx
-import { createFileRoute } from '@tanstack/react-router'
-import { json } from '@tanstack/react-start'
-
-export const Route = createFileRoute('/api/hello')({
- server: {
- handlers: {
- GET: () => json({ message: 'Hello, World!' }),
- },
- },
-})
-```
-
-## Data Fetching
-
-There are multiple ways to fetch data in your application. You can use TanStack Query to fetch data from a server. But you can also use the `loader` functionality built into TanStack Router to load the data for a route before it's rendered.
-
-For example:
-
-```tsx
-import { createFileRoute } from '@tanstack/react-router'
-
-export const Route = createFileRoute('/people')({
- loader: async () => {
- const response = await fetch('https://swapi.dev/api/people')
- return response.json()
- },
- component: PeopleComponent,
-})
-
-function PeopleComponent() {
- const data = Route.useLoaderData()
- return (
-
- {data.results.map((person) => (
- {person.name}
- ))}
-
- )
-}
-```
+- `src/routes/` — TanStack Router file-based routing
+- `src/hooks/` — `useSignUp`, `useSignIn`, `useEncryption`
+- `src/lib/crypto.ts` — PGP key generation, encrypt/decrypt
+- `convex/` — Convex backend (userKeys, auth config)
+- `src/integrations/` — Clerk, Convex providers
-Loaders simplify your data fetching logic dramatically. Check out more information in the [Loader documentation](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#loader-parameters).
+## Additional Features
-# Demo files
+**Paraglide i18n:** Messages in `project.inlang/messages`; URLs localized via Paraglide Vite plugin and router `rewrite` hooks.
-Files prefixed with `demo` can be safely deleted. They are there to provide a starting point for you to play around with the features you've installed.
+**shadcn:** Add components with `pnpm dlx shadcn@latest add button`
-# Learn More
+**T3 Env:** Type-safe environment variables. Add variables in `src/env.ts`, use via `import { env } from '@/env'`.
-You can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com).
+## Learn More
-For TanStack Start specific documentation, visit [TanStack Start](https://tanstack.com/start).
+- [TanStack Start](https://tanstack.com/start)
+- [Convex](https://convex.dev)
+- [Clerk](https://clerk.com)
+- [OpenPGP.js](https://openpgpjs.org)
diff --git a/src/env.ts b/src/env.ts
index b4c9f2e..b92d7c3 100644
--- a/src/env.ts
+++ b/src/env.ts
@@ -13,7 +13,8 @@ export const env = createEnv({
clientPrefix: 'VITE_',
client: {
- VITE_APP_TITLE: z.string().min(1).optional(),
+ VITE_CLERK_PUBLISHABLE_KEY: z.string().min(1),
+ VITE_CONVEX_URL: z.string().url(),
},
/**
diff --git a/src/integrations/clerk/provider.tsx b/src/integrations/clerk/provider.tsx
index 3dbbbf1..ed88bcd 100644
--- a/src/integrations/clerk/provider.tsx
+++ b/src/integrations/clerk/provider.tsx
@@ -1,6 +1,7 @@
import { ClerkProvider } from '@clerk/clerk-react'
+import { env } from '@/env'
-const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
+const PUBLISHABLE_KEY = env.VITE_CLERK_PUBLISHABLE_KEY
if (!PUBLISHABLE_KEY) {
throw new Error('Add your Clerk Publishable Key to the .env.local file')
}
diff --git a/src/integrations/convex/provider.tsx b/src/integrations/convex/provider.tsx
index 27d9794..816d438 100644
--- a/src/integrations/convex/provider.tsx
+++ b/src/integrations/convex/provider.tsx
@@ -1,8 +1,9 @@
import { ConvexProviderWithClerk } from 'convex/react-clerk'
import { ConvexQueryClient } from '@convex-dev/react-query'
import { useAuth } from '@clerk/clerk-react'
+import { env } from '@/env'
-const CONVEX_URL = (import.meta as any).env.VITE_CONVEX_URL
+const CONVEX_URL = env.VITE_CONVEX_URL
if (!CONVEX_URL) {
console.error('missing envar CONVEX_URL')
}
From 06176a0eaa70b8a8caa25108b60bf89d56317d5c Mon Sep 17 00:00:00 2001
From: jslyonnais <6282845+jslyonnais@users.noreply.github.com>
Date: Fri, 20 Feb 2026 13:42:11 -0500
Subject: [PATCH 2/2] new linter eslint-plugin-perfectionist
---
.eslintrc.perfectionist.js | 143 +++++
convex/auth.config.ts | 2 +-
convex/schema.ts | 4 +-
convex/userKeys.ts | 15 +-
eslint.config.js | 14 +-
package-lock.json | 529 ++++++------------
package.json | 2 +
src/__tests__/auth.test.ts | 1 +
src/__tests__/crypto.test.ts | 1 +
src/__tests__/useEncryption.test.tsx | 13 +-
src/__tests__/useSignIn.test.tsx | 15 +-
src/__tests__/useSignOut.test.tsx | 3 +-
src/__tests__/useSignUp.test.tsx | 9 +-
src/__tests__/waitFor.test.ts | 5 +-
.../Forms/SignIn/SignInCredsForm.tsx | 14 +-
src/components/Forms/SignIn/SignInMfaForm.tsx | 18 +-
.../Forms/SignUp/SignUpCredsForm.tsx | 24 +-
.../Forms/SignUp/SignUpVerificationForm.tsx | 16 +-
src/components/Layouts/Layout.tsx | 3 +-
src/components/Layouts/LayoutAside.tsx | 5 +-
src/components/Layouts/LayoutContent.tsx | 2 +-
.../Layouts/Navigation/MainNavigation.tsx | 14 +-
.../Layouts/Navigation/NavigationLink.tsx | 8 +-
.../Layouts/Navigation/UserNavigation.tsx | 9 +-
src/components/LocaleSwitcher.tsx | 18 +-
src/components/NotFound.tsx | 2 +-
src/components/ProtectedRoutes.tsx | 3 +-
src/components/ui/avatar.tsx | 5 +-
src/components/ui/breadcrumb.tsx | 19 +-
src/components/ui/button.tsx | 50 +-
src/components/ui/card.tsx | 8 +-
src/components/ui/dropdown-menu.tsx | 19 +-
src/components/ui/field.tsx | 23 +-
src/components/ui/input.tsx | 2 +-
src/components/ui/label.tsx | 1 +
src/components/ui/separator.tsx | 3 +-
src/components/ui/sheet.tsx | 15 +-
src/components/ui/sidebar.tsx | 161 +++---
src/components/ui/skeleton.tsx | 2 +-
src/components/ui/spinner.tsx | 2 +-
src/components/ui/tooltip.tsx | 3 +-
src/env.ts | 30 +-
src/hooks/auth.helpers.ts | 10 +-
src/hooks/useEncryption.ts | 3 +-
src/hooks/useSignIn.ts | 55 +-
src/hooks/useSignOut.ts | 1 +
src/hooks/useSignUp.ts | 43 +-
src/integrations/clerk/provider.tsx | 3 +-
src/integrations/convex/provider.tsx | 5 +-
src/lib/crypto.ts | 14 +-
src/lib/utils.ts | 2 +-
src/lib/waitFor.ts | 4 +-
src/router.tsx | 8 +-
src/routes/auth/signin.tsx | 7 +-
src/routes/auth/signup.tsx | 3 +-
src/routes/index.tsx | 3 +-
src/types/auth.ts | 8 +-
src/types/clerk.ts | 20 +-
src/views/EncryptDemoView.tsx | 16 +-
src/views/SignInView.tsx | 2 +-
src/views/SignUpView.tsx | 2 +-
vite.config.ts | 22 +-
vitest.config.ts | 10 +-
63 files changed, 732 insertions(+), 744 deletions(-)
create mode 100644 .eslintrc.perfectionist.js
diff --git a/.eslintrc.perfectionist.js b/.eslintrc.perfectionist.js
new file mode 100644
index 0000000..35ec307
--- /dev/null
+++ b/.eslintrc.perfectionist.js
@@ -0,0 +1,143 @@
+export default {
+ 'perfectionist/sort-enums': [
+ 'error',
+ {
+ order: 'asc',
+ sortByValue: 'always',
+ type: 'natural',
+ },
+ ],
+ 'perfectionist/sort-exports': [
+ 'error',
+ {
+ order: 'asc',
+ type: 'natural',
+ },
+ ],
+ 'perfectionist/sort-imports': [
+ 'error',
+ {
+ order: 'asc',
+ type: 'natural',
+ customGroups: [
+ {
+ elementNamePattern: ['^react$', '^react-.+'],
+ groupName: 'react',
+ },
+ {
+ elementNamePattern: ['^expo$', '^expo-.+'],
+ groupName: 'expo',
+ },
+ ],
+ groups: [
+ 'side-effect',
+ 'react',
+ 'external',
+ 'internal',
+ 'parent',
+ 'sibling',
+ 'style',
+ ],
+ },
+ ],
+ 'perfectionist/sort-jsx-props': [
+ 'error',
+ {
+ order: 'asc',
+ type: 'natural',
+ customGroups: [
+ {
+ elementNamePattern: ['^key$'],
+ groupName: 'key',
+ },
+ {
+ elementNamePattern: ['^id$', '^testID$'],
+ groupName: 'main-identifier',
+ },
+ {
+ elementNamePattern: ['.*Id$', '.*ID$'],
+ groupName: 'secondary-identifier',
+ },
+ {
+ elementNamePattern: ['^on[A-Z][a-zA-Z]*$'],
+ groupName: 'method',
+ },
+ {
+ elementNamePattern: ['^style$'],
+ groupName: 'style',
+ },
+ ],
+ groups: [
+ 'key',
+ 'main-identifier',
+ 'secondary-identifier',
+ 'unknown',
+ 'method',
+ 'multiline-prop',
+ 'shorthand-prop',
+ 'style',
+ ],
+ },
+ ],
+ 'perfectionist/sort-object-types': [
+ 'error',
+ {
+ order: 'asc',
+ type: 'natural',
+ customGroups: [
+ {
+ elementNamePattern: ['^id$', '^testID$'],
+ groupName: 'main-identifier',
+ },
+ {
+ elementNamePattern: ['.*Id$', '.*ID$'],
+ groupName: 'secondary-identifier',
+ },
+ ],
+ groups: [
+ 'main-identifier',
+ 'secondary-identifier',
+ 'required-property',
+ 'optional-property',
+ 'method',
+ 'multiline-member',
+ ],
+ },
+ ],
+ 'perfectionist/sort-objects': [
+ 'error',
+ {
+ order: 'asc',
+ type: 'natural',
+ customGroups: [
+ {
+ elementNamePattern: ['^id$', '^testID$'],
+ groupName: 'main-identifier',
+ },
+ {
+ elementNamePattern: ['.*Id$', '.*ID$'],
+ groupName: 'secondary-identifier',
+ },
+ ],
+ groups: [
+ 'main-identifier',
+ 'secondary-identifier',
+ 'unknown',
+ 'method',
+ 'multiline-member',
+ ],
+ },
+ ],
+ 'perfectionist/sort-switch-case': [
+ 'error',
+ {
+ type: 'unsorted',
+ },
+ ],
+ 'perfectionist/sort-union-types': [
+ 'error',
+ {
+ type: 'unsorted',
+ },
+ ],
+}
diff --git a/convex/auth.config.ts b/convex/auth.config.ts
index 4b0a8c9..c98967b 100644
--- a/convex/auth.config.ts
+++ b/convex/auth.config.ts
@@ -3,12 +3,12 @@ import type { AuthConfig } from 'convex/server'
export default {
providers: [
{
+ applicationID: 'convex',
// Replace with your own Clerk Issuer URL from your "convex" JWT template
// or with `process.env.CLERK_JWT_ISSUER_DOMAIN`
// and configure CLERK_JWT_ISSUER_DOMAIN on the Convex Dashboard
// See https://docs.convex.dev/auth/clerk#configuring-dev-and-prod-instances
domain: process.env.CLERK_JWT_ISSUER_DOMAIN!,
- applicationID: 'convex',
},
],
} satisfies AuthConfig
diff --git a/convex/schema.ts b/convex/schema.ts
index bbcab02..e8f0d54 100644
--- a/convex/schema.ts
+++ b/convex/schema.ts
@@ -9,9 +9,9 @@ export default defineSchema({
*/
userKeys: defineTable({
userId: v.string(),
+ createdAt: v.number(),
+ encryptedPassphrase: v.string(),
encryptedPrivateKey: v.string(),
publicKey: v.string(),
- encryptedPassphrase: v.string(),
- createdAt: v.number(),
}).index('by_user', ['userId']),
})
diff --git a/convex/userKeys.ts b/convex/userKeys.ts
index a67ad9a..7d7fe88 100644
--- a/convex/userKeys.ts
+++ b/convex/userKeys.ts
@@ -1,4 +1,5 @@
import { v } from 'convex/values'
+
import { mutation, query } from './_generated/server'
/**
@@ -22,11 +23,6 @@ export const getUserKeys = query({
* Create the user's encryption data (PGP keys + encrypted passphrase) on signup
*/
export const createUserKeys = mutation({
- args: {
- encryptedPrivateKey: v.string(),
- publicKey: v.string(),
- encryptedPassphrase: v.string(),
- },
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity()
if (!identity) throw new Error('Unauthorized')
@@ -42,10 +38,15 @@ export const createUserKeys = mutation({
return await ctx.db.insert('userKeys', {
userId: identity.subject,
+ createdAt: Date.now(),
+ encryptedPassphrase: args.encryptedPassphrase,
encryptedPrivateKey: args.encryptedPrivateKey,
publicKey: args.publicKey,
- encryptedPassphrase: args.encryptedPassphrase,
- createdAt: Date.now(),
})
},
+ args: {
+ encryptedPassphrase: v.string(),
+ encryptedPrivateKey: v.string(),
+ publicKey: v.string(),
+ },
})
diff --git a/eslint.config.js b/eslint.config.js
index c595c5d..75162c1 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -1,9 +1,20 @@
// @ts-check
-
import { tanstackConfig } from '@tanstack/eslint-config'
+import perfectionistPlugin from 'eslint-plugin-perfectionist'
+import perfectionistRules from './.eslintrc.perfectionist.js'
export default [
...tanstackConfig,
+ {
+ plugins: {
+ perfectionist: perfectionistPlugin,
+ },
+ rules: {
+ ...perfectionistRules,
+ // Disable import/order - it conflicts with perfectionist/sort-imports (causes circular fixes)
+ 'import/order': 'off',
+ },
+ },
{
ignores: [
'convex/_generated/**',
@@ -14,6 +25,7 @@ export default [
'src/routes/__root.tsx',
'src/routes/demo/**',
'.output/**',
+ 'eslint.config.js',
],
},
]
diff --git a/package-lock.json b/package-lock.json
index 369b015..9b41ce7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -49,6 +49,8 @@
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.4",
+ "eslint": "^10.0.1",
+ "eslint-plugin-perfectionist": "^5.6.0",
"husky": "^9.1.7",
"jotai-babel": "^0.1.0",
"jsdom": "^28.1.0",
@@ -1122,152 +1124,68 @@
}
},
"node_modules/@eslint/config-array": {
- "version": "0.21.1",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
- "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
+ "version": "0.23.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.2.tgz",
+ "integrity": "sha512-YF+fE6LV4v5MGWRGj7G404/OZzGNepVF8fxk7jqmqo3lrza7a0uUcDnROGRBG1WFC1omYUS/Wp1f42i0M+3Q3A==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"dependencies": {
- "@eslint/object-schema": "^2.1.7",
+ "@eslint/object-schema": "^3.0.2",
"debug": "^4.3.1",
- "minimatch": "^3.1.2"
+ "minimatch": "^10.2.1"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@eslint/config-helpers": {
- "version": "0.4.2",
- "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
- "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.2.tgz",
+ "integrity": "sha512-a5MxrdDXEvqnIq+LisyCX6tQMPF/dSJpCfBgBauY+pNZ28yCtSsTvyTYrMhaI+LK26bVyCJfJkT0u8KIj2i1dQ==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"dependencies": {
- "@eslint/core": "^0.17.0"
+ "@eslint/core": "^1.1.0"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@eslint/core": {
- "version": "0.17.0",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
- "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.0.tgz",
+ "integrity": "sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"dependencies": {
"@types/json-schema": "^7.0.15"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/eslintrc": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
- "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "ajv": "^6.12.4",
- "debug": "^4.3.2",
- "espree": "^10.0.1",
- "globals": "^14.0.0",
- "ignore": "^5.2.0",
- "import-fresh": "^3.2.1",
- "js-yaml": "^4.1.1",
- "minimatch": "^3.1.2",
- "strip-json-comments": "^3.1.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/ajv": {
- "version": "6.12.6",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
- "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "fast-deep-equal": "^3.1.1",
- "fast-json-stable-stringify": "^2.0.0",
- "json-schema-traverse": "^0.4.1",
- "uri-js": "^4.2.2"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/epoberezkin"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/globals": {
- "version": "14.0.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
- "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
- "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
- "dev": true,
- "license": "MIT",
- "peer": true
- },
- "node_modules/@eslint/js": {
- "version": "9.39.3",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.3.tgz",
- "integrity": "sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://eslint.org/donate"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@eslint/object-schema": {
- "version": "2.1.7",
- "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
- "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.2.tgz",
+ "integrity": "sha512-HOy56KJt48Bx8KmJ+XGQNSUMT/6dZee/M54XyUyuvTvPXJmsERRvBchsUVx1UMe1WwIH49XLAczNC7V2INsuUw==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@eslint/plugin-kit": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
- "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.0.tgz",
+ "integrity": "sha512-bIZEUzOI1jkhviX2cp5vNyXQc6olzb2ohewQubuYlMXZ2Q/XjBO0x0XhGPvc9fjSIiUN0vw+0hq53BJ4eQSJKQ==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"dependencies": {
- "@eslint/core": "^0.17.0",
+ "@eslint/core": "^1.1.0",
"levn": "^0.4.1"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@exodus/bytes": {
@@ -1347,7 +1265,6 @@
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"engines": {
"node": ">=18.18.0"
}
@@ -1358,7 +1275,6 @@
"integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"dependencies": {
"@humanfs/core": "^0.19.1",
"@humanwhocodes/retry": "^0.4.0"
@@ -1373,7 +1289,6 @@
"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"engines": {
"node": ">=12.22"
},
@@ -1388,7 +1303,6 @@
"integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"engines": {
"node": ">=18.18"
},
@@ -4300,6 +4214,56 @@
}
}
},
+ "node_modules/@tanstack/eslint-config/node_modules/eslint-plugin-import-x": {
+ "version": "4.16.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.16.1.tgz",
+ "integrity": "sha512-vPZZsiOKaBAIATpFE2uMI4w5IRwdv/FpQ+qZZMR4E+PeOcM4OeoEbqxRMnywdxP19TyB/3h6QBB0EWon7letSQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "^8.35.0",
+ "comment-parser": "^1.4.1",
+ "debug": "^4.4.1",
+ "eslint-import-context": "^0.1.9",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.3 || ^10.0.1",
+ "semver": "^7.7.2",
+ "stable-hash-x": "^0.2.0",
+ "unrs-resolver": "^1.9.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-plugin-import-x"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/utils": "^8.0.0",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "eslint-import-resolver-node": "*"
+ },
+ "peerDependenciesMeta": {
+ "@typescript-eslint/utils": {
+ "optional": true
+ },
+ "eslint-import-resolver-node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@tanstack/eslint-config/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/@tanstack/history": {
"version": "1.154.14",
"resolved": "https://registry.npmjs.org/@tanstack/history/-/history-1.154.14.tgz",
@@ -5031,6 +4995,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/esrecurse": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
+ "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
@@ -5042,8 +5013,7 @@
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
"dev": true,
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/@types/lodash": {
"version": "4.17.23",
@@ -5770,9 +5740,9 @@
}
},
"node_modules/acorn": {
- "version": "8.15.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
- "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "version": "8.16.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
+ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
@@ -6038,17 +6008,6 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
- "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",
- "peer": true,
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/caniuse-lite": {
"version": "1.0.30001770",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001770.tgz",
@@ -6189,20 +6148,6 @@
"color-string": "^1.6.0"
}
},
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
@@ -6359,7 +6304,6 @@
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -6559,8 +6503,7 @@
"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",
- "peer": true
+ "license": "MIT"
},
"node_modules/dequal": {
"version": "2.0.3",
@@ -6764,7 +6707,6 @@
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=10"
},
@@ -6773,34 +6715,30 @@
}
},
"node_modules/eslint": {
- "version": "9.39.3",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.3.tgz",
- "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==",
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.0.1.tgz",
+ "integrity": "sha512-20MV9SUdeN6Jd84xESsKhRly+/vxI+hwvpBMA93s+9dAcjdCuCojn4IqUGS3lvVaqjVYGYHSRMCpeFtF2rQYxQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
- "@eslint-community/regexpp": "^4.12.1",
- "@eslint/config-array": "^0.21.1",
- "@eslint/config-helpers": "^0.4.2",
- "@eslint/core": "^0.17.0",
- "@eslint/eslintrc": "^3.3.1",
- "@eslint/js": "9.39.3",
- "@eslint/plugin-kit": "^0.4.1",
+ "@eslint-community/regexpp": "^4.12.2",
+ "@eslint/config-array": "^0.23.2",
+ "@eslint/config-helpers": "^0.5.2",
+ "@eslint/core": "^1.1.0",
+ "@eslint/plugin-kit": "^0.6.0",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",
"@types/estree": "^1.0.6",
"ajv": "^6.12.4",
- "chalk": "^4.0.0",
"cross-spawn": "^7.0.6",
"debug": "^4.3.2",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^8.4.0",
- "eslint-visitor-keys": "^4.2.1",
- "espree": "^10.4.0",
- "esquery": "^1.5.0",
+ "eslint-scope": "^9.1.1",
+ "eslint-visitor-keys": "^5.0.1",
+ "espree": "^11.1.1",
+ "esquery": "^1.7.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
"file-entry-cache": "^8.0.0",
@@ -6810,8 +6748,7 @@
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
- "lodash.merge": "^4.6.2",
- "minimatch": "^3.1.2",
+ "minimatch": "^10.2.1",
"natural-compare": "^1.4.0",
"optionator": "^0.9.3"
},
@@ -6819,7 +6756,7 @@
"eslint": "bin/eslint.js"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://eslint.org/donate"
@@ -6909,56 +6846,6 @@
"eslint": ">=8"
}
},
- "node_modules/eslint-plugin-import-x": {
- "version": "4.16.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.16.1.tgz",
- "integrity": "sha512-vPZZsiOKaBAIATpFE2uMI4w5IRwdv/FpQ+qZZMR4E+PeOcM4OeoEbqxRMnywdxP19TyB/3h6QBB0EWon7letSQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "^8.35.0",
- "comment-parser": "^1.4.1",
- "debug": "^4.4.1",
- "eslint-import-context": "^0.1.9",
- "is-glob": "^4.0.3",
- "minimatch": "^9.0.3 || ^10.0.1",
- "semver": "^7.7.2",
- "stable-hash-x": "^0.2.0",
- "unrs-resolver": "^1.9.2"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint-plugin-import-x"
- },
- "peerDependencies": {
- "@typescript-eslint/utils": "^8.0.0",
- "eslint": "^8.57.0 || ^9.0.0",
- "eslint-import-resolver-node": "*"
- },
- "peerDependenciesMeta": {
- "@typescript-eslint/utils": {
- "optional": true
- },
- "eslint-import-resolver-node": {
- "optional": true
- }
- }
- },
- "node_modules/eslint-plugin-import-x/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/eslint-plugin-n": {
"version": "17.24.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.24.0.tgz",
@@ -7012,18 +6899,37 @@
"node": ">=10"
}
},
+ "node_modules/eslint-plugin-perfectionist": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-5.6.0.tgz",
+ "integrity": "sha512-pxrLrfRp5wl1Vol1fAEa/G5yTXxefTPJjz07qC7a8iWFXcOZNuWBItMQ2OtTzfQIvMq6bMyYcrzc3Wz++na55Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/utils": "^8.56.0",
+ "natural-orderby": "^5.0.0"
+ },
+ "engines": {
+ "node": "^20.0.0 || >=22.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^8.45.0 || ^9.0.0 || ^10.0.0"
+ }
+ },
"node_modules/eslint-scope": {
- "version": "8.4.0",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
- "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "version": "9.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.1.tgz",
+ "integrity": "sha512-GaUN0sWim5qc8KVErfPBWmc31LEsOkrUJbvJZV+xuL3u2phMUK4HIvXlWAakfC8W4nzlK+chPEAkYOYb5ZScIw==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
+ "@types/esrecurse": "^4.3.1",
+ "@types/estree": "^1.0.8",
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://opencollective.com/eslint"
@@ -7048,7 +6954,6 @@
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -7060,39 +6965,35 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
- "node_modules/eslint/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/eslint/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
"dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "color-convert": "^2.0.1"
- },
+ "license": "Apache-2.0",
"engines": {
- "node": ">=8"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ "url": "https://opencollective.com/eslint"
}
},
- "node_modules/eslint/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/eslint/node_modules/espree": {
+ "version": "11.1.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-11.1.1.tgz",
+ "integrity": "sha512-AVHPqQoZYc+RUM4/3Ly5udlZY/U4LS8pIG05jEjWM2lQMU/oaZ7qshzAl2YP1tfNmXfftH3ohurfwNAug+MnsQ==",
"dev": true,
- "license": "MIT",
- "peer": true,
+ "license": "BSD-2-Clause",
"dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
+ "acorn": "^8.16.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^5.0.1"
},
"engines": {
- "node": ">=10"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
+ "url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint/node_modules/glob-parent": {
@@ -7101,7 +7002,6 @@
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"dev": true,
"license": "ISC",
- "peer": true,
"dependencies": {
"is-glob": "^4.0.3"
},
@@ -7114,8 +7014,7 @@
"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",
- "peer": true
+ "license": "MIT"
},
"node_modules/espree": {
"version": "10.4.0",
@@ -7200,7 +7099,6 @@
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true,
"license": "BSD-2-Clause",
- "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -7226,24 +7124,21 @@
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true,
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true,
- "license": "MIT",
- "peer": 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",
- "peer": true
+ "license": "MIT"
},
"node_modules/fdir": {
"version": "6.5.0",
@@ -7268,7 +7163,6 @@
"integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"flat-cache": "^4.0.0"
},
@@ -7294,7 +7188,6 @@
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"locate-path": "^6.0.0",
"path-exists": "^4.0.0"
@@ -7312,7 +7205,6 @@
"integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"flatted": "^3.2.9",
"keyv": "^4.5.4"
@@ -7326,8 +7218,7 @@
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
"dev": true,
- "license": "ISC",
- "peer": true
+ "license": "ISC"
},
"node_modules/fsevents": {
"version": "2.3.3",
@@ -7474,17 +7365,6 @@
}
}
},
- "node_modules/has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/highlight.js": {
"version": "11.11.1",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
@@ -7626,31 +7506,12 @@
"integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==",
"license": "MIT"
},
- "node_modules/import-fresh": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
- "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "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",
- "peer": true,
"engines": {
"node": ">=0.8.19"
}
@@ -7724,8 +7585,7 @@
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true,
- "license": "ISC",
- "peer": true
+ "license": "ISC"
},
"node_modules/javascript-stringify": {
"version": "2.1.0",
@@ -7970,16 +7830,14 @@
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
"dev": true,
- "license": "MIT",
- "peer": 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",
- "peer": true
+ "license": "MIT"
},
"node_modules/json5": {
"version": "2.2.3",
@@ -8014,7 +7872,6 @@
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"json-buffer": "3.0.1"
}
@@ -8046,7 +7903,6 @@
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"prelude-ls": "^1.2.1",
"type-check": "~0.4.0"
@@ -8310,7 +8166,6 @@
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"p-locate": "^5.0.0"
},
@@ -8327,14 +8182,6 @@
"integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==",
"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",
- "peer": true
- },
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -8442,6 +8289,16 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/natural-orderby": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/natural-orderby/-/natural-orderby-5.0.0.tgz",
+ "integrity": "sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/nf3": {
"version": "0.3.10",
"resolved": "https://registry.npmjs.org/nf3/-/nf3-0.3.10.tgz",
@@ -8679,7 +8536,6 @@
"integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"deep-is": "^0.1.3",
"fast-levenshtein": "^2.0.6",
@@ -8698,7 +8554,6 @@
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"yocto-queue": "^0.1.0"
},
@@ -8715,7 +8570,6 @@
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"p-limit": "^3.0.2"
},
@@ -8726,20 +8580,6 @@
"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",
- "peer": true,
- "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",
@@ -8795,7 +8635,6 @@
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=8"
}
@@ -8806,7 +8645,6 @@
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=8"
}
@@ -8869,7 +8707,6 @@
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">= 0.8.0"
}
@@ -9228,17 +9065,6 @@
"node": ">=0.10.0"
}
},
- "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",
- "peer": true,
- "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",
@@ -9396,7 +9222,6 @@
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"shebang-regex": "^3.0.0"
},
@@ -9410,7 +9235,6 @@
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=8"
}
@@ -9520,34 +9344,6 @@
"integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
"license": "MIT"
},
- "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",
- "peer": true,
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "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",
- "peer": true,
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/swr": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/swr/-/swr-2.3.4.tgz",
@@ -9812,7 +9608,6 @@
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"prelude-ls": "^1.2.1"
},
@@ -9987,7 +9782,6 @@
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"license": "BSD-2-Clause",
- "peer": true,
"dependencies": {
"punycode": "^2.1.0"
}
@@ -10404,7 +10198,6 @@
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"license": "ISC",
- "peer": true,
"dependencies": {
"isexe": "^2.0.0"
},
@@ -10438,7 +10231,6 @@
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -10508,7 +10300,6 @@
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=10"
},
diff --git a/package.json b/package.json
index 974bdf6..3e52f04 100644
--- a/package.json
+++ b/package.json
@@ -58,6 +58,8 @@
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.4",
+ "eslint": "^10.0.1",
+ "eslint-plugin-perfectionist": "^5.6.0",
"husky": "^9.1.7",
"jotai-babel": "^0.1.0",
"jsdom": "^28.1.0",
diff --git a/src/__tests__/auth.test.ts b/src/__tests__/auth.test.ts
index b12c86a..763aabf 100644
--- a/src/__tests__/auth.test.ts
+++ b/src/__tests__/auth.test.ts
@@ -1,4 +1,5 @@
import { describe, expect, it, vi } from 'vitest'
+
import { createSession } from '@/hooks/auth.helpers'
describe('Authentication Helpers', () => {
diff --git a/src/__tests__/crypto.test.ts b/src/__tests__/crypto.test.ts
index 0b3dcef..0fe429f 100644
--- a/src/__tests__/crypto.test.ts
+++ b/src/__tests__/crypto.test.ts
@@ -3,6 +3,7 @@
* OpenPGP has Uint8Array/instanceof issues in jsdom - run in Node
*/
import { describe, expect, it } from 'vitest'
+
import {
createPassphrase,
decryptPassphrase,
diff --git a/src/__tests__/useEncryption.test.tsx b/src/__tests__/useEncryption.test.tsx
index a4c0c55..1503f50 100644
--- a/src/__tests__/useEncryption.test.tsx
+++ b/src/__tests__/useEncryption.test.tsx
@@ -1,15 +1,16 @@
-import { beforeEach, describe, expect, it, vi } from 'vitest'
import { renderHook } from '@testing-library/react'
+import { beforeEach, describe, expect, it, vi } from 'vitest'
+
import { useEncryption } from '@/hooks/useEncryption'
const mockEncryptWithPublicKey = vi.fn()
const mockDecryptWithPrivateKey = vi.fn()
vi.mock('@/lib/crypto', () => ({
- encryptWithPublicKey: (...args: Array) =>
- mockEncryptWithPublicKey(...args),
decryptWithPrivateKey: (...args: Array) =>
mockDecryptWithPrivateKey(...args),
+ encryptWithPublicKey: (...args: Array) =>
+ mockEncryptWithPublicKey(...args),
}))
const mockUserKeys = vi.fn()
@@ -25,8 +26,8 @@ describe('useEncryption', () => {
describe('encryptData / decryptData', () => {
it('encrypts data when userKeys are available', async () => {
mockUserKeys.mockReturnValue({
- publicKey: 'armored-pub-key',
encryptedPrivateKey: 'armored-priv-key',
+ publicKey: 'armored-pub-key',
})
mockEncryptWithPublicKey.mockResolvedValue('-----BEGIN PGP MESSAGE-----')
@@ -42,8 +43,8 @@ describe('useEncryption', () => {
it('decrypts data when userKeys and passphrase are available', async () => {
mockUserKeys.mockReturnValue({
- publicKey: 'armored-pub-key',
encryptedPrivateKey: 'armored-priv-key',
+ publicKey: 'armored-pub-key',
})
mockDecryptWithPrivateKey.mockResolvedValue('decrypted plaintext')
@@ -83,8 +84,8 @@ describe('useEncryption', () => {
it('throws when decryptData called with empty passphrase', async () => {
mockUserKeys.mockReturnValue({
- publicKey: 'pub',
encryptedPrivateKey: 'priv',
+ publicKey: 'pub',
})
const { result } = renderHook(() => useEncryption())
diff --git a/src/__tests__/useSignIn.test.tsx b/src/__tests__/useSignIn.test.tsx
index dc2f972..aabb2dc 100644
--- a/src/__tests__/useSignIn.test.tsx
+++ b/src/__tests__/useSignIn.test.tsx
@@ -1,5 +1,6 @@
-import { beforeEach, describe, expect, it, vi } from 'vitest'
import { act, renderHook } from '@testing-library/react'
+import { beforeEach, describe, expect, it, vi } from 'vitest'
+
import { useSignIn } from '@/hooks/useSignIn'
const mockSignInCreate = vi.fn()
@@ -15,12 +16,12 @@ vi.mock('@clerk/clerk-react', () => ({
useClerk: () => ({ user: { id: 'user_1' } }),
useSignIn: () => ({
isLoaded: true,
+ setActive: mockSetActive,
signIn: {
+ attemptSecondFactor: mockAttemptSecondFactor,
create: mockSignInCreate,
prepareSecondFactor: mockPrepareSecondFactor,
- attemptSecondFactor: mockAttemptSecondFactor,
},
- setActive: mockSetActive,
}),
}))
@@ -61,8 +62,8 @@ describe('useSignIn', () => {
it('handles sign-in success and redirects', async () => {
mockSignInCreate.mockResolvedValue({
- status: 'complete',
createdSessionId: 'sess_123',
+ status: 'complete',
})
mockSetActive.mockResolvedValue(undefined)
mockUserKeys.mockReturnValue({
@@ -83,9 +84,9 @@ describe('useSignIn', () => {
})
expect(mockSignInCreate).toHaveBeenCalledWith({
- strategy: 'password',
identifier: 'test@example.com',
password: 'password',
+ strategy: 'password',
})
expect(mockSetActive).toHaveBeenCalled()
expect(mockSetPassphrase).toHaveBeenCalledWith('decrypted-passphrase')
@@ -153,8 +154,8 @@ describe('useSignIn', () => {
it('handleMfaVerify verifies code, decrypts passphrase and redirects', async () => {
mockAttemptSecondFactor.mockResolvedValue({
- status: 'complete',
createdSessionId: 'sess_mfa',
+ status: 'complete',
})
mockSetActive.mockResolvedValue(undefined)
mockUserKeys.mockReturnValue({
@@ -175,8 +176,8 @@ describe('useSignIn', () => {
})
expect(mockAttemptSecondFactor).toHaveBeenCalledWith({
- strategy: 'email_code',
code: '654321',
+ strategy: 'email_code',
})
expect(mockSetActive).toHaveBeenCalled()
expect(mockSetPassphrase).toHaveBeenCalledWith('decrypted-passphrase')
diff --git a/src/__tests__/useSignOut.test.tsx b/src/__tests__/useSignOut.test.tsx
index 42e597d..8105402 100644
--- a/src/__tests__/useSignOut.test.tsx
+++ b/src/__tests__/useSignOut.test.tsx
@@ -1,6 +1,7 @@
-import { beforeEach, describe, expect, it, vi } from 'vitest'
import { act, renderHook } from '@testing-library/react'
import { RESET } from 'jotai/utils'
+import { beforeEach, describe, expect, it, vi } from 'vitest'
+
import { useSignOut } from '@/hooks/useSignOut'
const mockSignOut = vi.fn().mockResolvedValue(undefined)
diff --git a/src/__tests__/useSignUp.test.tsx b/src/__tests__/useSignUp.test.tsx
index d5220f0..aeac2a4 100644
--- a/src/__tests__/useSignUp.test.tsx
+++ b/src/__tests__/useSignUp.test.tsx
@@ -1,5 +1,6 @@
-import { beforeEach, describe, expect, it, vi } from 'vitest'
import { act, renderHook } from '@testing-library/react'
+import { beforeEach, describe, expect, it, vi } from 'vitest'
+
import { useSignUp } from '@/hooks/useSignUp'
const mockSetActive = vi.fn()
@@ -18,12 +19,12 @@ vi.mock('@clerk/clerk-react', () => ({
useClerk: () => ({ user: { id: 'user_1' } }),
useSignUp: () => ({
isLoaded: true,
+ setActive: mockSetActive,
signUp: {
+ attemptEmailAddressVerification: mockAttemptEmailVerification,
create: mockSignUpCreate,
prepareEmailAddressVerification: mockPrepareEmailVerification,
- attemptEmailAddressVerification: mockAttemptEmailVerification,
},
- setActive: mockSetActive,
}),
}))
@@ -96,8 +97,8 @@ describe('useSignUp', () => {
it('verifies email and creates keys on handleVerification', async () => {
mockAttemptEmailVerification.mockResolvedValue({
- status: 'complete',
createdSessionId: 'sess_456',
+ status: 'complete',
})
mockSetActive.mockResolvedValue(undefined)
diff --git a/src/__tests__/waitFor.test.ts b/src/__tests__/waitFor.test.ts
index 5bc1cf3..1788106 100644
--- a/src/__tests__/waitFor.test.ts
+++ b/src/__tests__/waitFor.test.ts
@@ -1,4 +1,5 @@
import { describe, expect, it, vi } from 'vitest'
+
import { waitForValue } from '@/lib/waitFor'
describe('waitForValue', () => {
@@ -26,14 +27,14 @@ describe('waitForValue', () => {
.fn()
.mockReturnValueOnce(undefined)
.mockReturnValueOnce('done')
- await waitForValue(getValue, { pollInterval: 5, onRetry })
+ await waitForValue(getValue, { onRetry, pollInterval: 5 })
expect(onRetry).toHaveBeenCalledTimes(1)
})
it('throws after timeout when value never appears', async () => {
const getValue = vi.fn().mockReturnValue(undefined)
await expect(
- waitForValue(getValue, { timeout: 50, pollInterval: 10 }),
+ waitForValue(getValue, { pollInterval: 10, timeout: 50 }),
).rejects.toThrow(/Timeout waiting for value/)
})
})
diff --git a/src/components/Forms/SignIn/SignInCredsForm.tsx b/src/components/Forms/SignIn/SignInCredsForm.tsx
index e878a1e..3e00174 100644
--- a/src/components/Forms/SignIn/SignInCredsForm.tsx
+++ b/src/components/Forms/SignIn/SignInCredsForm.tsx
@@ -1,4 +1,3 @@
-import type { useSignIn } from '@/hooks/useSignIn'
import { Button } from '@/components/ui/button'
import {
Field,
@@ -8,6 +7,7 @@ import {
FieldLabel,
} from '@/components/ui/field'
import { Input } from '@/components/ui/input'
+import type { useSignIn } from '@/hooks/useSignIn'
import { m } from '@/paraglide/messages'
import { RoutesPath } from '@/types/routes'
@@ -15,12 +15,12 @@ type SignInCredsFormProps = ReturnType
export const SignInCredsForm = ({
email,
- setEmail,
+ error,
+ handleSignIn,
isLoading,
password,
+ setEmail,
setPassword,
- handleSignIn,
- error,
}: SignInCredsFormProps) => {
return (