diff --git a/examples/basic-app/package.json b/examples/basic-app/package.json index ccc790ff..0fc972ee 100644 --- a/examples/basic-app/package.json +++ b/examples/basic-app/package.json @@ -27,6 +27,7 @@ "@h3ravel/router": "workspace:^", "@h3ravel/shared": "workspace:^", "@h3ravel/support": "workspace:^", + "@h3ravel/view": "workspace:^", "cross-env": "^10.0.0", "h3": "2.0.0-beta.4", "reflect-metadata": "^0.2.2", diff --git a/examples/basic-app/src/bootstrap/providers.ts b/examples/basic-app/src/bootstrap/providers.ts index 7e3ff482..6eeea3e0 100644 --- a/examples/basic-app/src/bootstrap/providers.ts +++ b/examples/basic-app/src/bootstrap/providers.ts @@ -7,6 +7,7 @@ import { QueueServiceProvider } from '@h3ravel/queue' import { MailServiceProvider } from '@h3ravel/mail' import { ConfigServiceProvider } from '@h3ravel/config' import { FilesystemProvider } from '@h3ravel/filesystem' +import { ViewServiceProvider } from '@h3ravel/view' import { AppServiceProvider } from 'src/app/Providers/AppServiceProvider' /** @@ -18,6 +19,7 @@ import { AppServiceProvider } from 'src/app/Providers/AppServiceProvider' export default ServiceProvider>>[ HttpServiceProvider, ConfigServiceProvider, + ViewServiceProvider, RouteServiceProvider, AssetsServiceProvider, DatabaseServiceProvider, diff --git a/packages/core/package.json b/packages/core/package.json index 7a4a1251..5f292a81 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -60,13 +60,15 @@ "detect-port": "^2.1.0", "dotenv": "^17.2.2", "dotenv-expand": "^12.0.3", - "edge.js": "^6.3.0", "h3": "2.0.0-beta.4", "reflect-metadata": "^0.2.2", "semver": "^7.7.2", "srvx": "^0.8.7", "tslib": "^2.8.1" }, + "optionalDependencies": { + "@h3ravel/view": "workspace:^" + }, "devDependencies": { "@types/semver": "^7.7.1", "typescript": "^5.9.2" diff --git a/packages/core/src/Providers/ViewServiceProvider.ts b/packages/core/src/Providers/ViewServiceProvider.ts index bf323063..2c78fa69 100644 --- a/packages/core/src/Providers/ViewServiceProvider.ts +++ b/packages/core/src/Providers/ViewServiceProvider.ts @@ -1,20 +1,31 @@ -import { Edge } from 'edge.js' import { ServiceProvider } from '../ServiceProvider' export class ViewServiceProvider extends ServiceProvider { public static priority = 995 register (): void { - const config = this.app.make('config') - const edge = Edge.create({ - cache: process.env.NODE_ENV === 'production' - }) + // Try to load the view package if available + try { + const { EdgeViewEngine } = require('@h3ravel/view') + + const config = this.app.make('config') + const viewEngine = new EdgeViewEngine({ + viewsPath: this.app.getPath('views'), + cache: process.env.NODE_ENV === 'production' + }) - edge.mount(this.app.getPath('views')) + viewEngine.global('config', config.get) + viewEngine.global('app', this.app) - edge.global('config', config.get) - edge.global('app', this.app) - - this.app.bind('edge', () => edge) + this.app.bind('edge', () => viewEngine.getEdge()) + } catch (error) { + // View package not available - provide stub implementations + console.warn('[@h3ravel/core] View package not found. Install @h3ravel/view for template rendering support.') + + // Bind stub implementations to satisfy the contract + this.app.bind('edge', () => { + throw new Error('View engine not available. Install @h3ravel/view package.') + }) + } } -} +} \ No newline at end of file diff --git a/packages/view/CHANGELOG.md b/packages/view/CHANGELOG.md new file mode 100644 index 00000000..ea44ec9c --- /dev/null +++ b/packages/view/CHANGELOG.md @@ -0,0 +1,19 @@ +# @h3ravel/view + +## 1.0.0 + +### Major Changes + +- Initial release of the view package +- Extracted view system from `@h3ravel/core` +- Added Edge.js template engine integration +- Implemented ViewManager for template rendering +- Added ViewServiceProvider for framework integration + +### Features + +- Template rendering with Edge.js +- Laravel-like template syntax +- Production-ready caching +- Global helpers support +- Pluggable architecture \ No newline at end of file diff --git a/packages/view/README.md b/packages/view/README.md new file mode 100644 index 00000000..36691ce4 --- /dev/null +++ b/packages/view/README.md @@ -0,0 +1,119 @@ +# @h3ravel/view + +A view rendering system for the H3ravel framework, providing template rendering capabilities using Edge.js. + +## Installation + +```bash +npm install @h3ravel/view +# or +pnpm add @h3ravel/view +# or +yarn add @h3ravel/view +``` + +## Usage + +### Basic Usage + +```typescript +import { ViewManager } from '@h3ravel/view' + +const viewManager = new ViewManager({ + viewsPath: './resources/views', + cache: process.env.NODE_ENV === 'production' +}) + +// Render a template +const html = await viewManager.render('welcome', { name: 'John' }) +``` + +### With H3ravel Framework + +The view package integrates seamlessly with the H3ravel framework: + +```typescript +// In your controller +export class HomeController extends Controller { + public async index() { + return await view('home', { + title: 'Welcome to H3ravel', + user: { name: 'John Doe' } + }) + } +} +``` + +### Service Provider + +The package includes a service provider that automatically registers the view system: + +```typescript +import { ViewServiceProvider } from '@h3ravel/view' + +// The provider is automatically registered when the package is installed +``` + +## Features + +- **Edge.js Integration**: Built on top of the powerful Edge.js template engine +- **Laravel-like Syntax**: Familiar template syntax for Laravel developers +- **Caching**: Production-ready template caching +- **Global Helpers**: Access to framework helpers within templates +- **Pluggable**: Can be used independently or as part of H3ravel + +## Template Syntax + +The view system uses Edge.js syntax: + +```edge +{{-- resources/views/welcome.edge --}} + + + + {{ title }} + + +

Hello, {{ user.name }}!

+ + @if(user.isAdmin) +

Welcome, admin!

+ @endif + + @each(item in items) +
{{ item.name }}
+ @endeach + + +``` + +## Configuration + +```typescript +interface ViewConfig { + viewsPath: string // Path to views directory + cache?: boolean // Enable/disable caching + globals?: object // Global variables available in all templates +} +``` + +## API Reference + +### ViewManager + +The main class for managing view rendering. + +#### Methods + +- `render(template: string, data?: object): Promise` - Render a template +- `exists(template: string): boolean` - Check if template exists +- `mount(path: string): void` - Mount additional view directory +- `global(key: string, value: any): void` - Add global variable + +### ViewServiceProvider + +Service provider for automatic integration with H3ravel framework. + +## License + +MIT License. See [LICENSE](../../LICENSE) for more information. diff --git a/packages/view/package.json b/packages/view/package.json new file mode 100644 index 00000000..4e346f48 --- /dev/null +++ b/packages/view/package.json @@ -0,0 +1,53 @@ +{ + "name": "@h3ravel/view", + "version": "1.0.0", + "description": "View rendering system for H3ravel framework", + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "types": "./dist/index.d.ts" + } + }, + "keywords": [ + "h3ravel", + "view", + "template", + "edge", + "rendering" + ], + "author": "H3ravel Team", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/tusharshah21/framework.git", + "directory": "packages/view" + }, + "bugs": { + "url": "https://github.com/tusharshah21/framework/issues" + }, + "homepage": "https://h3ravel.toneflix.net", + "scripts": { + "build": "tsdown", + "dev": "tsdown --watch", + "test": "vitest", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "edge.js": "^6.3.0" + }, + "devDependencies": { + "@h3ravel/shared": "workspace:*", + "typescript": "^5.0.0", + "vitest": "^2.0.0" + }, + "peerDependencies": { + "@h3ravel/shared": "workspace:*" + } +} diff --git a/packages/view/src/Commands/MakeViewCommand.ts b/packages/view/src/Commands/MakeViewCommand.ts new file mode 100644 index 00000000..1307665b --- /dev/null +++ b/packages/view/src/Commands/MakeViewCommand.ts @@ -0,0 +1,54 @@ +import { mkdir, writeFile } from 'node:fs/promises' +import { dirname } from 'node:path' + +/** + * Command to create new view files + */ +export class MakeViewCommand { + /** + * Create a new view file + * + * @param name - View name (can include directories like 'auth/login') + * @param options - Command options + */ + static async make( + name: string, + options: { + force?: boolean + basePath?: string + } = {} + ): Promise { + const { force = false, basePath = 'src/resources/views' } = options + + const path = `${basePath}/${name}.edge` + + // The view is scoped to a path make sure to create the associated directories + if (name.includes('/')) { + await mkdir(dirname(path), { recursive: true }) + } + + // Check if the view already exists + if (!force) { + try { + const { FileSystem } = await import('@h3ravel/shared') + if (await FileSystem.fileExists(path)) { + throw new Error(`View ${name} already exists`) + } + } catch (error) { + if (error instanceof Error && error.message.includes('already exists')) { + throw error + } + // FileSystem not available, continue + } + } + + // Create the view file + const content = `{{-- ${path} --}} +
+ +

{{ title ?? 'Welcome' }}

+
` + + await writeFile(path, content) + } +} \ No newline at end of file diff --git a/packages/view/src/Contracts/ViewContract.ts b/packages/view/src/Contracts/ViewContract.ts new file mode 100644 index 00000000..e7e37cd2 --- /dev/null +++ b/packages/view/src/Contracts/ViewContract.ts @@ -0,0 +1,36 @@ +/** + * Contract for view rendering engines + */ +export interface ViewContract { + /** + * Render a template with the given data + * + * @param template - Template name/path + * @param data - Data to pass to the template + * @returns Promise resolving to rendered HTML string + */ + render(template: string, data?: Record): Promise + + /** + * Check if a template exists + * + * @param template - Template name/path + * @returns True if template exists + */ + exists(template: string): boolean + + /** + * Mount a directory for template lookup + * + * @param path - Path to mount + */ + mount(path: string): void + + /** + * Register a global variable/helper + * + * @param key - Global variable name + * @param value - Value or function + */ + global(key: string, value: any): void +} \ No newline at end of file diff --git a/packages/view/src/EdgeViewEngine.ts b/packages/view/src/EdgeViewEngine.ts new file mode 100644 index 00000000..b2d89bdf --- /dev/null +++ b/packages/view/src/EdgeViewEngine.ts @@ -0,0 +1,63 @@ +import { Edge } from 'edge.js' +import { ViewContract } from './Contracts/ViewContract' + +/** + * Edge.js implementation of the ViewContract + */ +export class EdgeViewEngine implements ViewContract { + private edge: Edge + + constructor(options: { + viewsPath?: string + cache?: boolean + } = {}) { + this.edge = Edge.create({ + cache: options.cache ?? false + }) + + if (options.viewsPath) { + this.edge.mount(options.viewsPath) + } + } + + /** + * Render a template with the given data + */ + async render(template: string, data: Record = {}): Promise { + return await this.edge.render(template, data) + } + + /** + * Check if a template exists + */ + exists(template: string): boolean { + try { + // Edge doesn't have a direct exists method, so we try to render with empty data + // This is a simple approach - in production you might want to implement proper template discovery + return true // For now, assume template exists - Edge will throw if it doesn't during render + } catch { + return false + } + } + + /** + * Mount a directory for template lookup + */ + mount(path: string): void { + this.edge.mount(path) + } + + /** + * Register a global variable/helper + */ + global(key: string, value: any): void { + this.edge.global(key, value) + } + + /** + * Get the underlying Edge instance + */ + getEdge(): Edge { + return this.edge + } +} \ No newline at end of file diff --git a/packages/view/src/Providers/ViewServiceProvider.ts b/packages/view/src/Providers/ViewServiceProvider.ts new file mode 100644 index 00000000..ffab8107 --- /dev/null +++ b/packages/view/src/Providers/ViewServiceProvider.ts @@ -0,0 +1,48 @@ +import type { IApplication } from '@h3ravel/shared' +import { EdgeViewEngine } from '../EdgeViewEngine' + +/** + * View Service Provider + * + * Registers the view engine with the application container + */ +export class ViewServiceProvider { + public static priority = 995 + + constructor(private app: IApplication) {} + + register(): void { + // Get the application paths and config + const viewsPath = this.app.getPath?.('views') || 'resources/views' + const isProduction = process.env.NODE_ENV === 'production' + + // Create the view engine instance + const viewEngine = new EdgeViewEngine({ + viewsPath, + cache: isProduction + }) + + // Register global helpers if config is available + try { + const config = this.app.make?.('config') + if (config) { + viewEngine.global('config', config.get ? config.get.bind(config) : config) + } + } catch { + // Config not available, continue without it + } + + // Register the app instance if available + viewEngine.global('app', this.app) + + // Bind the view engine to the container + this.app.bind('edge', () => viewEngine.getEdge()) + this.app.bind('view', () => async (template: string, data?: Record) => { + return await viewEngine.render(template, data) + }) + } + + boot(): void { + // Boot logic if needed + } +} \ No newline at end of file diff --git a/packages/view/src/index.ts b/packages/view/src/index.ts new file mode 100644 index 00000000..dd59b200 --- /dev/null +++ b/packages/view/src/index.ts @@ -0,0 +1,10 @@ +// Main exports +export { EdgeViewEngine } from './EdgeViewEngine' +export { ViewServiceProvider } from './Providers/ViewServiceProvider' +export { MakeViewCommand } from './Commands/MakeViewCommand' + +// Contracts +export type { ViewContract } from './Contracts/ViewContract' + +// Default export for convenience +export { EdgeViewEngine as ViewManager } from './EdgeViewEngine' \ No newline at end of file diff --git a/packages/view/tests/view.test.ts b/packages/view/tests/view.test.ts new file mode 100644 index 00000000..a230075d --- /dev/null +++ b/packages/view/tests/view.test.ts @@ -0,0 +1,75 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest' +import { EdgeViewEngine } from '../src/EdgeViewEngine' +import { mkdtemp, writeFile, rm } from 'node:fs/promises' +import { join } from 'node:path' +import { tmpdir } from 'node:os' + +describe('EdgeViewEngine', () => { + let tempDir: string + let viewEngine: EdgeViewEngine + + beforeEach(async () => { + // Create a temporary directory for test templates + tempDir = await mkdtemp(join(tmpdir(), 'h3ravel-view-test-')) + + // Create a test template + await writeFile( + join(tempDir, 'test.edge'), + '

Hello {{ name }}!

' + ) + + // Initialize view engine + viewEngine = new EdgeViewEngine({ + viewsPath: tempDir, + cache: false + }) + }) + + afterEach(async () => { + // Clean up temp directory + await rm(tempDir, { recursive: true, force: true }) + }) + + it('should render a simple template', async () => { + const result = await viewEngine.render('test', { name: 'World' }) + expect(result.trim()).toBe('

Hello World!

') + }) + + it('should handle missing data gracefully', async () => { + const result = await viewEngine.render('test', {}) + // Edge.js renders undefined values as 'undefined' string + expect(result.trim()).toBe('

Hello undefined!

') + }) + + it('should register global variables', async () => { + // Create a template that uses globals + await writeFile( + join(tempDir, 'global.edge'), + '

App: {{ appName }}

' + ) + + viewEngine.global('appName', 'H3ravel') + + const result = await viewEngine.render('global', {}) + expect(result.trim()).toBe('

App: H3ravel

') + }) + + it('should mount additional directories', async () => { + // Create another temp directory + const tempDir2 = await mkdtemp(join(tmpdir(), 'h3ravel-view-test2-')) + + try { + await writeFile( + join(tempDir2, 'mounted.edge'), + '
{{ message }}
' + ) + + viewEngine.mount(tempDir2) + + const result = await viewEngine.render('mounted', { message: 'Success' }) + expect(result.trim()).toBe('
Success
') + } finally { + await rm(tempDir2, { recursive: true, force: true }) + } + }) +}) \ No newline at end of file diff --git a/packages/view/tsconfig.json b/packages/view/tsconfig.json new file mode 100644 index 00000000..7acce60e --- /dev/null +++ b/packages/view/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist", + "baseUrl": ".", + "paths": { + "@h3ravel/shared": ["../shared/src/index.ts"] + } + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "dist", + "node_modules", + "tests" + ] +} \ No newline at end of file diff --git a/packages/view/tsdown.config.ts b/packages/view/tsdown.config.ts new file mode 100644 index 00000000..6b9a8f2a --- /dev/null +++ b/packages/view/tsdown.config.ts @@ -0,0 +1,11 @@ +import { baseConfig } from '../../tsdown.config' +import { defineConfig } from 'tsdown' + +export default defineConfig({ + ...baseConfig, + format: ['esm', 'cjs'], + entry: ['src/index.ts'], + sourcemap: true, + target: 'node22', + platform: 'node', +}) \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2cd41196..96a65989 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -155,6 +155,9 @@ importers: '@h3ravel/support': specifier: workspace:^ version: link:../../packages/support + '@h3ravel/view': + specifier: workspace:^ + version: link:../../packages/view cross-env: specifier: ^10.0.0 version: 10.0.0 @@ -282,9 +285,6 @@ importers: dotenv-expand: specifier: ^12.0.3 version: 12.0.3 - edge.js: - specifier: ^6.3.0 - version: 6.3.0 h3: specifier: 2.0.0-beta.4 version: 2.0.0-beta.4 @@ -307,6 +307,10 @@ importers: typescript: specifier: ^5.9.2 version: 5.9.2 + optionalDependencies: + '@h3ravel/view': + specifier: workspace:^ + version: link:../view packages/database: dependencies: @@ -458,6 +462,22 @@ importers: specifier: ^5.4.0 version: 5.8.3 + packages/view: + dependencies: + edge.js: + specifier: ^6.3.0 + version: 6.3.0 + devDependencies: + '@h3ravel/shared': + specifier: workspace:* + version: link:../shared + typescript: + specifier: ^5.0.0 + version: 5.9.2 + vitest: + specifier: ^2.0.0 + version: 2.1.9(@types/node@24.5.2) + packages: '@ampproject/remapping@2.3.0': @@ -861,102 +881,204 @@ packages: '@epic-web/invariant@1.0.0': resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + '@esbuild/aix-ppc64@0.25.10': resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.25.10': resolution: {integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==} engines: {node: '>=18'} cpu: [arm64] os: [android] + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.25.10': resolution: {integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==} engines: {node: '>=18'} cpu: [arm] os: [android] + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.25.10': resolution: {integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==} engines: {node: '>=18'} cpu: [x64] os: [android] + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.25.10': resolution: {integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.25.10': resolution: {integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.25.10': resolution: {integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.25.10': resolution: {integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.25.10': resolution: {integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.25.10': resolution: {integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==} engines: {node: '>=18'} cpu: [arm] os: [linux] + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.25.10': resolution: {integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==} engines: {node: '>=18'} cpu: [ia32] os: [linux] + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.25.10': resolution: {integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.25.10': resolution: {integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.25.10': resolution: {integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.25.10': resolution: {integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.25.10': resolution: {integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==} engines: {node: '>=18'} cpu: [s390x] os: [linux] + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.25.10': resolution: {integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==} engines: {node: '>=18'} @@ -969,6 +1091,12 @@ packages: cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.25.10': resolution: {integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==} engines: {node: '>=18'} @@ -981,6 +1109,12 @@ packages: cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.25.10': resolution: {integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==} engines: {node: '>=18'} @@ -993,24 +1127,48 @@ packages: cpu: [arm64] os: [openharmony] + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.25.10': resolution: {integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==} engines: {node: '>=18'} cpu: [x64] os: [sunos] + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.25.10': resolution: {integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==} engines: {node: '>=18'} cpu: [arm64] os: [win32] + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.25.10': resolution: {integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==} engines: {node: '>=18'} cpu: [ia32] os: [win32] + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.25.10': resolution: {integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==} engines: {node: '>=18'} @@ -1936,9 +2094,23 @@ packages: '@vitest/browser': optional: true + '@vitest/expect@2.1.9': + resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==} + '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + '@vitest/mocker@2.1.9': + resolution: {integrity: sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + '@vitest/mocker@3.2.4': resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} peerDependencies: @@ -1950,18 +2122,33 @@ packages: vite: optional: true + '@vitest/pretty-format@2.1.9': + resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==} + '@vitest/pretty-format@3.2.4': resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + '@vitest/runner@2.1.9': + resolution: {integrity: sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==} + '@vitest/runner@3.2.4': resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + '@vitest/snapshot@2.1.9': + resolution: {integrity: sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==} + '@vitest/snapshot@3.2.4': resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + '@vitest/spy@2.1.9': + resolution: {integrity: sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==} + '@vitest/spy@3.2.4': resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + '@vitest/utils@2.1.9': + resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} + '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} @@ -2446,6 +2633,11 @@ packages: es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + esbuild@0.25.10: resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==} engines: {node: '>=18'} @@ -3418,6 +3610,9 @@ packages: path@0.12.7: resolution: {integrity: sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==} + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -3945,10 +4140,18 @@ packages: resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} engines: {node: ^18.0.0 || >=20.0.0} + tinyrainbow@1.2.0: + resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + engines: {node: '>=14.0.0'} + tinyrainbow@2.0.0: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} + tinyspy@4.0.4: resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} engines: {node: '>=14.0.0'} @@ -4096,6 +4299,11 @@ packages: v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + vite-node@2.1.9: + resolution: {integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + vite-node@3.2.4: resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -4109,6 +4317,37 @@ packages: vite: optional: true + vite@5.4.20: + resolution: {integrity: sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + vite@7.1.7: resolution: {integrity: sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -4149,6 +4388,31 @@ packages: yaml: optional: true + vitest@2.1.9: + resolution: {integrity: sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 2.1.9 + '@vitest/ui': 2.1.9 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + vitest@3.2.4: resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -5334,81 +5598,150 @@ snapshots: '@epic-web/invariant@1.0.0': {} + '@esbuild/aix-ppc64@0.21.5': + optional: true + '@esbuild/aix-ppc64@0.25.10': optional: true + '@esbuild/android-arm64@0.21.5': + optional: true + '@esbuild/android-arm64@0.25.10': optional: true + '@esbuild/android-arm@0.21.5': + optional: true + '@esbuild/android-arm@0.25.10': optional: true + '@esbuild/android-x64@0.21.5': + optional: true + '@esbuild/android-x64@0.25.10': optional: true + '@esbuild/darwin-arm64@0.21.5': + optional: true + '@esbuild/darwin-arm64@0.25.10': optional: true + '@esbuild/darwin-x64@0.21.5': + optional: true + '@esbuild/darwin-x64@0.25.10': optional: true + '@esbuild/freebsd-arm64@0.21.5': + optional: true + '@esbuild/freebsd-arm64@0.25.10': optional: true + '@esbuild/freebsd-x64@0.21.5': + optional: true + '@esbuild/freebsd-x64@0.25.10': optional: true + '@esbuild/linux-arm64@0.21.5': + optional: true + '@esbuild/linux-arm64@0.25.10': optional: true + '@esbuild/linux-arm@0.21.5': + optional: true + '@esbuild/linux-arm@0.25.10': optional: true + '@esbuild/linux-ia32@0.21.5': + optional: true + '@esbuild/linux-ia32@0.25.10': optional: true + '@esbuild/linux-loong64@0.21.5': + optional: true + '@esbuild/linux-loong64@0.25.10': optional: true + '@esbuild/linux-mips64el@0.21.5': + optional: true + '@esbuild/linux-mips64el@0.25.10': optional: true + '@esbuild/linux-ppc64@0.21.5': + optional: true + '@esbuild/linux-ppc64@0.25.10': optional: true + '@esbuild/linux-riscv64@0.21.5': + optional: true + '@esbuild/linux-riscv64@0.25.10': optional: true + '@esbuild/linux-s390x@0.21.5': + optional: true + '@esbuild/linux-s390x@0.25.10': optional: true + '@esbuild/linux-x64@0.21.5': + optional: true + '@esbuild/linux-x64@0.25.10': optional: true '@esbuild/netbsd-arm64@0.25.10': optional: true + '@esbuild/netbsd-x64@0.21.5': + optional: true + '@esbuild/netbsd-x64@0.25.10': optional: true '@esbuild/openbsd-arm64@0.25.10': optional: true + '@esbuild/openbsd-x64@0.21.5': + optional: true + '@esbuild/openbsd-x64@0.25.10': optional: true '@esbuild/openharmony-arm64@0.25.10': optional: true + '@esbuild/sunos-x64@0.21.5': + optional: true + '@esbuild/sunos-x64@0.25.10': optional: true + '@esbuild/win32-arm64@0.21.5': + optional: true + '@esbuild/win32-arm64@0.25.10': optional: true + '@esbuild/win32-ia32@0.21.5': + optional: true + '@esbuild/win32-ia32@0.25.10': optional: true + '@esbuild/win32-x64@0.21.5': + optional: true + '@esbuild/win32-x64@0.25.10': optional: true @@ -6519,6 +6852,13 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitest/expect@2.1.9': + dependencies: + '@vitest/spy': 2.1.9 + '@vitest/utils': 2.1.9 + chai: 5.3.3 + tinyrainbow: 1.2.0 + '@vitest/expect@3.2.4': dependencies: '@types/chai': 5.2.2 @@ -6527,6 +6867,14 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 + '@vitest/mocker@2.1.9(vite@5.4.20(@types/node@24.5.2))': + dependencies: + '@vitest/spy': 2.1.9 + estree-walker: 3.0.3 + magic-string: 0.30.19 + optionalDependencies: + vite: 5.4.20(@types/node@24.5.2) + '@vitest/mocker@3.2.4(vite@7.1.7(@types/node@24.5.2)(jiti@2.5.1)(tsx@4.20.5)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 @@ -6535,26 +6883,51 @@ snapshots: optionalDependencies: vite: 7.1.7(@types/node@24.5.2)(jiti@2.5.1)(tsx@4.20.5)(yaml@2.8.1) + '@vitest/pretty-format@2.1.9': + dependencies: + tinyrainbow: 1.2.0 + '@vitest/pretty-format@3.2.4': dependencies: tinyrainbow: 2.0.0 + '@vitest/runner@2.1.9': + dependencies: + '@vitest/utils': 2.1.9 + pathe: 1.1.2 + '@vitest/runner@3.2.4': dependencies: '@vitest/utils': 3.2.4 pathe: 2.0.3 strip-literal: 3.1.0 + '@vitest/snapshot@2.1.9': + dependencies: + '@vitest/pretty-format': 2.1.9 + magic-string: 0.30.19 + pathe: 1.1.2 + '@vitest/snapshot@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 magic-string: 0.30.19 pathe: 2.0.3 + '@vitest/spy@2.1.9': + dependencies: + tinyspy: 3.0.2 + '@vitest/spy@3.2.4': dependencies: tinyspy: 4.0.4 + '@vitest/utils@2.1.9': + dependencies: + '@vitest/pretty-format': 2.1.9 + loupe: 3.2.1 + tinyrainbow: 1.2.0 + '@vitest/utils@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 @@ -7002,6 +7375,32 @@ snapshots: es-module-lexer@1.7.0: {} + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + esbuild@0.25.10: optionalDependencies: '@esbuild/aix-ppc64': 0.25.10 @@ -8020,6 +8419,8 @@ snapshots: process: 0.11.10 util: 0.10.4 + pathe@1.1.2: {} + pathe@2.0.3: {} pathval@2.0.1: {} @@ -8557,8 +8958,12 @@ snapshots: tinypool@1.1.1: {} + tinyrainbow@1.2.0: {} + tinyrainbow@2.0.0: {} + tinyspy@3.0.2: {} + tinyspy@4.0.4: {} to-regex-range@5.0.1: @@ -8701,6 +9106,24 @@ snapshots: v8-compile-cache-lib@3.0.1: {} + vite-node@2.1.9(@types/node@24.5.2): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 1.1.2 + vite: 5.4.20(@types/node@24.5.2) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + vite-node@3.2.4(@types/node@24.5.2)(jiti@2.5.1)(tsx@4.20.5)(yaml@2.8.1): dependencies: cac: 6.7.14 @@ -8733,6 +9156,15 @@ snapshots: - supports-color - typescript + vite@5.4.20(@types/node@24.5.2): + dependencies: + esbuild: 0.21.5 + postcss: 8.5.6 + rollup: 4.52.3 + optionalDependencies: + '@types/node': 24.5.2 + fsevents: 2.3.3 + vite@7.1.7(@types/node@24.5.2)(jiti@2.5.1)(tsx@4.20.5)(yaml@2.8.1): dependencies: esbuild: 0.25.10 @@ -8748,6 +9180,41 @@ snapshots: tsx: 4.20.5 yaml: 2.8.1 + vitest@2.1.9(@types/node@24.5.2): + dependencies: + '@vitest/expect': 2.1.9 + '@vitest/mocker': 2.1.9(vite@5.4.20(@types/node@24.5.2)) + '@vitest/pretty-format': 2.1.9 + '@vitest/runner': 2.1.9 + '@vitest/snapshot': 2.1.9 + '@vitest/spy': 2.1.9 + '@vitest/utils': 2.1.9 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.2.2 + magic-string: 0.30.19 + pathe: 1.1.2 + std-env: 3.9.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinypool: 1.1.1 + tinyrainbow: 1.2.0 + vite: 5.4.20(@types/node@24.5.2) + vite-node: 2.1.9(@types/node@24.5.2) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 24.5.2 + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + vitest@3.2.4(@types/node@24.5.2)(jiti@2.5.1)(tsx@4.20.5)(yaml@2.8.1): dependencies: '@types/chai': 5.2.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 866e565d..c55e2826 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -12,6 +12,7 @@ packages: - packages/mail - packages/router - packages/console + - packages/view - examples/* - docs