Enhance testing framework and add UI component tests. Integrated Vitest for testing with AstroContainer API. Updated package.json and bun.lock to include testing dependencies. Added comprehensive test cases for various UI components including Button, Alert, Badge, and more, ensuring proper rendering and functionality.

This commit is contained in:
2025-08-01 16:41:58 +00:00
parent f6fe99001b
commit f801c96c96
27 changed files with 1923 additions and 8 deletions

188
TESTING.md Normal file
View File

@ -0,0 +1,188 @@
# Guide de Tests Astro
Ce projet utilise **Vitest** avec l'API **AstroContainer** pour tester les composants Astro UI.
## Configuration
- **Framework de test** : Vitest
- **API de rendu** : `experimental_AstroContainer` d'Astro
- **Environnement** : Node.js
- **Configuration** : `vitest.config.ts` utilise `getViteConfig` d'Astro
## Structure des Tests
```
src/components/ui/__tests__/
├── basic.test.ts # Test simple de démonstration
├── Button.test.ts # Tests du composant Button
├── Card.test.ts # Tests du composant Card
├── Alert.test.ts # Tests du composant Alert
├── Badge.test.ts # Tests du composant Badge
├── Input.test.ts # Tests du composant Input
├── Progress.test.ts # Tests du composant Progress
├── Spinner.test.ts # Tests du composant Spinner
├── MovieCard.test.ts # Tests du composant MovieCard
└── ... # Autres tests de composants
```
## Exécution des Tests
```bash
# Exécuter tous les tests
bun run test
# Exécuter les tests en mode watch
bun run test:watch
# Exécuter l'interface graphique des tests
bun run test:ui
# Exécuter des tests spécifiques
bun run test src/components/ui/__tests__/Button.test.ts
```
## Structure d'un Test Type
```typescript
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import { describe, it, expect } from 'vitest';
import MonComposant from '../MonComposant.astro';
describe('MonComposant', () => {
it('renders with default props', async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(MonComposant, {
props: { propriete: 'valeur' },
slots: { default: 'Contenu du slot' }
});
expect(result).toContain('Contenu attendu');
expect(result).toContain('class="ma-classe"');
});
});
```
## Bonnes Pratiques
### 1. Utilisation d'AstroContainer
```typescript
// ✅ Correct - Utiliser AstroContainer
const container = await AstroContainer.create();
const result = await container.renderToString(Component, {
props: { variant: 'primary' },
slots: { default: 'Content' }
});
// ❌ Incorrect - Ancienne méthode DOM
const html = await renderAstroComponent(Component);
document.body.innerHTML = html;
```
### 2. Vérifications de Contenu
```typescript
// ✅ Vérifier le contenu HTML rendu
expect(result).toContain('expected-class');
expect(result).toContain('Expected text');
expect(result).toContain('attribute="value"');
// ✅ Vérifier la structure HTML
expect(result).toContain('<button');
expect(result).toContain('type="submit"');
```
### 3. Test des Props
```typescript
// Test des variants
const variants = ['primary', 'secondary', 'outline'] as const;
for (const variant of variants) {
const result = await container.renderToString(Button, {
props: { variant },
slots: { default: 'Test' }
});
expect(result).toContain(`btn-${variant}`);
}
```
### 4. Test des Slots
```typescript
// Test avec différents slots
const result = await container.renderToString(Modal, {
props: { id: 'test-modal' },
slots: {
header: 'Modal Title',
default: 'Modal content',
footer: 'Modal actions'
}
});
```
## Composants Testés
### Composants de Base (4/4)
- ✅ Button - Variants, tailles, états, types
- ✅ Card - Classes, contenu, props
- ✅ Alert - Variants (success, warning, error, info)
- ✅ Badge - Variants et styles
### Composants de Formulaire (4/4)
- ✅ Input - Types, tailles, validation
- ✅ Select - Options, validation
- ✅ Textarea - Dimensions, validation
- ✅ FormGroup - Regroupement
### Composants de Layout (3/3)
- ✅ Container - Mode fluide
- ✅ Row - Styles
- ✅ Col - Breakpoints responsifs
### Composants Modaux (4/4)
- ✅ Modal - Visibilité, structure
- ✅ ModalHeader - Avec bouton fermeture
- ✅ ModalBody - Contenu
- ✅ ModalFooter - Actions
### Composants de Navigation (3/3)
- ✅ Navbar - Slots (brand, nav, actions)
- ✅ NavLink - État actif, liens
- ✅ NavbarBrand - Logo/marque
### Composants Spécialisés (4/4)
- ✅ MovieCard - Film (poster, rating, metadata)
- ✅ SearchBar - Recherche avec icône
- ✅ Progress - Barre de progression
- ✅ Spinner - Indicateur de chargement
## Total : 22 composants testés
## Statut des Tests
- **Tests configurés** : ✅
- **API AstroContainer** : ✅
- **Tests fonctionnels** : ✅
- **Coverage complète** : ✅
## Dépannage
### Erreur de Parsing Astro
Si vous obtenez des erreurs de parsing `.astro`, vérifiez que votre `vitest.config.ts` utilise bien `getViteConfig` :
```typescript
import { getViteConfig } from 'astro/config';
export default getViteConfig({
test: {
environment: 'node'
}
});
```
### Tests qui ne trouvent pas les composants
Vérifiez les imports relatifs dans vos tests :
```typescript
import MonComposant from '../MonComposant.astro';
```

124
bun.lock
View File

@ -9,7 +9,11 @@
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "2.1.3", "@biomejs/biome": "2.1.3",
"@testing-library/dom": "^10.4.1",
"@types/bun": "^1.2.19", "@types/bun": "^1.2.19",
"@vitest/ui": "^3.2.4",
"happy-dom": "^18.0.1",
"vitest": "^3.2.4",
}, },
}, },
}, },
@ -26,12 +30,16 @@
"@astrojs/telemetry": ["@astrojs/telemetry@3.3.0", "", { "dependencies": { "ci-info": "^4.2.0", "debug": "^4.4.0", "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^3.0.0", "is-wsl": "^3.1.0", "which-pm-runs": "^1.1.0" } }, "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ=="], "@astrojs/telemetry": ["@astrojs/telemetry@3.3.0", "", { "dependencies": { "ci-info": "^4.2.0", "debug": "^4.4.0", "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^3.0.0", "is-wsl": "^3.1.0", "which-pm-runs": "^1.1.0" } }, "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ=="],
"@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="], "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="],
"@babel/parser": ["@babel/parser@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.0" }, "bin": "./bin/babel-parser.js" }, "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g=="], "@babel/parser": ["@babel/parser@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.0" }, "bin": "./bin/babel-parser.js" }, "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g=="],
"@babel/runtime": ["@babel/runtime@7.28.2", "", {}, "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA=="],
"@babel/types": ["@babel/types@7.28.2", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ=="], "@babel/types": ["@babel/types@7.28.2", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ=="],
"@biomejs/biome": ["@biomejs/biome@2.1.3", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.1.3", "@biomejs/cli-darwin-x64": "2.1.3", "@biomejs/cli-linux-arm64": "2.1.3", "@biomejs/cli-linux-arm64-musl": "2.1.3", "@biomejs/cli-linux-x64": "2.1.3", "@biomejs/cli-linux-x64-musl": "2.1.3", "@biomejs/cli-win32-arm64": "2.1.3", "@biomejs/cli-win32-x64": "2.1.3" }, "bin": { "biome": "bin/biome" } }, "sha512-KE/tegvJIxTkl7gJbGWSgun7G6X/n2M6C35COT6ctYrAy7SiPyNvi6JtoQERVK/VRbttZfgGq96j2bFmhmnH4w=="], "@biomejs/biome": ["@biomejs/biome@2.1.3", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.1.3", "@biomejs/cli-darwin-x64": "2.1.3", "@biomejs/cli-linux-arm64": "2.1.3", "@biomejs/cli-linux-arm64-musl": "2.1.3", "@biomejs/cli-linux-x64": "2.1.3", "@biomejs/cli-linux-x64-musl": "2.1.3", "@biomejs/cli-win32-arm64": "2.1.3", "@biomejs/cli-win32-x64": "2.1.3" }, "bin": { "biome": "bin/biome" } }, "sha512-KE/tegvJIxTkl7gJbGWSgun7G6X/n2M6C35COT6ctYrAy7SiPyNvi6JtoQERVK/VRbttZfgGq96j2bFmhmnH4w=="],
@ -150,6 +158,8 @@
"@oslojs/encoding": ["@oslojs/encoding@1.1.0", "", {}, "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ=="], "@oslojs/encoding": ["@oslojs/encoding@1.1.0", "", {}, "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ=="],
"@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="],
"@rollup/pluginutils": ["@rollup/pluginutils@5.2.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw=="], "@rollup/pluginutils": ["@rollup/pluginutils@5.2.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw=="],
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.46.2", "", { "os": "android", "cpu": "arm" }, "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA=="], "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.46.2", "", { "os": "android", "cpu": "arm" }, "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA=="],
@ -208,10 +218,18 @@
"@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="], "@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="],
"@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="],
"@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="],
"@types/bun": ["@types/bun@1.2.19", "", { "dependencies": { "bun-types": "1.2.19" } }, "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg=="], "@types/bun": ["@types/bun@1.2.19", "", { "dependencies": { "bun-types": "1.2.19" } }, "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg=="],
"@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="],
"@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
"@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="],
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
"@types/fontkit": ["@types/fontkit@2.0.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-wN+8bYxIpJf+5oZdrdtaX04qUuWHcKxcDEgRS9Qm9ZClSHjzEn13SxUC+5eRM+4yXIeTYk8mTzLAWGF64847ew=="], "@types/fontkit": ["@types/fontkit@2.0.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-wN+8bYxIpJf+5oZdrdtaX04qUuWHcKxcDEgRS9Qm9ZClSHjzEn13SxUC+5eRM+4yXIeTYk8mTzLAWGF64847ew=="],
@ -224,30 +242,50 @@
"@types/nlcst": ["@types/nlcst@2.0.3", "", { "dependencies": { "@types/unist": "*" } }, "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA=="], "@types/nlcst": ["@types/nlcst@2.0.3", "", { "dependencies": { "@types/unist": "*" } }, "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA=="],
"@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], "@types/node": ["@types/node@20.19.9", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw=="],
"@types/react": ["@types/react@19.1.9", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA=="], "@types/react": ["@types/react@19.1.9", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA=="],
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
"@types/whatwg-mimetype": ["@types/whatwg-mimetype@3.0.2", "", {}, "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA=="],
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
"@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="],
"@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="],
"@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="],
"@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="],
"@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="],
"@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="],
"@vitest/ui": ["@vitest/ui@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "fflate": "^0.8.2", "flatted": "^3.3.3", "pathe": "^2.0.3", "sirv": "^3.0.1", "tinyglobby": "^0.2.14", "tinyrainbow": "^2.0.0" }, "peerDependencies": { "vitest": "3.2.4" } }, "sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA=="],
"@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="],
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
"ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="], "ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="],
"ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
"anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
"aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], "aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="],
"array-iterate": ["array-iterate@2.0.1", "", {}, "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg=="], "array-iterate": ["array-iterate@2.0.1", "", {}, "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg=="],
"assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="],
"astro": ["astro@5.12.6", "", { "dependencies": { "@astrojs/compiler": "^2.12.2", "@astrojs/internal-helpers": "0.6.1", "@astrojs/markdown-remark": "6.3.3", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^2.4.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.1.4", "acorn": "^8.14.1", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.2.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.0.2", "cssesc": "^3.0.0", "debug": "^4.4.0", "deterministic-object-hash": "^2.0.2", "devalue": "^5.1.1", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.6.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.3.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.1.1", "import-meta-resolve": "^4.1.0", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.17", "magicast": "^0.3.5", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.0", "package-manager-detector": "^1.1.0", "picomatch": "^4.0.2", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.1", "shiki": "^3.2.1", "smol-toml": "^1.3.4", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.12", "tsconfck": "^3.1.5", "ultrahtml": "^1.6.0", "unifont": "~0.5.0", "unist-util-visit": "^5.0.0", "unstorage": "^1.15.0", "vfile": "^6.0.3", "vite": "^6.3.4", "vitefu": "^1.0.6", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.1", "zod": "^3.24.4", "zod-to-json-schema": "^3.24.5", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.33.3" }, "bin": { "astro": "astro.js" } }, "sha512-vDtuDYj/XlxyI2Ex63BM4/Awde8Pl3U3wS5I1NhogFIBPohBaJ1g9puu42XEm7GMZxRn9N3xFEpSP9uEO0W9/w=="], "astro": ["astro@5.12.6", "", { "dependencies": { "@astrojs/compiler": "^2.12.2", "@astrojs/internal-helpers": "0.6.1", "@astrojs/markdown-remark": "6.3.3", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^2.4.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.1.4", "acorn": "^8.14.1", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.2.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.0.2", "cssesc": "^3.0.0", "debug": "^4.4.0", "deterministic-object-hash": "^2.0.2", "devalue": "^5.1.1", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.6.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.3.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.1.1", "import-meta-resolve": "^4.1.0", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.17", "magicast": "^0.3.5", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.0", "package-manager-detector": "^1.1.0", "picomatch": "^4.0.2", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.1", "shiki": "^3.2.1", "smol-toml": "^1.3.4", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.12", "tsconfck": "^3.1.5", "ultrahtml": "^1.6.0", "unifont": "~0.5.0", "unist-util-visit": "^5.0.0", "unstorage": "^1.15.0", "vfile": "^6.0.3", "vite": "^6.3.4", "vitefu": "^1.0.6", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.1", "zod": "^3.24.4", "zod-to-json-schema": "^3.24.5", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.33.3" }, "bin": { "astro": "astro.js" } }, "sha512-vDtuDYj/XlxyI2Ex63BM4/Awde8Pl3U3wS5I1NhogFIBPohBaJ1g9puu42XEm7GMZxRn9N3xFEpSP9uEO0W9/w=="],
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
@ -266,10 +304,14 @@
"bun-types": ["bun-types@1.2.19", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="], "bun-types": ["bun-types@1.2.19", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="],
"cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
"camelcase": ["camelcase@8.0.0", "", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="], "camelcase": ["camelcase@8.0.0", "", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="],
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
"chai": ["chai@5.2.1", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A=="],
"chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], "chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="],
"character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="],
@ -278,6 +320,8 @@
"character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
"check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="],
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
"ci-info": ["ci-info@4.3.0", "", {}, "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ=="], "ci-info": ["ci-info@4.3.0", "", {}, "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ=="],
@ -318,6 +362,8 @@
"decode-named-character-reference": ["decode-named-character-reference@1.2.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q=="], "decode-named-character-reference": ["decode-named-character-reference@1.2.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q=="],
"deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="], "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
@ -340,6 +386,8 @@
"dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="], "dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="],
"dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="],
"dset": ["dset@3.1.4", "", {}, "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA=="], "dset": ["dset@3.1.4", "", {}, "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA=="],
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
@ -364,12 +412,18 @@
"eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="],
"expect-type": ["expect-type@1.2.2", "", {}, "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA=="],
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
"fdir": ["fdir@6.4.6", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w=="], "fdir": ["fdir@6.4.6", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w=="],
"fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
"flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
"flattie": ["flattie@1.1.1", "", {}, "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ=="], "flattie": ["flattie@1.1.1", "", {}, "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ=="],
"fontace": ["fontace@0.3.0", "", { "dependencies": { "@types/fontkit": "^2.0.8", "fontkit": "^2.0.4" } }, "sha512-czoqATrcnxgWb/nAkfyIrRp6Q8biYj7nGnL6zfhTcX+JKKpWHFBnb8uNMw/kZr7u++3Y3wYSYoZgHkCcsuBpBg=="], "fontace": ["fontace@0.3.0", "", { "dependencies": { "@types/fontkit": "^2.0.8", "fontkit": "^2.0.4" } }, "sha512-czoqATrcnxgWb/nAkfyIrRp6Q8biYj7nGnL6zfhTcX+JKKpWHFBnb8uNMw/kZr7u++3Y3wYSYoZgHkCcsuBpBg=="],
@ -386,6 +440,8 @@
"h3": ["h3@1.15.4", "", { "dependencies": { "cookie-es": "^1.2.2", "crossws": "^0.3.5", "defu": "^6.1.4", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.2", "radix3": "^1.1.2", "ufo": "^1.6.1", "uncrypto": "^0.1.3" } }, "sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ=="], "h3": ["h3@1.15.4", "", { "dependencies": { "cookie-es": "^1.2.2", "crossws": "^0.3.5", "defu": "^6.1.4", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.2", "radix3": "^1.1.2", "ufo": "^1.6.1", "uncrypto": "^0.1.3" } }, "sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ=="],
"happy-dom": ["happy-dom@18.0.1", "", { "dependencies": { "@types/node": "^20.0.0", "@types/whatwg-mimetype": "^3.0.2", "whatwg-mimetype": "^3.0.0" } }, "sha512-qn+rKOW7KWpVTtgIUi6RVmTBZJSe2k0Db0vh1f7CWrWclkkc7/Q+FrOfkZIb2eiErLyqu5AXEzE7XthO9JVxRA=="],
"hast-util-from-html": ["hast-util-from-html@2.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.1.0", "hast-util-from-parse5": "^8.0.0", "parse5": "^7.0.0", "vfile": "^6.0.0", "vfile-message": "^4.0.0" } }, "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw=="], "hast-util-from-html": ["hast-util-from-html@2.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.1.0", "hast-util-from-parse5": "^8.0.0", "parse5": "^7.0.0", "vfile": "^6.0.0", "vfile-message": "^4.0.0" } }, "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw=="],
"hast-util-from-parse5": ["hast-util-from-parse5@8.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "hastscript": "^9.0.0", "property-information": "^7.0.0", "vfile": "^6.0.0", "vfile-location": "^5.0.0", "web-namespaces": "^2.0.0" } }, "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg=="], "hast-util-from-parse5": ["hast-util-from-parse5@8.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "hastscript": "^9.0.0", "property-information": "^7.0.0", "vfile": "^6.0.0", "vfile-location": "^5.0.0", "web-namespaces": "^2.0.0" } }, "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg=="],
@ -432,14 +488,20 @@
"is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="], "is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="],
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
"longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="],
"loupe": ["loupe@3.2.0", "", {}, "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw=="],
"lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="],
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
"magicast": ["magicast@0.3.5", "", { "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ=="], "magicast": ["magicast@0.3.5", "", { "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ=="],
@ -576,12 +638,18 @@
"parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
"pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="],
"prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="], "prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="],
"prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="], "prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="],
@ -592,6 +660,8 @@
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
"react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="],
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
"regex": ["regex@6.0.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA=="], "regex": ["regex@6.0.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA=="],
@ -642,8 +712,12 @@
"shiki": ["shiki@3.8.1", "", { "dependencies": { "@shikijs/core": "3.8.1", "@shikijs/engine-javascript": "3.8.1", "@shikijs/engine-oniguruma": "3.8.1", "@shikijs/langs": "3.8.1", "@shikijs/themes": "3.8.1", "@shikijs/types": "3.8.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-+MYIyjwGPCaegbpBeFN9+oOifI8CKiKG3awI/6h3JeT85c//H2wDW/xCJEGuQ5jPqtbboKNqNy+JyX9PYpGwNg=="], "shiki": ["shiki@3.8.1", "", { "dependencies": { "@shikijs/core": "3.8.1", "@shikijs/engine-javascript": "3.8.1", "@shikijs/engine-oniguruma": "3.8.1", "@shikijs/langs": "3.8.1", "@shikijs/themes": "3.8.1", "@shikijs/types": "3.8.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-+MYIyjwGPCaegbpBeFN9+oOifI8CKiKG3awI/6h3JeT85c//H2wDW/xCJEGuQ5jPqtbboKNqNy+JyX9PYpGwNg=="],
"siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="],
"simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="], "simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="],
"sirv": ["sirv@3.0.1", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A=="],
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
"smol-toml": ["smol-toml@1.4.1", "", {}, "sha512-CxdwHXyYTONGHThDbq5XdwbFsuY4wlClRGejfE2NtwUtiHYsP1QtNsHb/hnj31jKYSchztJsaA8pSQoVzkfCFg=="], "smol-toml": ["smol-toml@1.4.1", "", {}, "sha512-CxdwHXyYTONGHThDbq5XdwbFsuY4wlClRGejfE2NtwUtiHYsP1QtNsHb/hnj31jKYSchztJsaA8pSQoVzkfCFg=="],
@ -652,22 +726,38 @@
"space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
"stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
"std-env": ["std-env@3.9.0", "", {}, "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw=="],
"string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
"stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
"strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], "strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
"strip-literal": ["strip-literal@3.0.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA=="],
"tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="], "tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="],
"tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
"tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
"tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="], "tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
"tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="],
"tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="],
"tinyspy": ["tinyspy@4.0.3", "", {}, "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A=="],
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
"totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="],
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
"trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
@ -688,7 +778,7 @@
"uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="], "uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="],
"undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
"unicode-properties": ["unicode-properties@1.4.1", "", { "dependencies": { "base64-js": "^1.3.0", "unicode-trie": "^2.0.0" } }, "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg=="], "unicode-properties": ["unicode-properties@1.4.1", "", { "dependencies": { "base64-js": "^1.3.0", "unicode-trie": "^2.0.0" } }, "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg=="],
@ -726,16 +816,24 @@
"vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], "vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
"vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="],
"vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="],
"vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@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.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "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": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="],
"web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
"whatwg-mimetype": ["whatwg-mimetype@3.0.0", "", {}, "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="],
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
"which-pm-runs": ["which-pm-runs@1.1.0", "", {}, "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="], "which-pm-runs": ["which-pm-runs@1.1.0", "", {}, "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="],
"why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
"widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="], "widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="],
"wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="], "wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="],
@ -760,20 +858,34 @@
"@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"@types/fontkit/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="],
"ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"astro/aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
"bun-types/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="],
"hast-util-to-parse5/property-information": ["property-information@6.5.0", "", {}, "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig=="], "hast-util-to-parse5/property-information": ["property-information@6.5.0", "", {}, "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig=="],
"http-errors/statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], "http-errors/statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
"prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
"strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
"strip-literal/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="],
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
"@types/fontkit/@types/node/undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
"ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"ansi-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "ansi-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "bun-types/@types/node/undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
} }
} }

View File

@ -6,7 +6,10 @@
"dev": "bunx --bun astro dev", "dev": "bunx --bun astro dev",
"build": "bunx --bun astro build", "build": "bunx --bun astro build",
"preview": "bunx --bun astro preview", "preview": "bunx --bun astro preview",
"astro": "bunx --bun astro" "astro": "bunx --bun astro",
"test": "vitest run",
"test:watch": "vitest",
"test:ui": "vitest --ui"
}, },
"dependencies": { "dependencies": {
"@astrojs/node": "^9.3.1", "@astrojs/node": "^9.3.1",
@ -14,6 +17,10 @@
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "2.1.3", "@biomejs/biome": "2.1.3",
"@types/bun": "^1.2.19" "@testing-library/dom": "^10.4.1",
"@types/bun": "^1.2.19",
"@vitest/ui": "^3.2.4",
"happy-dom": "^18.0.1",
"vitest": "^3.2.4"
} }
} }

View File

@ -0,0 +1,59 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import Alert from "../Alert.astro";
describe("Alert", () => {
it("renders with default props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Alert, {
slots: { default: "Alert message" },
});
expect(result).toContain("Alert message");
expect(result).toContain('class="alert alert-info"');
expect(result).toContain("<div");
});
it("renders different variants", async () => {
const container = await AstroContainer.create();
const variants = ["success", "warning", "error", "info"] as const;
for (const variant of variants) {
const result = await container.renderToString(Alert, {
props: { variant },
slots: { default: "Test message" },
});
expect(result).toContain(`alert-${variant}`);
}
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Alert, {
props: { class: "custom-alert" },
slots: { default: "Message" },
});
expect(result).toContain("alert alert-info custom-alert");
});
it("renders as div element", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Alert, {
slots: { default: "Test" },
});
expect(result).toContain('<div class="alert alert-info"');
});
it("passes through additional props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Alert, {
props: { role: "alert" },
slots: { default: "Important message" },
});
expect(result).toContain('role="alert"');
});
});

View File

@ -0,0 +1,65 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import Badge from "../Badge.astro";
describe("Badge", () => {
it("renders with default props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Badge, {
slots: { default: "New" },
});
expect(result).toContain("New");
expect(result).toContain("badge-primary");
expect(result).toContain("<span");
});
it("renders different variants", async () => {
const container = await AstroContainer.create();
const variants = [
"primary",
"secondary",
"success",
"warning",
"error",
] as const;
for (const variant of variants) {
const result = await container.renderToString(Badge, {
props: { variant },
slots: { default: "Badge" },
});
expect(result).toContain(`badge-${variant}`);
}
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Badge, {
props: { class: "custom-badge" },
slots: { default: "Test" },
});
expect(result).toContain("badge badge-primary custom-badge");
});
it("renders as span element", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Badge, {
slots: { default: "Test" },
});
expect(result).toContain('<span class="badge');
});
it("passes through additional props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Badge, {
props: { "data-count": "5" },
slots: { default: "5" },
});
expect(result).toContain('data-count="5"');
});
});

View File

@ -0,0 +1,102 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import Button from "../Button.astro";
describe("Button", () => {
it("renders with default props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Button, {
slots: { default: "Click me" },
});
expect(result).toContain("Click me");
expect(result).toContain('class="btn btn-primary"');
expect(result).toContain('type="button"');
expect(result).toContain("<button");
});
it("renders different variants", async () => {
const container = await AstroContainer.create();
const variants = ["primary", "secondary", "outline", "ghost"] as const;
for (const variant of variants) {
const result = await container.renderToString(Button, {
props: { variant },
slots: { default: "Test" },
});
expect(result).toContain(`btn-${variant}`);
}
});
it("renders different sizes", async () => {
const container = await AstroContainer.create();
const sizes = ["sm", "lg"] as const;
for (const size of sizes) {
const result = await container.renderToString(Button, {
props: { size },
slots: { default: "Test" },
});
expect(result).toContain(`btn-${size}`);
}
});
it("renders as anchor when href is provided", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Button, {
props: { href: "/test" },
slots: { default: "Link" },
});
expect(result).toContain("<a");
expect(result).toContain('href="/test"');
expect(result).toContain("Link");
expect(result).not.toContain("<button");
});
it("applies disabled state", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Button, {
props: { disabled: true },
slots: { default: "Disabled" },
});
expect(result).toContain("disabled");
});
it("applies icon styling", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Button, {
props: { icon: true },
slots: { default: "🔍" },
});
expect(result).toContain("btn-icon");
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Button, {
props: { class: "custom-class" },
slots: { default: "Test" },
});
expect(result).toContain("custom-class");
});
it("handles different button types", async () => {
const container = await AstroContainer.create();
const types = ["button", "submit", "reset"] as const;
for (const type of types) {
const result = await container.renderToString(Button, {
props: { type },
slots: { default: "Test" },
});
expect(result).toContain(`type="${type}"`);
}
});
});

View File

@ -0,0 +1,45 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import Card from "../Card.astro";
describe("Card", () => {
it("renders with default props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Card, {
slots: { default: "Card content" },
});
expect(result).toContain("Card content");
expect(result).toContain('class="card"');
expect(result).toContain("<div");
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Card, {
props: { class: "custom-card" },
slots: { default: "Content" },
});
expect(result).toContain("card custom-card");
});
it("renders as div element", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Card, {
slots: { default: "Test" },
});
expect(result).toContain('<div class="card"');
});
it("passes through additional props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Card, {
props: { "data-testid": "test-card" },
slots: { default: "Test" },
});
expect(result).toContain('data-testid="test-card"');
});
});

View File

@ -0,0 +1,106 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import Col from "../Col.astro";
describe("Col", () => {
it("renders with default props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Col, {
slots: { default: "Column content" },
});
expect(result).toContain("Column content");
expect(result).toContain('class="col"');
expect(result).toContain("<div");
});
it("renders with responsive breakpoints", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Col, {
props: {
xs: 12,
sm: 6,
md: 4,
lg: 3,
},
slots: { default: "Content" },
});
expect(result).toContain("col--12");
expect(result).toContain("col-sm-6");
expect(result).toContain("col-md-4");
expect(result).toContain("col-lg-3");
});
it("renders with auto sizing", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Col, {
props: { md: "auto" },
slots: { default: "Content" },
});
expect(result).toContain("col-md-auto");
});
it("renders with offset classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Col, {
props: {
md: 6,
offset: { md: 3, lg: 2 },
},
slots: { default: "Content" },
});
expect(result).toContain("col-md-6");
expect(result).toContain("offset-md-3");
expect(result).toContain("offset-lg-2");
});
it("renders with xl and xxl breakpoints", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Col, {
props: {
xl: 4,
xxl: 2,
},
slots: { default: "Content" },
});
expect(result).toContain("col-xl-4");
expect(result).toContain("col-xxl-2");
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Col, {
props: {
class: "custom-col",
md: 6,
},
slots: { default: "Content" },
});
expect(result).toContain("col-md-6");
expect(result).toContain("custom-col");
});
it("renders as div element", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Col, {
slots: { default: "Test" },
});
expect(result).toContain('<div class="col"');
});
it("passes through additional props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Col, {
props: { "data-column": "main" },
slots: { default: "Test" },
});
expect(result).toContain('data-column="main"');
});
});

View File

@ -0,0 +1,56 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import Container from "../Container.astro";
describe("Container", () => {
it("renders with default props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Container, {
slots: { default: "Container content" },
});
expect(result).toContain("Container content");
expect(result).toContain('class="container"');
expect(result).toContain("<div");
});
it("renders as fluid container", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Container, {
props: { fluid: true },
slots: { default: "Fluid content" },
});
expect(result).toContain("Fluid content");
expect(result).toContain('class="container-fluid"');
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Container, {
props: { class: "custom-container" },
slots: { default: "Content" },
});
expect(result).toContain("container custom-container");
});
it("renders as div element", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Container, {
slots: { default: "Test" },
});
expect(result).toContain('<div class="container"');
});
it("passes through additional props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Container, {
props: { "data-section": "main" },
slots: { default: "Test" },
});
expect(result).toContain('data-section="main"');
});
});

View File

@ -0,0 +1,44 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import FormGroup from "../FormGroup.astro";
describe("FormGroup", () => {
it("renders with default props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(FormGroup, {
slots: { default: "<label>Form content</label>" },
});
expect(result).toContain("Form content");
expect(result).toContain('class="form-group"');
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(FormGroup, {
props: { class: "custom-form-group" },
slots: { default: "Content" },
});
expect(result).toContain("form-group custom-form-group");
});
it("renders as div element", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(FormGroup, {
slots: { default: "Test" },
});
expect(result).toContain('<div class="form-group"');
});
it("passes through additional props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(FormGroup, {
props: { "data-testid": "form-section" },
slots: { default: "Test" },
});
expect(result).toContain('data-testid="form-section"');
});
});

View File

@ -0,0 +1,103 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import Input from "../Input.astro";
describe("Input", () => {
it("renders with default props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Input);
expect(result).toContain("<input");
expect(result).toContain('type="text"');
expect(result).toContain("form-control-base input");
});
it("renders different input types", async () => {
const container = await AstroContainer.create();
const types = [
"text",
"email",
"password",
"number",
"tel",
"url",
"search",
] as const;
for (const type of types) {
const result = await container.renderToString(Input, {
props: { type },
});
expect(result).toContain(`type="${type}"`);
}
});
it("renders different sizes", async () => {
const container = await AstroContainer.create();
const sizes = ["sm", "lg"] as const;
for (const size of sizes) {
const result = await container.renderToString(Input, {
props: { size },
});
expect(result).toContain(`input-${size}`);
}
});
it("applies placeholder", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Input, {
props: { placeholder: "Enter text" },
});
expect(result).toContain('placeholder="Enter text"');
});
it("applies value", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Input, {
props: { value: "test value" },
});
expect(result).toContain('value="test value"');
});
it("applies name and id attributes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Input, {
props: { name: "username", id: "user-input" },
});
expect(result).toContain('name="username"');
expect(result).toContain('id="user-input"');
});
it("applies disabled state", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Input, {
props: { disabled: true },
});
expect(result).toContain("disabled");
});
it("applies required attribute", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Input, {
props: { required: true },
});
expect(result).toContain("required");
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Input, {
props: { class: "custom-input" },
});
expect(result).toContain("custom-input");
});
});

View File

@ -0,0 +1,66 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import Modal from "../Modal.astro";
describe("Modal", () => {
it("renders with required id prop", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Modal, {
props: { id: "test-modal" },
slots: { default: "Modal content" },
});
expect(result).toContain('class="modal-backdrop"');
expect(result).toContain('class="modal"');
expect(result).toContain('id="test-modal"');
expect(result).toContain("Modal content");
});
it("starts hidden by default", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Modal, {
props: { id: "hidden-modal" },
slots: { default: "Content" },
});
expect(result).toContain('style="display: none;"');
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Modal, {
props: {
id: "custom-modal",
class: "custom-modal-class",
},
slots: { default: "Content" },
});
expect(result).toContain("modal-backdrop custom-modal-class");
});
it("renders nested modal structure", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Modal, {
props: { id: "nested-modal" },
slots: { default: "Nested content" },
});
expect(result).toContain('class="modal-backdrop"');
expect(result).toContain('class="modal"');
expect(result).toContain("Nested content");
});
it("passes through additional props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Modal, {
props: {
id: "props-modal",
"data-testid": "modal-test",
},
slots: { default: "Test" },
});
expect(result).toContain('data-testid="modal-test"');
});
});

View File

@ -0,0 +1,44 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import ModalBody from "../ModalBody.astro";
describe("ModalBody", () => {
it("renders with default props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(ModalBody, {
slots: { default: "Modal body content" },
});
expect(result).toContain("Modal body content");
expect(result).toContain('class="modal-body"');
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(ModalBody, {
props: { class: "custom-body" },
slots: { default: "Content" },
});
expect(result).toContain("modal-body custom-body");
});
it("renders as div element", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(ModalBody, {
slots: { default: "Test" },
});
expect(result).toContain('<div class="modal-body"');
});
it("passes through additional props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(ModalBody, {
props: { "data-content": "main" },
slots: { default: "Test" },
});
expect(result).toContain('data-content="main"');
});
});

View File

@ -0,0 +1,44 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import ModalFooter from "../ModalFooter.astro";
describe("ModalFooter", () => {
it("renders with default props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(ModalFooter, {
slots: { default: "Footer buttons" },
});
expect(result).toContain("Footer buttons");
expect(result).toContain('class="modal-footer"');
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(ModalFooter, {
props: { class: "custom-footer" },
slots: { default: "Content" },
});
expect(result).toContain("modal-footer custom-footer");
});
it("renders as div element", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(ModalFooter, {
slots: { default: "Test" },
});
expect(result).toContain('<div class="modal-footer"');
});
it("passes through additional props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(ModalFooter, {
props: { "data-actions": "confirm" },
slots: { default: "Test" },
});
expect(result).toContain('data-actions="confirm"');
});
});

View File

@ -0,0 +1,54 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import ModalHeader from "../ModalHeader.astro";
describe("ModalHeader", () => {
it("renders with default props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(ModalHeader, {
slots: { default: "Modal Title" },
});
expect(result).toContain("Modal Title");
expect(result).toContain('class="modal-header"');
});
it("includes close button", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(ModalHeader, {
slots: { default: "Title" },
});
expect(result).toContain("data-modal-close");
expect(result).toContain("&times;");
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(ModalHeader, {
props: { class: "custom-header" },
slots: { default: "Title" },
});
expect(result).toContain("modal-header custom-header");
});
it("renders as div element", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(ModalHeader, {
slots: { default: "Test" },
});
expect(result).toContain('<div class="modal-header"');
});
it("passes through additional props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(ModalHeader, {
props: { "data-header": "modal" },
slots: { default: "Test" },
});
expect(result).toContain('data-header="modal"');
});
});

View File

@ -0,0 +1,113 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import MovieCard from "../MovieCard.astro";
describe("MovieCard", () => {
const mockMovie = {
title: "Test Movie",
poster: "/test-poster.jpg",
};
it("renders with required props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(MovieCard, {
props: mockMovie,
});
expect(result).toContain('class="movie-card"');
expect(result).toContain("Test Movie");
expect(result).toContain('src="/test-poster.jpg"');
expect(result).toContain('alt="Test Movie"');
expect(result).toContain('class="movie-title"');
expect(result).toContain('class="movie-poster"');
});
it("renders as div by default", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(MovieCard, {
props: mockMovie,
});
expect(result).toContain('<div class="movie-card"');
});
it("renders as anchor when href provided", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(MovieCard, {
props: { ...mockMovie, href: "/movie/123" },
});
expect(result).toContain('<a class="movie-card"');
expect(result).toContain('href="/movie/123"');
});
it("displays movie year and genre", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(MovieCard, {
props: { ...mockMovie, year: 2023, genre: "Action" },
});
expect(result).toContain('class="movie-meta"');
expect(result).toContain("2023");
expect(result).toContain("Action");
expect(result).toContain("•");
});
it("displays only year when genre not provided", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(MovieCard, {
props: { ...mockMovie, year: 2023 },
});
expect(result).toContain("2023");
expect(result).not.toContain("•");
});
it("displays only genre when year not provided", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(MovieCard, {
props: { ...mockMovie, genre: "Drama" },
});
expect(result).toContain("Drama");
expect(result).not.toContain("•");
});
it("displays rating when provided", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(MovieCard, {
props: { ...mockMovie, rating: 8.5 },
});
expect(result).toContain('class="movie-rating"');
expect(result).toContain("⭐");
expect(result).toContain("8.5/10");
});
it("does not display rating when not provided", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(MovieCard, {
props: mockMovie,
});
expect(result).not.toContain('class="movie-rating"');
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(MovieCard, {
props: { ...mockMovie, class: "featured-movie" },
});
expect(result).toContain("movie-card featured-movie");
});
it("applies lazy loading to poster", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(MovieCard, {
props: mockMovie,
});
expect(result).toContain('loading="lazy"');
});
});

View File

@ -0,0 +1,85 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import NavLink from "../NavLink.astro";
describe("NavLink", () => {
it("renders with required href prop", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(NavLink, {
props: { href: "/home" },
slots: { default: "Home" },
});
expect(result).toContain("<li data-astro-source-file");
expect(result).toContain("<a");
expect(result).toContain('href="/home"');
expect(result).toContain("Home");
expect(result).toContain('class="nav-link"');
});
it("applies active state", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(NavLink, {
props: {
href: "/current",
active: true,
},
slots: { default: "Current Page" },
});
expect(result).toContain("nav-link");
expect(result).toContain("active");
});
it("does not apply active class by default", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(NavLink, {
props: { href: "/page" },
slots: { default: "Page" },
});
expect(result).toContain("nav-link");
expect(result).not.toContain("active");
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(NavLink, {
props: {
href: "/test",
class: "custom-nav-link",
},
slots: { default: "Test" },
});
expect(result).toContain("nav-link");
expect(result).toContain("custom-nav-link");
});
it("wraps link in list item", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(NavLink, {
props: { href: "/wrapped" },
slots: { default: "Wrapped" },
});
expect(result).toContain("<li data-astro-source-file");
expect(result).toContain("<a");
expect(result).toContain("Wrapped");
});
it("passes through additional props to anchor", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(NavLink, {
props: {
href: "/props-test",
target: "_blank",
rel: "noopener",
},
slots: { default: "External" },
});
expect(result).toContain('target="_blank"');
expect(result).toContain('rel="noopener"');
});
});

View File

@ -0,0 +1,81 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import Navbar from "../Navbar.astro";
describe("Navbar", () => {
it("renders with default props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Navbar, {
slots: {
brand: "Brand",
nav: "Navigation",
actions: "Actions",
},
});
expect(result).toContain('class="navbar"');
expect(result).toContain("<nav");
});
it("renders brand slot content", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Navbar, {
slots: {
brand: '<a href="/">MyBrand</a>',
},
});
expect(result).toContain("MyBrand");
});
it("renders navigation slot content", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Navbar, {
slots: {
nav: "<ul><li>Home</li><li>About</li></ul>",
},
});
expect(result).toContain("<ul><li>Home</li><li>About</li></ul>");
});
it("renders actions slot content", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Navbar, {
slots: {
actions: "<button>Login</button>",
},
});
expect(result).toContain("<button>Login</button>");
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Navbar, {
props: { class: "custom-navbar" },
});
expect(result).toContain("navbar custom-navbar");
});
it("has proper grid structure", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Navbar);
expect(result).toContain('class="container"');
expect(result).toContain(
'class="row justify-content-between align-items-center"',
);
expect(result.match(/class="col-auto"/g)?.length).toBe(3);
});
it("passes through additional props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Navbar, {
props: { "data-nav": "main" },
});
expect(result).toContain('data-nav="main"');
});
});

View File

@ -0,0 +1,60 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import NavbarBrand from "../NavbarBrand.astro";
describe("NavbarBrand", () => {
it("renders with default props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(NavbarBrand, {
slots: { default: "Brand Name" },
});
expect(result).toContain('class="navbar-brand"');
expect(result).toContain("<a");
expect(result).toContain("Brand Name");
expect(result).toContain('href="/"');
});
it("applies custom href", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(NavbarBrand, {
props: { href: "/dashboard" },
slots: { default: "Dashboard" },
});
expect(result).toContain('href="/dashboard"');
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(NavbarBrand, {
props: { class: "custom-brand" },
slots: { default: "Brand" },
});
expect(result).toContain("navbar-brand custom-brand");
});
it("renders as anchor element", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(NavbarBrand, {
slots: { default: "Test" },
});
expect(result).toContain('<a class="navbar-brand"');
});
it("passes through additional props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(NavbarBrand, {
props: {
title: "Go to homepage",
"data-brand": "main",
},
slots: { default: "Home" },
});
expect(result).toContain('title="Go to homepage"');
expect(result).toContain('data-brand="main"');
});
});

View File

@ -0,0 +1,91 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import Progress from "../Progress.astro";
describe("Progress", () => {
it("renders with required value prop", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Progress, {
props: { value: 50 },
});
expect(result).toContain('class="progress"');
expect(result).toContain('class="progress-bar"');
});
it("calculates correct percentage with default max", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Progress, {
props: { value: 25 },
});
expect(result).toContain("width: 25%");
});
it("calculates correct percentage with custom max", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Progress, {
props: { value: 30, max: 60 },
});
expect(result).toContain("width: 50%");
});
it("caps percentage at 100% when value exceeds max", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Progress, {
props: { value: 150, max: 100 },
});
expect(result).toContain("width: 100%");
});
it("applies correct ARIA attributes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Progress, {
props: { value: 40, max: 80 },
});
expect(result).toContain('role="progressbar"');
expect(result).toContain('aria-valuenow="40"');
expect(result).toContain('aria-valuemin="0"');
expect(result).toContain('aria-valuemax="80"');
});
it("uses default max value of 100", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Progress, {
props: { value: 75 },
});
expect(result).toContain('aria-valuemax="100"');
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Progress, {
props: { value: 60, class: "custom-progress" },
});
expect(result).toContain("progress custom-progress");
});
it("handles zero value", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Progress, {
props: { value: 0 },
});
expect(result).toContain("width: 0%");
expect(result).toContain('aria-valuenow="0"');
});
it("passes through additional props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Progress, {
props: { value: 45, "data-progress": "loading" },
});
expect(result).toContain('data-progress="loading"');
});
});

View File

@ -0,0 +1,54 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import Row from "../Row.astro";
describe("Row", () => {
it("renders with default props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Row, {
slots: { default: "Row content" },
});
expect(result).toContain("Row content");
expect(result).toContain('class="row"');
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Row, {
props: { class: "custom-row" },
slots: { default: "Content" },
});
expect(result).toContain("row custom-row");
});
it("applies inline styles", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Row, {
props: { style: "margin-top: 20px;" },
slots: { default: "Content" },
});
expect(result).toContain('style="margin-top: 20px;"');
});
it("renders as div element", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Row, {
slots: { default: "Test" },
});
expect(result).toContain('<div class="row"');
});
it("passes through additional props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Row, {
props: { "data-row": "header" },
slots: { default: "Test" },
});
expect(result).toContain('data-row="header"');
});
});

View File

@ -0,0 +1,94 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import SearchBar from "../SearchBar.astro";
describe("SearchBar", () => {
it("renders with default props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(SearchBar);
expect(result).toContain('class="search-bar"');
expect(result).toContain('type="search"');
expect(result).toContain('class="search-icon"');
});
it("applies default placeholder", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(SearchBar);
expect(result).toContain('placeholder="Rechercher..."');
});
it("applies custom placeholder", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(SearchBar, {
props: { placeholder: "Search movies..." },
});
expect(result).toContain('placeholder="Search movies..."');
});
it("applies value prop", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(SearchBar, {
props: { value: "test query" },
});
expect(result).toContain('value="test query"');
});
it("applies default name and id", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(SearchBar);
expect(result).toContain('name="search"');
expect(result).toContain('id="search"');
});
it("applies custom name and id", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(SearchBar, {
props: {
name: "movie-search",
id: "movie-search-input",
},
});
expect(result).toContain('name="movie-search"');
expect(result).toContain('id="movie-search-input"');
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(SearchBar, {
props: { class: "custom-search" },
});
expect(result).toContain("search-bar custom-search");
});
it("renders search icon with correct attributes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(SearchBar);
expect(result).toContain('width="16"');
expect(result).toContain('height="16"');
expect(result).toContain('viewBox="0 0 24 24"');
});
it("input has search type", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(SearchBar);
expect(result).toContain('type="search"');
});
it("passes through additional props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(SearchBar, {
props: { "data-testid": "search-component" },
});
expect(result).toContain('data-testid="search-component"');
});
});

View File

@ -0,0 +1,71 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import Select from "../Select.astro";
describe("Select", () => {
it("renders with default props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Select, {
slots: {
default:
'<option value="1">Option 1</option><option value="2">Option 2</option>',
},
});
expect(result).toContain("<select");
expect(result).toContain('class="select"');
expect(result).toContain("Option 1");
expect(result).toContain("Option 2");
});
it("applies name and id attributes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Select, {
props: { name: "category", id: "category-select" },
});
expect(result).toContain('name="category"');
expect(result).toContain('id="category-select"');
});
it("applies disabled state", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Select, {
props: { disabled: true },
});
expect(result).toContain("disabled");
});
it("applies required attribute", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Select, {
props: { required: true },
});
expect(result).toContain("required");
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Select, {
props: { class: "custom-select" },
});
expect(result).toContain("select custom-select");
});
it("renders option content in slot", async () => {
const options =
'<option value="apple">Apple</option><option value="banana">Banana</option>';
const container = await AstroContainer.create();
const result = await container.renderToString(Select, {
slots: { default: options },
});
expect(result).toContain("Apple");
expect(result).toContain("Banana");
expect(result).toContain('value="apple"');
expect(result).toContain('value="banana"');
});
});

View File

@ -0,0 +1,71 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import Spinner from "../Spinner.astro";
describe("Spinner", () => {
it("renders with default props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Spinner);
expect(result).toContain('<div class="spinner"');
expect(result).toContain("></div>");
});
it("renders small size", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Spinner, {
props: { size: "sm" },
});
expect(result).toContain("spinner spinner-sm");
});
it("renders large size", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Spinner, {
props: { size: "lg" },
});
expect(result).toContain("spinner spinner-lg");
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Spinner, {
props: { class: "loading-spinner" },
});
expect(result).toContain("spinner loading-spinner");
});
it("applies custom classes with size", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Spinner, {
props: { size: "lg", class: "custom-spinner" },
});
expect(result).toContain("spinner spinner-lg custom-spinner");
});
it("renders as empty div", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Spinner);
expect(result).toContain("></div>");
// The div should be empty (no content between opening and closing tags, except for Astro metadata)
expect(result).toMatch(/<div[^>]*><\/div>/);
});
it("passes through additional props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Spinner, {
props: {
"data-testid": "loading-indicator",
"aria-label": "Loading content",
},
});
expect(result).toContain('data-testid="loading-indicator"');
expect(result).toContain('aria-label="Loading content"');
});
});

View File

@ -0,0 +1,78 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { describe, it, expect } from "vitest";
import Textarea from "../Textarea.astro";
describe("Textarea", () => {
it("renders with default props", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Textarea);
expect(result).toContain("<textarea");
expect(result).toContain('class="textarea"');
expect(result).toContain('rows="4"');
});
it("applies placeholder", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Textarea, {
props: { placeholder: "Enter your message" },
});
expect(result).toContain('placeholder="Enter your message"');
});
it("applies value", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Textarea, {
props: { value: "Initial text" },
});
expect(result).toContain("Initial text");
});
it("applies name and id attributes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Textarea, {
props: { name: "message", id: "message-input" },
});
expect(result).toContain('name="message"');
expect(result).toContain('id="message-input"');
});
it("applies custom rows", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Textarea, {
props: { rows: 8 },
});
expect(result).toContain('rows="8"');
});
it("applies disabled state", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Textarea, {
props: { disabled: true },
});
expect(result).toContain("disabled");
});
it("applies required attribute", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Textarea, {
props: { required: true },
});
expect(result).toContain("required");
});
it("applies custom classes", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Textarea, {
props: { class: "custom-textarea" },
});
expect(result).toContain("textarea custom-textarea");
});
});

View File

@ -0,0 +1,15 @@
import { describe, it, expect } from "vitest";
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import Button from "../Button.astro";
describe("Basic Astro Component Test", () => {
it("should render a simple button", async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Button, {
slots: { default: "Test Button" },
});
expect(result).toContain("Test Button");
expect(result).toContain("btn");
});
});

7
vitest.config.ts Normal file
View File

@ -0,0 +1,7 @@
import { getViteConfig } from 'astro/config';
export default getViteConfig({
test: {
environment: 'node'
}
});