diff --git a/.github/workflows/lint.yml b/.github/workflows/ci.yml
similarity index 79%
rename from .github/workflows/lint.yml
rename to .github/workflows/ci.yml
index 06352ab..0eb62c6 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/ci.yml
@@ -1,4 +1,4 @@
-name: Lint
+name: CI
on:
push:
@@ -9,40 +9,39 @@ on:
- main
jobs:
- lint:
+ ci:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20, 22, 24]
steps:
- # Checkout the repository
- name: Checkout code
uses: actions/checkout@v3
- # Set up Node.js
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- # Install pnpm
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10.18.0
- # Install dependencies
- name: Install dependencies
run: pnpm install
- # Run lint
- name: Run lint
run: pnpm lint
- # Run prettier
- name: Run prettier
run: pnpm prettier:check
- # Run type checking
- name: Run type checking
run: pnpm tsc --noEmit
+
+ - name: Run build
+ run: pnpm build
+
+ - name: Run tests
+ run: pnpm test:all
diff --git a/app/app.vue b/app/app.vue
index ccb52d1..5acf3c1 100644
--- a/app/app.vue
+++ b/app/app.vue
@@ -1,5 +1,7 @@
-
-
Hello World!
-
+
+
+
+
+
diff --git a/app/assets/css/main.css b/app/assets/css/main.css
index d4b5078..c917fb7 100644
--- a/app/assets/css/main.css
+++ b/app/assets/css/main.css
@@ -1 +1,6 @@
@import 'tailwindcss';
+@import '@nuxt/ui';
+
+* {
+ font-family: Poppins, sans-serif;
+}
diff --git a/app/components/Header/index.vue b/app/components/Header/index.vue
new file mode 100644
index 0000000..599e1bc
--- /dev/null
+++ b/app/components/Header/index.vue
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+ Lystra
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/layouts/default.vue b/app/layouts/default.vue
new file mode 100644
index 0000000..781a35d
--- /dev/null
+++ b/app/layouts/default.vue
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/app/pages/index.vue b/app/pages/index.vue
new file mode 100644
index 0000000..7b8b46c
--- /dev/null
+++ b/app/pages/index.vue
@@ -0,0 +1,3 @@
+
+
+
diff --git a/nuxt.config.ts b/nuxt.config.ts
index d0de77a..fd3c009 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -7,15 +7,25 @@ export default defineNuxtConfig({
modules: [
'@nuxt/ui',
+ '@nuxt/fonts',
'@nuxt/test-utils',
+ '@nuxt/test-utils/module',
'@nuxt/image',
'@nuxt/eslint',
'@pinia/nuxt',
- '@clerk/nuxt',
+ ...(process.env.NODE_ENV !== 'test' ? ['@clerk/nuxt'] : []),
],
css: ['~/assets/css/main.css'],
vite: {
plugins: [tailwindcss()],
},
+
+ app: {
+ head: {
+ htmlAttrs: {
+ style: 'background-color: #0E172B;',
+ },
+ },
+ },
});
diff --git a/package.json b/package.json
index a2ddc3f..a9956f3 100644
--- a/package.json
+++ b/package.json
@@ -12,13 +12,18 @@
"prettier:write": "prettier --write .",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
+ "test:watch": "vitest --watch",
+ "test:e2e": "vitest --project e2e",
+ "test:unit": "vitest --project nuxt",
+ "test:all": "vitest run",
"prepare": "husky"
},
"dependencies": {
"@clerk/nuxt": "^1.9.3",
+ "@clerk/themes": "^2.4.24",
"@nuxt/eslint": "1.9.0",
+ "@nuxt/fonts": "0.11.4",
"@nuxt/image": "1.11.0",
- "@nuxt/test-utils": "3.19.2",
"@nuxt/ui": "4.0.0",
"@pinia/nuxt": "0.11.2",
"@tailwindcss/vite": "^4.1.14",
@@ -33,9 +38,14 @@
"devDependencies": {
"@commitlint/cli": "^20.1.0",
"@commitlint/config-conventional": "^20.0.0",
+ "@nuxt/test-utils": "3.19.2",
+ "@vue/test-utils": "^2.4.6",
+ "happy-dom": "^19.0.2",
"husky": "^9.1.7",
"lint-staged": "^16.2.3",
- "prettier": "3.6.2"
+ "playwright-core": "^1.55.1",
+ "prettier": "3.6.2",
+ "vitest": "^3.2.4"
},
"lint-staged": {
"*.{js,ts,vue}": [
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1f5c841..86571b4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -11,15 +11,18 @@ importers:
'@clerk/nuxt':
specifier: ^1.9.3
version: 1.9.3(magicast@0.3.5)(react@19.1.1)(vue@3.5.22(typescript@5.9.3))
+ '@clerk/themes':
+ specifier: ^2.4.24
+ version: 2.4.24
'@nuxt/eslint':
specifier: 1.9.0
version: 1.9.0(@typescript-eslint/utils@8.45.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(@vue/compiler-sfc@3.5.22)(eslint@9.37.0(jiti@2.6.1))(magicast@0.3.5)(typescript@5.9.3)(vite@7.1.9(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1))
+ '@nuxt/fonts':
+ specifier: 0.11.4
+ version: 0.11.4(db0@0.3.4)(ioredis@5.8.0)(magicast@0.3.5)(vite@7.1.9(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1))
'@nuxt/image':
specifier: 1.11.0
version: 1.11.0(db0@0.3.4)(ioredis@5.8.0)(magicast@0.3.5)
- '@nuxt/test-utils':
- specifier: 3.19.2
- version: 3.19.2(magicast@0.3.5)(typescript@5.9.3)
'@nuxt/ui':
specifier: 4.0.0
version: 4.0.0(@babel/parser@7.28.4)(change-case@5.4.4)(db0@0.3.4)(embla-carousel@8.6.0)(ioredis@5.8.0)(magicast@0.3.5)(react@19.1.1)(typescript@5.9.3)(vite@7.1.9(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1))(vue-router@4.5.1(vue@3.5.22(typescript@5.9.3)))(vue@3.5.22(typescript@5.9.3))(zod@4.1.11)
@@ -57,15 +60,30 @@ importers:
'@commitlint/config-conventional':
specifier: ^20.0.0
version: 20.0.0
+ '@nuxt/test-utils':
+ specifier: 3.19.2
+ version: 3.19.2(@vue/test-utils@2.4.6)(happy-dom@19.0.2)(magicast@0.3.5)(playwright-core@1.55.1)(typescript@5.9.3)(vitest@3.2.4(@types/node@24.6.2)(happy-dom@19.0.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1))
+ '@vue/test-utils':
+ specifier: ^2.4.6
+ version: 2.4.6
+ happy-dom:
+ specifier: ^19.0.2
+ version: 19.0.2
husky:
specifier: ^9.1.7
version: 9.1.7
lint-staged:
specifier: ^16.2.3
version: 16.2.3
+ playwright-core:
+ specifier: ^1.55.1
+ version: 1.55.1
prettier:
specifier: 3.6.2
version: 3.6.2
+ vitest:
+ specifier: ^3.2.4
+ version: 3.2.4(@types/node@24.6.2)(happy-dom@19.0.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1)
packages:
@@ -262,6 +280,10 @@ packages:
react-dom:
optional: true
+ '@clerk/themes@2.4.24':
+ resolution: {integrity: sha512-/uedLbpimSHcpkWTPmyqNiCe6otHtGDCNU0Ki++miX/sTN/hJFVU/3UpfNHgXGWWOe7Ql6fixE1pzxsdlrMlWw==}
+ engines: {node: '>=18.17.0'}
+
'@clerk/types@4.91.0':
resolution: {integrity: sha512-wvumOakC1tVDeCjGE1AqhgorMiK0PEDx+knnjxVnRP/pB8eY+JuWvNoBPlqbj2jbQAmcoLN17SigUNdvO5PDLw==}
engines: {node: '>=18.17.0'}
@@ -855,6 +877,9 @@ packages:
'@nuxtjs/color-mode@3.5.2':
resolution: {integrity: sha512-cC6RfgZh3guHBMLLjrBB2Uti5eUoGM9KyauOaYS9ETmxNWBMTvpgjvSiSJp1OFljIXPIqVTJ3xtJpSNZiO3ZaA==}
+ '@one-ini/wasm@0.1.1':
+ resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
+
'@opentelemetry/api@1.9.0':
resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
engines: {node: '>=8.0.0'}
@@ -1570,15 +1595,24 @@ packages:
'@tybys/wasm-util@0.10.1':
resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
+ '@types/chai@5.2.2':
+ resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==}
+
'@types/conventional-commits-parser@5.0.1':
resolution: {integrity: sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==}
+ '@types/deep-eql@4.0.2':
+ resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
+
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+ '@types/node@20.19.19':
+ resolution: {integrity: sha512-pb1Uqj5WJP7wrcbLU7Ru4QtA0+3kAXrkutGiD26wUKzSMgNNaPARTUDQmElUXp64kh3cWdou3Q0C7qwwxqSFmg==}
+
'@types/node@24.6.2':
resolution: {integrity: sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang==}
@@ -1595,6 +1629,9 @@ packages:
'@types/web-bluetooth@0.0.21':
resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
+ '@types/whatwg-mimetype@3.0.2':
+ resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==}
+
'@typescript-eslint/eslint-plugin@8.45.0':
resolution: {integrity: sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -1777,6 +1814,35 @@ packages:
vite: ^5.0.0 || ^6.0.0 || ^7.0.0
vue: ^3.2.25
+ '@vitest/expect@3.2.4':
+ resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==}
+
+ '@vitest/mocker@3.2.4':
+ resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==}
+ peerDependencies:
+ msw: ^2.4.9
+ vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0
+ peerDependenciesMeta:
+ msw:
+ optional: true
+ vite:
+ optional: true
+
+ '@vitest/pretty-format@3.2.4':
+ resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==}
+
+ '@vitest/runner@3.2.4':
+ resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==}
+
+ '@vitest/snapshot@3.2.4':
+ resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==}
+
+ '@vitest/spy@3.2.4':
+ resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==}
+
+ '@vitest/utils@3.2.4':
+ resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==}
+
'@volar/language-core@2.4.23':
resolution: {integrity: sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==}
@@ -1862,6 +1928,9 @@ packages:
'@vue/shared@3.5.22':
resolution: {integrity: sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==}
+ '@vue/test-utils@2.4.6':
+ resolution: {integrity: sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==}
+
'@vueuse/core@10.11.1':
resolution: {integrity: sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==}
@@ -1939,6 +2008,10 @@ packages:
resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==}
hasBin: true
+ abbrev@2.0.0:
+ resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
abbrev@3.0.1:
resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==}
engines: {node: ^18.17.0 || >=20.5.0}
@@ -2031,6 +2104,10 @@ packages:
array-ify@1.0.0:
resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==}
+ assertion-error@2.0.1:
+ resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
+ engines: {node: '>=12'}
+
ast-kit@2.1.2:
resolution: {integrity: sha512-cl76xfBQM6pztbrFWRnxbrDm9EOqDr1BF6+qQnnDZG2Co2LjyUktkN9GTJfBAfdae+DbT2nJf2nCGAdDDN7W2g==}
engines: {node: '>=20.18.0'}
@@ -2189,6 +2266,10 @@ packages:
caniuse-lite@1.0.30001748:
resolution: {integrity: sha512-5P5UgAr0+aBmNiplks08JLw+AW/XG/SurlgZLgB1dDLfAw7EfRGxIwzPHxdSCGY/BTKDqIVyJL87cCN6s0ZR0w==}
+ chai@5.3.3:
+ resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==}
+ engines: {node: '>=18'}
+
chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
@@ -2200,6 +2281,10 @@ packages:
change-case@5.4.4:
resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==}
+ check-error@2.1.1:
+ resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
+ engines: {node: '>= 16'}
+
chokidar@3.6.0:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'}
@@ -2273,6 +2358,10 @@ packages:
colortranslator@5.0.0:
resolution: {integrity: sha512-Z3UPUKasUVDFCDYAjP2fmlVRf1jFHJv1izAmPjiOa0OCIw1W7iC8PZ2GsoDa8uZv+mKyWopxxStT9q05+27h7w==}
+ commander@10.0.1:
+ resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
+ engines: {node: '>=14'}
+
commander@11.1.0:
resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==}
engines: {node: '>=16'}
@@ -2314,6 +2403,9 @@ packages:
confbox@0.2.2:
resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==}
+ config-chain@1.1.13:
+ resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
+
consola@3.4.2:
resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==}
engines: {node: ^14.18.0 || >=16.10.0}
@@ -2492,6 +2584,10 @@ packages:
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
engines: {node: '>=10'}
+ deep-eql@5.0.2:
+ resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
+ engines: {node: '>=6'}
+
deep-extend@0.6.0:
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
engines: {node: '>=4.0.0'}
@@ -2591,6 +2687,11 @@ packages:
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+ editorconfig@1.0.4:
+ resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==}
+ engines: {node: '>=14'}
+ hasBin: true
+
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
@@ -2875,6 +2976,10 @@ packages:
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
engines: {node: '>=6'}
+ expect-type@1.2.2:
+ resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==}
+ engines: {node: '>=12.0.0'}
+
exsolve@1.0.7:
resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==}
@@ -3083,6 +3188,10 @@ packages:
h3@1.15.4:
resolution: {integrity: sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==}
+ happy-dom@19.0.2:
+ resolution: {integrity: sha512-831CLbgDyjRbd2lApHZFsBDe56onuFcjsCBPodzWpzedTpeDr8CGZjs7iEIdNW1DVwSFRecfwzLpVyGBPamwGA==}
+ engines: {node: '>=20.0.0'}
+
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
@@ -3293,6 +3402,11 @@ packages:
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
hasBin: true
+ js-beautify@1.15.4:
+ resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==}
+ engines: {node: '>=14'}
+ hasBin: true
+
js-cookie@3.0.5:
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
engines: {node: '>=14'}
@@ -3530,6 +3644,9 @@ packages:
resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==}
engines: {node: '>=18'}
+ loupe@3.2.1:
+ resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==}
+
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
@@ -3614,6 +3731,10 @@ packages:
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
engines: {node: '>=10'}
+ minimatch@9.0.1:
+ resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
minimatch@9.0.5:
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -3737,6 +3858,11 @@ packages:
node-releases@2.0.23:
resolution: {integrity: sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==}
+ nopt@7.2.1:
+ resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+ hasBin: true
+
nopt@8.1.0:
resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==}
engines: {node: ^18.17.0 || >=20.5.0}
@@ -3919,6 +4045,10 @@ packages:
pathe@2.0.3:
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+ pathval@2.0.1:
+ resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==}
+ engines: {node: '>= 14.16'}
+
perfect-debounce@1.0.0:
resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
@@ -3956,6 +4086,11 @@ packages:
pkg-types@2.3.0:
resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
+ playwright-core@1.55.1:
+ resolution: {integrity: sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==}
+ engines: {node: '>=18'}
+ hasBin: true
+
pluralize@8.0.0:
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
engines: {node: '>=4'}
@@ -4166,6 +4301,9 @@ packages:
resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
engines: {node: '>= 6'}
+ proto-list@1.2.4:
+ resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
+
protocols@2.0.2:
resolution: {integrity: sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==}
@@ -4375,6 +4513,9 @@ packages:
resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==}
engines: {node: '>= 0.4'}
+ siginfo@2.0.0:
+ resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+
signal-exit@4.1.0:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
@@ -4445,6 +4586,9 @@ packages:
resolution: {integrity: sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==}
engines: {node: '>=12.0.0'}
+ stackback@0.0.2:
+ resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+
standard-as-callback@2.1.0:
resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
@@ -4625,6 +4769,12 @@ packages:
tiny-invariant@1.3.3:
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
+ tinybench@2.9.0:
+ resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
+
+ tinyexec@0.3.2:
+ resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
+
tinyexec@1.0.1:
resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==}
@@ -4632,6 +4782,18 @@ packages:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
+ tinypool@1.1.1:
+ resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+
+ tinyrainbow@2.0.0:
+ resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
+ engines: {node: '>=14.0.0'}
+
+ tinyspy@4.0.4:
+ resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==}
+ engines: {node: '>=14.0.0'}
+
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
@@ -4687,6 +4849,9 @@ packages:
unctx@2.4.1:
resolution: {integrity: sha512-AbaYw0Nm4mK4qjhns67C+kgxR2YWiwlDBPzxrN8h8C6VtAdCgditAY5Dezu3IJy4XVqAnbrXt9oQJvsn3fyozg==}
+ undici-types@6.21.0:
+ resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+
undici-types@7.13.0:
resolution: {integrity: sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==}
@@ -4973,12 +5138,43 @@ packages:
vitest-environment-nuxt@1.0.1:
resolution: {integrity: sha512-eBCwtIQriXW5/M49FjqNKfnlJYlG2LWMSNFsRVKomc8CaMqmhQPBS5LZ9DlgYL9T8xIVsiA6RZn2lk7vxov3Ow==}
+ vitest@3.2.4:
+ resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ hasBin: true
+ peerDependencies:
+ '@edge-runtime/vm': '*'
+ '@types/debug': ^4.1.12
+ '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
+ '@vitest/browser': 3.2.4
+ '@vitest/ui': 3.2.4
+ happy-dom: '*'
+ jsdom: '*'
+ peerDependenciesMeta:
+ '@edge-runtime/vm':
+ optional: true
+ '@types/debug':
+ optional: true
+ '@types/node':
+ optional: true
+ '@vitest/browser':
+ optional: true
+ '@vitest/ui':
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+
vscode-uri@3.1.0:
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
vue-bundle-renderer@2.2.0:
resolution: {integrity: sha512-sz/0WEdYH1KfaOm0XaBmRZOWgYTEvUDt6yPYaUzl4E52qzgWLlknaPPTTZmp6benaPTlQAI/hN1x3tAzZygycg==}
+ vue-component-type-helpers@2.2.12:
+ resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==}
+
vue-component-type-helpers@3.1.0:
resolution: {integrity: sha512-cC1pYNRZkSS1iCvdlaMbbg2sjDwxX098FucEjtz9Yig73zYjWzQsnMe5M9H8dRNv55hAIDGUI29hF2BEUA4FMQ==}
@@ -5021,6 +5217,10 @@ packages:
webpack-virtual-modules@0.6.2:
resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
+ whatwg-mimetype@3.0.0:
+ resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==}
+ engines: {node: '>=12'}
+
whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
@@ -5038,6 +5238,11 @@ packages:
engines: {node: ^18.17.0 || >=20.5.0}
hasBin: true
+ why-is-node-running@2.3.0:
+ resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
+ engines: {node: '>=8'}
+ hasBin: true
+
word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
@@ -5395,6 +5600,11 @@ snapshots:
optionalDependencies:
react: 19.1.1
+ '@clerk/themes@2.4.24':
+ dependencies:
+ '@clerk/types': 4.91.0
+ tslib: 2.8.1
+
'@clerk/types@4.91.0':
dependencies:
csstype: 3.1.3
@@ -6227,7 +6437,7 @@ snapshots:
transitivePeerDependencies:
- magicast
- '@nuxt/test-utils@3.19.2(magicast@0.3.5)(typescript@5.9.3)':
+ '@nuxt/test-utils@3.19.2(@vue/test-utils@2.4.6)(happy-dom@19.0.2)(magicast@0.3.5)(playwright-core@1.55.1)(typescript@5.9.3)(vitest@3.2.4(@types/node@24.6.2)(happy-dom@19.0.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1))':
dependencies:
'@nuxt/kit': 3.19.2(magicast@0.3.5)
c12: 3.3.0(magicast@0.3.5)
@@ -6251,8 +6461,13 @@ snapshots:
tinyexec: 1.0.1
ufo: 1.6.1
unplugin: 2.3.10
- vitest-environment-nuxt: 1.0.1(magicast@0.3.5)(typescript@5.9.3)
+ vitest-environment-nuxt: 1.0.1(@vue/test-utils@2.4.6)(happy-dom@19.0.2)(magicast@0.3.5)(playwright-core@1.55.1)(typescript@5.9.3)(vitest@3.2.4(@types/node@24.6.2)(happy-dom@19.0.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1))
vue: 3.5.22(typescript@5.9.3)
+ optionalDependencies:
+ '@vue/test-utils': 2.4.6
+ happy-dom: 19.0.2
+ playwright-core: 1.55.1
+ vitest: 3.2.4(@types/node@24.6.2)(happy-dom@19.0.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1)
transitivePeerDependencies:
- magicast
- typescript
@@ -6416,6 +6631,8 @@ snapshots:
transitivePeerDependencies:
- magicast
+ '@one-ini/wasm@0.1.1': {}
+
'@opentelemetry/api@1.9.0': {}
'@oxc-minify/binding-android-arm64@0.87.0':
@@ -6908,14 +7125,24 @@ snapshots:
tslib: 2.8.1
optional: true
+ '@types/chai@5.2.2':
+ dependencies:
+ '@types/deep-eql': 4.0.2
+
'@types/conventional-commits-parser@5.0.1':
dependencies:
'@types/node': 24.6.2
+ '@types/deep-eql@4.0.2': {}
+
'@types/estree@1.0.8': {}
'@types/json-schema@7.0.15': {}
+ '@types/node@20.19.19':
+ dependencies:
+ undici-types: 6.21.0
+
'@types/node@24.6.2':
dependencies:
undici-types: 7.13.0
@@ -6930,6 +7157,8 @@ snapshots:
'@types/web-bluetooth@0.0.21': {}
+ '@types/whatwg-mimetype@3.0.2': {}
+
'@typescript-eslint/eslint-plugin@8.45.0(@typescript-eslint/parser@8.45.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.1
@@ -7127,6 +7356,48 @@ snapshots:
vite: 7.1.9(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1)
vue: 3.5.22(typescript@5.9.3)
+ '@vitest/expect@3.2.4':
+ dependencies:
+ '@types/chai': 5.2.2
+ '@vitest/spy': 3.2.4
+ '@vitest/utils': 3.2.4
+ chai: 5.3.3
+ tinyrainbow: 2.0.0
+
+ '@vitest/mocker@3.2.4(vite@7.1.9(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1))':
+ dependencies:
+ '@vitest/spy': 3.2.4
+ estree-walker: 3.0.3
+ magic-string: 0.30.19
+ optionalDependencies:
+ vite: 7.1.9(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1)
+
+ '@vitest/pretty-format@3.2.4':
+ dependencies:
+ tinyrainbow: 2.0.0
+
+ '@vitest/runner@3.2.4':
+ dependencies:
+ '@vitest/utils': 3.2.4
+ pathe: 2.0.3
+ strip-literal: 3.1.0
+
+ '@vitest/snapshot@3.2.4':
+ dependencies:
+ '@vitest/pretty-format': 3.2.4
+ magic-string: 0.30.19
+ pathe: 2.0.3
+
+ '@vitest/spy@3.2.4':
+ dependencies:
+ tinyspy: 4.0.4
+
+ '@vitest/utils@3.2.4':
+ dependencies:
+ '@vitest/pretty-format': 3.2.4
+ loupe: 3.2.1
+ tinyrainbow: 2.0.0
+
'@volar/language-core@2.4.23':
dependencies:
'@volar/source-map': 2.4.23
@@ -7270,6 +7541,11 @@ snapshots:
'@vue/shared@3.5.22': {}
+ '@vue/test-utils@2.4.6':
+ dependencies:
+ js-beautify: 1.15.4
+ vue-component-type-helpers: 2.2.12
+
'@vueuse/core@10.11.1(vue@3.5.22(typescript@5.9.3))':
dependencies:
'@types/web-bluetooth': 0.0.20
@@ -7333,6 +7609,8 @@ snapshots:
jsonparse: 1.3.1
through: 2.3.8
+ abbrev@2.0.0: {}
+
abbrev@3.0.1: {}
abort-controller@3.0.0:
@@ -7428,6 +7706,8 @@ snapshots:
array-ify@1.0.0: {}
+ assertion-error@2.0.1: {}
+
ast-kit@2.1.2:
dependencies:
'@babel/parser': 7.28.4
@@ -7595,6 +7875,14 @@ snapshots:
caniuse-lite@1.0.30001748: {}
+ chai@5.3.3:
+ dependencies:
+ assertion-error: 2.0.1
+ check-error: 2.1.1
+ deep-eql: 5.0.2
+ loupe: 3.2.1
+ pathval: 2.0.1
+
chalk@4.1.2:
dependencies:
ansi-styles: 4.3.0
@@ -7604,6 +7892,8 @@ snapshots:
change-case@5.4.4: {}
+ check-error@2.1.1: {}
+
chokidar@3.6.0:
dependencies:
anymatch: 3.1.3
@@ -7684,6 +7974,8 @@ snapshots:
colortranslator@5.0.0: {}
+ commander@10.0.1: {}
+
commander@11.1.0: {}
commander@14.0.1: {}
@@ -7718,6 +8010,11 @@ snapshots:
confbox@0.2.2: {}
+ config-chain@1.1.13:
+ dependencies:
+ ini: 1.3.8
+ proto-list: 1.2.4
+
consola@3.4.2: {}
conventional-changelog-angular@7.0.0:
@@ -7892,6 +8189,8 @@ snapshots:
mimic-response: 3.1.0
optional: true
+ deep-eql@5.0.2: {}
+
deep-extend@0.6.0:
optional: true
@@ -7964,6 +8263,13 @@ snapshots:
eastasianwidth@0.2.0: {}
+ editorconfig@1.0.4:
+ dependencies:
+ '@one-ini/wasm': 0.1.1
+ commander: 10.0.1
+ minimatch: 9.0.1
+ semver: 7.7.2
+
ee-first@1.1.1: {}
electron-to-chromium@1.5.230: {}
@@ -8301,6 +8607,8 @@ snapshots:
expand-template@2.0.3:
optional: true
+ expect-type@1.2.2: {}
+
exsolve@1.0.7: {}
fake-indexeddb@6.2.2: {}
@@ -8516,6 +8824,12 @@ snapshots:
ufo: 1.6.1
uncrypto: 0.1.3
+ happy-dom@19.0.2:
+ dependencies:
+ '@types/node': 20.19.19
+ '@types/whatwg-mimetype': 3.0.2
+ whatwg-mimetype: 3.0.0
+
has-flag@4.0.0: {}
hasown@2.0.2:
@@ -8578,8 +8892,7 @@ snapshots:
inherits@2.0.4: {}
- ini@1.3.8:
- optional: true
+ ini@1.3.8: {}
ini@4.1.1: {}
@@ -8735,6 +9048,14 @@ snapshots:
jiti@2.6.1: {}
+ js-beautify@1.15.4:
+ dependencies:
+ config-chain: 1.1.13
+ editorconfig: 1.0.4
+ glob: 10.4.5
+ js-cookie: 3.0.5
+ nopt: 7.2.1
+
js-cookie@3.0.5: {}
js-tokens@4.0.0: {}
@@ -8941,6 +9262,8 @@ snapshots:
strip-ansi: 7.1.2
wrap-ansi: 9.0.2
+ loupe@3.2.1: {}
+
lru-cache@10.4.3: {}
lru-cache@5.1.1:
@@ -9018,6 +9341,10 @@ snapshots:
dependencies:
brace-expansion: 2.0.2
+ minimatch@9.0.1:
+ dependencies:
+ brace-expansion: 2.0.2
+
minimatch@9.0.5:
dependencies:
brace-expansion: 2.0.2
@@ -9208,6 +9535,10 @@ snapshots:
node-releases@2.0.23: {}
+ nopt@7.2.1:
+ dependencies:
+ abbrev: 2.0.0
+
nopt@8.1.0:
dependencies:
abbrev: 3.0.1
@@ -9544,6 +9875,8 @@ snapshots:
pathe@2.0.3: {}
+ pathval@2.0.1: {}
+
perfect-debounce@1.0.0: {}
perfect-debounce@2.0.0: {}
@@ -9575,6 +9908,8 @@ snapshots:
exsolve: 1.0.7
pathe: 2.0.3
+ playwright-core@1.55.1: {}
+
pluralize@8.0.0: {}
postcss-calc@10.1.1(postcss@8.5.6):
@@ -9775,6 +10110,8 @@ snapshots:
kleur: 3.0.3
sisteransi: 1.0.5
+ proto-list@1.2.4: {}
+
protocols@2.0.2: {}
pump@3.0.3:
@@ -10031,6 +10368,8 @@ snapshots:
shell-quote@1.8.3: {}
+ siginfo@2.0.0: {}
+
signal-exit@4.1.0: {}
simple-concat@1.0.1:
@@ -10099,6 +10438,8 @@ snapshots:
stable-hash-x@0.2.0: {}
+ stackback@0.0.2: {}
+
standard-as-callback@2.1.0: {}
standardwebhooks@1.0.0:
@@ -10305,6 +10646,10 @@ snapshots:
tiny-invariant@1.3.3: {}
+ tinybench@2.9.0: {}
+
+ tinyexec@0.3.2: {}
+
tinyexec@1.0.1: {}
tinyglobby@0.2.15:
@@ -10312,6 +10657,12 @@ snapshots:
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
+ tinypool@1.1.1: {}
+
+ tinyrainbow@2.0.0: {}
+
+ tinyspy@4.0.4: {}
+
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
@@ -10356,6 +10707,8 @@ snapshots:
magic-string: 0.30.19
unplugin: 2.3.10
+ undici-types@6.21.0: {}
+
undici-types@7.13.0: {}
unenv@2.0.0-rc.21:
@@ -10655,9 +11008,9 @@ snapshots:
terser: 5.44.0
yaml: 2.8.1
- vitest-environment-nuxt@1.0.1(magicast@0.3.5)(typescript@5.9.3):
+ vitest-environment-nuxt@1.0.1(@vue/test-utils@2.4.6)(happy-dom@19.0.2)(magicast@0.3.5)(playwright-core@1.55.1)(typescript@5.9.3)(vitest@3.2.4(@types/node@24.6.2)(happy-dom@19.0.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1)):
dependencies:
- '@nuxt/test-utils': 3.19.2(magicast@0.3.5)(typescript@5.9.3)
+ '@nuxt/test-utils': 3.19.2(@vue/test-utils@2.4.6)(happy-dom@19.0.2)(magicast@0.3.5)(playwright-core@1.55.1)(typescript@5.9.3)(vitest@3.2.4(@types/node@24.6.2)(happy-dom@19.0.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1))
transitivePeerDependencies:
- '@cucumber/cucumber'
- '@jest/globals'
@@ -10672,12 +11025,56 @@ snapshots:
- typescript
- vitest
+ vitest@3.2.4(@types/node@24.6.2)(happy-dom@19.0.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1):
+ dependencies:
+ '@types/chai': 5.2.2
+ '@vitest/expect': 3.2.4
+ '@vitest/mocker': 3.2.4(vite@7.1.9(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1))
+ '@vitest/pretty-format': 3.2.4
+ '@vitest/runner': 3.2.4
+ '@vitest/snapshot': 3.2.4
+ '@vitest/spy': 3.2.4
+ '@vitest/utils': 3.2.4
+ chai: 5.3.3
+ debug: 4.4.3
+ expect-type: 1.2.2
+ magic-string: 0.30.19
+ pathe: 2.0.3
+ picomatch: 4.0.3
+ std-env: 3.9.0
+ tinybench: 2.9.0
+ tinyexec: 0.3.2
+ tinyglobby: 0.2.15
+ tinypool: 1.1.1
+ tinyrainbow: 2.0.0
+ vite: 7.1.9(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1)
+ vite-node: 3.2.4(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1)
+ why-is-node-running: 2.3.0
+ optionalDependencies:
+ '@types/node': 24.6.2
+ happy-dom: 19.0.2
+ transitivePeerDependencies:
+ - jiti
+ - less
+ - lightningcss
+ - msw
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ - tsx
+ - yaml
+
vscode-uri@3.1.0: {}
vue-bundle-renderer@2.2.0:
dependencies:
ufo: 1.6.1
+ vue-component-type-helpers@2.2.12: {}
+
vue-component-type-helpers@3.1.0: {}
vue-demi@0.14.10(vue@3.5.22(typescript@5.9.3)):
@@ -10717,6 +11114,8 @@ snapshots:
webpack-virtual-modules@0.6.2: {}
+ whatwg-mimetype@3.0.0: {}
+
whatwg-url@5.0.0:
dependencies:
tr46: 0.0.3
@@ -10732,6 +11131,11 @@ snapshots:
dependencies:
isexe: 3.1.1
+ why-is-node-running@2.3.0:
+ dependencies:
+ siginfo: 2.0.0
+ stackback: 0.0.2
+
word-wrap@1.2.5: {}
wrap-ansi@7.0.0:
diff --git a/public/favicon.ico b/public/favicon.ico
index 18993ad..a50c3a3 100644
Binary files a/public/favicon.ico and b/public/favicon.ico differ
diff --git a/public/lystra-logo-dark.svg b/public/lystra-logo-dark.svg
new file mode 100644
index 0000000..f2af3d9
--- /dev/null
+++ b/public/lystra-logo-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/lystra-logo-light.svg b/public/lystra-logo-light.svg
new file mode 100644
index 0000000..b655e4d
--- /dev/null
+++ b/public/lystra-logo-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/nuxt/__snapshots__/app.nuxt.spec.ts.snap b/test/nuxt/__snapshots__/app.nuxt.spec.ts.snap
new file mode 100644
index 0000000..9f8d2ea
--- /dev/null
+++ b/test/nuxt/__snapshots__/app.nuxt.spec.ts.snap
@@ -0,0 +1,22 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`App > should match snapshot 1`] = `
+"
+
+"
+`;
diff --git a/test/nuxt/app.nuxt.spec.ts b/test/nuxt/app.nuxt.spec.ts
new file mode 100644
index 0000000..94c2f96
--- /dev/null
+++ b/test/nuxt/app.nuxt.spec.ts
@@ -0,0 +1,35 @@
+import { mountSuspended } from '@nuxt/test-utils/runtime';
+import App from '../../app/app.vue';
+import { describe, expect, it } from 'vitest';
+
+describe('App', () => {
+ it('should match snapshot', async () => {
+ const wrapper = await mountSuspended(App, {
+ global: {
+ stubs: {
+ SignInButton: true,
+ SignedOut: true,
+ UserButton: true,
+ SignedIn: true,
+ },
+ },
+ });
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ it('should render properly', async () => {
+ const wrapper = await mountSuspended(App, {
+ global: {
+ stubs: {
+ SignInButton: true,
+ SignedOut: true,
+ UserButton: true,
+ SignedIn: true,
+ },
+ },
+ });
+ expect(wrapper.exists()).toBe(true);
+ });
+
+ // TODO: Add more tests as needed
+});
diff --git a/test/nuxt/components/Header.nuxt.spec.ts b/test/nuxt/components/Header.nuxt.spec.ts
new file mode 100644
index 0000000..e6d1c54
--- /dev/null
+++ b/test/nuxt/components/Header.nuxt.spec.ts
@@ -0,0 +1,142 @@
+import { mountSuspended } from '@nuxt/test-utils/runtime';
+import Header from '../../../app/components/Header/index.vue';
+import { describe, expect, it, vi } from 'vitest';
+
+describe('Header', () => {
+ it('should match snapshot', async () => {
+ const wrapper = await mountSuspended(Header, {
+ global: {
+ stubs: {
+ SignInButton: true,
+ SignedOut: true,
+ UserButton: true,
+ SignedIn: true,
+ },
+ },
+ });
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ it('should render properly', async () => {
+ const wrapper = await mountSuspended(Header, {
+ global: {
+ stubs: {
+ SignInButton: true,
+ SignedOut: true,
+ UserButton: true,
+ SignedIn: true,
+ },
+ },
+ });
+ expect(wrapper.exists()).toBe(true);
+ });
+
+ it('should render the logo properly', async () => {
+ const wrapper = await mountSuspended(Header, {
+ global: {
+ stubs: {
+ SignInButton: true,
+ SignedOut: true,
+ UserButton: true,
+ SignedIn: true,
+ },
+ },
+ });
+ const logo = wrapper.find('img');
+ expect(logo.exists()).toBe(true);
+ expect(logo.attributes('src')).toBe(
+ '/_ipx/b_transparent&fit_contain&s_40x40/lystra-logo-light.svg'
+ );
+ expect(logo.attributes('alt')).toBe('Lystra logo');
+ expect(logo.attributes('width')).toBe('40');
+ expect(logo.attributes('height')).toBe('40');
+ });
+
+ it('should render signed in and signed out stubs', async () => {
+ const wrapper = await mountSuspended(Header, {
+ global: {
+ stubs: {
+ SignInButton: true,
+ SignedOut: true,
+ UserButton: true,
+ SignedIn: true,
+ },
+ },
+ });
+
+ const signedInButtonStub = wrapper.find('signed-in-stub');
+ expect(signedInButtonStub.exists()).toBe(true);
+ const signedOutButtonStub = wrapper.find('signed-out-stub');
+ expect(signedOutButtonStub.exists()).toBe(true);
+ });
+
+ it('should render the header properly', async () => {
+ const wrapper = await mountSuspended(Header, {
+ global: {
+ stubs: {
+ SignInButton: true,
+ SignedOut: true,
+ UserButton: true,
+ SignedIn: true,
+ },
+ },
+ });
+ const headerElement = wrapper.find('h1');
+ expect(headerElement.exists()).toBe(true);
+ expect(headerElement.attributes('class')).toContain(
+ 'text-gray-900 dark:text-white text-lg font-bold'
+ );
+ expect(headerElement.text()).toBe('Lystra');
+ });
+
+ it('should render the theme button properly', async () => {
+ const wrapper = await mountSuspended(Header, {
+ global: {
+ stubs: {
+ SignInButton: true,
+ SignedOut: true,
+ UserButton: true,
+ SignedIn: true,
+ },
+ },
+ });
+ const themeButton = wrapper.find('button');
+ expect(themeButton.exists()).toBe(true);
+ expect(themeButton.attributes('class')).toContain(
+ 'rounded-md font-medium inline-flex items-center disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 text-default hover:bg-elevated active:bg-elevated focus:outline-none focus-visible:bg-elevated hover:disabled:bg-transparent dark:hover:disabled:bg-transparent hover:aria-disabled:bg-transparent dark:hover:aria-disabled:bg-transparent p-1.5 cursor-pointer'
+ );
+ expect(themeButton.attributes('aria-label')).toBe('Switch to dark mode');
+ expect(themeButton.attributes('type')).toBe('button');
+
+ const icon = themeButton.find('span');
+ expect(icon.exists()).toBe(true);
+ expect(icon.attributes('class')).toContain('iconify i-lucide:sun shrink-0 size-5');
+ expect(icon.attributes('aria-hidden')).toBe('true');
+ });
+
+ it('should toggle colorMode.value when theme button is clicked', async () => {
+ const colorMode = { value: 'light' };
+ vi.mock('nuxt/app', () => ({
+ useColorMode: () => colorMode,
+ }));
+
+ const HeaderWithMock = (await import('../../../app/components/Header/index.vue')).default;
+ const wrapper = await mountSuspended(HeaderWithMock, {
+ global: {
+ stubs: {
+ SignInButton: true,
+ SignedOut: true,
+ UserButton: true,
+ SignedIn: true,
+ },
+ },
+ });
+
+ const themeButton = wrapper.findAll('button')[0];
+ expect(themeButton.exists()).toBe(true);
+
+ colorMode.value = 'dark';
+ wrapper.vm.$forceUpdate?.();
+ expect(colorMode.value).toBe('dark');
+ });
+});
diff --git a/test/nuxt/components/__snapshots__/Header.nuxt.spec.ts.snap b/test/nuxt/components/__snapshots__/Header.nuxt.spec.ts.snap
new file mode 100644
index 0000000..45d1a25
--- /dev/null
+++ b/test/nuxt/components/__snapshots__/Header.nuxt.spec.ts.snap
@@ -0,0 +1,17 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`Header > should match snapshot 1`] = `
+""
+`;
diff --git a/test/nuxt/layouts/__snapshots__/default.nuxt.spec.ts.snap b/test/nuxt/layouts/__snapshots__/default.nuxt.spec.ts.snap
new file mode 100644
index 0000000..83e1364
--- /dev/null
+++ b/test/nuxt/layouts/__snapshots__/default.nuxt.spec.ts.snap
@@ -0,0 +1,19 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`DefaultLayout > should match snapshot 1`] = `
+""
+`;
diff --git a/test/nuxt/layouts/default.nuxt.spec.ts b/test/nuxt/layouts/default.nuxt.spec.ts
new file mode 100644
index 0000000..fb34def
--- /dev/null
+++ b/test/nuxt/layouts/default.nuxt.spec.ts
@@ -0,0 +1,35 @@
+import { mountSuspended } from '@nuxt/test-utils/runtime';
+import DefaultLayout from '../../../app/layouts/default.vue';
+import { describe, expect, it } from 'vitest';
+
+describe('DefaultLayout', () => {
+ it('should match snapshot', async () => {
+ const wrapper = await mountSuspended(DefaultLayout, {
+ global: {
+ stubs: {
+ SignInButton: true,
+ SignedOut: true,
+ UserButton: true,
+ SignedIn: true,
+ },
+ },
+ });
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ it('should render properly', async () => {
+ const wrapper = await mountSuspended(DefaultLayout, {
+ global: {
+ stubs: {
+ SignInButton: true,
+ SignedOut: true,
+ UserButton: true,
+ SignedIn: true,
+ },
+ },
+ });
+ expect(wrapper.exists()).toBe(true);
+ });
+
+ // TODO: Add more tests as needed
+});
diff --git a/vitest.config.ts b/vitest.config.ts
new file mode 100644
index 0000000..5845597
--- /dev/null
+++ b/vitest.config.ts
@@ -0,0 +1,12 @@
+import { defineVitestConfig } from '@nuxt/test-utils/config';
+
+export default defineVitestConfig({
+ test: {
+ environment: 'nuxt',
+ env: {
+ NODE_ENV: 'test',
+ NUXT_PUBLIC_CLERK_PUBLISHABLE_KEY: 'pk_test_fake_key',
+ NUXT_CLERK_SECRET_KEY: 'sk_test_fake_key',
+ },
+ },
+});