diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c9b357c --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# Rust +target/ +Cargo.lock + +# Node +node_modules/ +.turbo/ +dist/ +build/ +.svelte-kit/ + +# Environment +.env +.env.local +.env.*.local + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Docker +docker/data/ diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b3a8dbe --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,52 @@ +[workspace] +resolver = "2" +members = [ + "crates/pvm-api", + "crates/pvm-core", + "crates/pvm-auth", + "crates/pvm-types", +] + +[workspace.package] +version = "0.1.0" +edition = "2024" +license = "UNLICENSED" + +[workspace.dependencies] +# Async runtime +tokio = { version = "1", features = ["full"] } + +# Web framework +axum = { version = "0.8", features = ["ws", "macros"] } +axum-extra = { version = "0.10", features = ["typed-header", "cookie"] } +tower = "0.5" +tower-http = { version = "0.6", features = ["cors", "trace", "compression-gzip"] } + +# Serialization +serde = { version = "1", features = ["derive"] } +serde_json = "1" + +# Auth / JWT +jsonwebtoken = "9" + +# Database +sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "sqlite", "chrono", "uuid"] } + +# Logging / Tracing +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] } + +# Error handling +thiserror = "2" +anyhow = "1" + +# OpenAPI +utoipa = { version = "5", features = ["axum_extras", "uuid", "chrono"] } +utoipa-axum = "0.2" +utoipa-swagger-ui = { version = "9", features = ["axum"] } + +# Misc +chrono = { version = "0.4", features = ["serde"] } +uuid = { version = "1", features = ["v4", "serde"] } +dotenvy = "0.15" +reqwest = { version = "0.12", features = ["json"] } diff --git a/apps/dashboard/.env.example b/apps/dashboard/.env.example new file mode 100644 index 0000000..1287633 --- /dev/null +++ b/apps/dashboard/.env.example @@ -0,0 +1,16 @@ +# Zitadel OIDC Configuration +AUTH_ZITADEL_ISSUER=https://auth.pvm.example.com +AUTH_ZITADEL_CLIENT_ID=your-client-id +AUTH_ZITADEL_CLIENT_SECRET=your-client-secret + +# Auth.js secret (generate with: openssl rand -base64 32) +AUTH_SECRET=your-auth-secret + +# Backend API URL +PUBLIC_API_URL=http://localhost:3001 + +# Zitadel account management URL (for password/MFA changes) +PUBLIC_ZITADEL_ACCOUNT_URL=https://auth.pvm.example.com/ui/console + +# App URL (for OIDC redirects) +ORIGIN=http://localhost:5173 diff --git a/apps/dashboard/.gitignore b/apps/dashboard/.gitignore new file mode 100644 index 0000000..3b462cb --- /dev/null +++ b/apps/dashboard/.gitignore @@ -0,0 +1,23 @@ +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/apps/dashboard/.npmrc b/apps/dashboard/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/apps/dashboard/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/apps/dashboard/README.md b/apps/dashboard/README.md new file mode 100644 index 0000000..dd3edc7 --- /dev/null +++ b/apps/dashboard/README.md @@ -0,0 +1,42 @@ +# sv + +Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). + +## Creating a project + +If you're seeing this, you've probably already done this step. Congrats! + +```sh +# create a new project +npx sv create my-app +``` + +To recreate this project with the same configuration: + +```sh +# recreate this project +npx sv create --template minimal --types ts --no-install dashboard +``` + +## Developing + +Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: + +```sh +npm run dev + +# or start the server and open the app in a new browser tab +npm run dev -- --open +``` + +## Building + +To create a production version of your app: + +```sh +npm run build +``` + +You can preview the production build with `npm run preview`. + +> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json new file mode 100644 index 0000000..85bdebb --- /dev/null +++ b/apps/dashboard/package.json @@ -0,0 +1,30 @@ +{ + "name": "dashboard", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" + }, + "devDependencies": { + "@sveltejs/adapter-auto": "^7.0.0", + "@sveltejs/adapter-node": "^5.0.0", + "@sveltejs/kit": "^2.50.2", + "@sveltejs/vite-plugin-svelte": "^6.2.4", + "@tailwindcss/vite": "^4.0.0", + "svelte": "^5.49.2", + "svelte-check": "^4.3.6", + "typescript": "^5.9.3", + "vite": "^7.3.1" + }, + "dependencies": { + "@auth/core": "^0.38.0", + "@auth/sveltekit": "^1.7.0", + "tailwindcss": "^4.0.0" + } +} diff --git a/apps/dashboard/src/app.css b/apps/dashboard/src/app.css new file mode 100644 index 0000000..b8029e2 --- /dev/null +++ b/apps/dashboard/src/app.css @@ -0,0 +1,38 @@ +@import "tailwindcss"; + +@theme { + --color-primary: #1e40af; + --color-primary-light: #3b82f6; + --color-primary-dark: #1e3a8a; + --color-surface: #ffffff; + --color-surface-dark: #0f172a; + --color-surface-card: #f8fafc; + --color-surface-card-dark: #1e293b; + --color-surface-sidebar: #f1f5f9; + --color-surface-sidebar-dark: #0f172a; + --color-border: #e2e8f0; + --color-border-dark: #334155; + --color-text: #0f172a; + --color-text-dark: #f1f5f9; + --color-text-muted: #64748b; + --color-text-muted-dark: #94a3b8; + --color-accent: #10b981; + --color-danger: #ef4444; +} + +:root { + color-scheme: light dark; +} + +body { + font-family: 'Inter', system-ui, -apple-system, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +@media (prefers-color-scheme: dark) { + body { + background-color: var(--color-surface-dark); + color: var(--color-text-dark); + } +} diff --git a/apps/dashboard/src/app.d.ts b/apps/dashboard/src/app.d.ts new file mode 100644 index 0000000..8334603 --- /dev/null +++ b/apps/dashboard/src/app.d.ts @@ -0,0 +1,27 @@ +// See https://svelte.dev/docs/kit/types#app.d.ts + +declare module '@auth/sveltekit' { + interface Session { + accessToken?: string; + } +} + +declare module '@auth/core/jwt' { + interface JWT { + accessToken?: string; + refreshToken?: string; + expiresAt?: number; + } +} + +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {}; diff --git a/apps/dashboard/src/app.html b/apps/dashboard/src/app.html new file mode 100644 index 0000000..f273cc5 --- /dev/null +++ b/apps/dashboard/src/app.html @@ -0,0 +1,11 @@ + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/apps/dashboard/src/auth.ts b/apps/dashboard/src/auth.ts new file mode 100644 index 0000000..9cd616d --- /dev/null +++ b/apps/dashboard/src/auth.ts @@ -0,0 +1,45 @@ +import { SvelteKitAuth } from '@auth/sveltekit'; +import type { Provider } from '@auth/core/providers'; +import { env } from '$env/dynamic/private'; + +function zitadelProvider(): Provider { + return { + id: 'zitadel', + name: 'Zitadel', + type: 'oidc', + issuer: env.AUTH_ZITADEL_ISSUER, + clientId: env.AUTH_ZITADEL_CLIENT_ID, + clientSecret: env.AUTH_ZITADEL_CLIENT_SECRET, + authorization: { + params: { + scope: 'openid profile email offline_access' + } + }, + checks: ['pkce', 'state'], + client: { + token_endpoint_auth_method: 'client_secret_basic' + } + }; +} + +export const { handle, signIn, signOut } = SvelteKitAuth({ + providers: [zitadelProvider()], + callbacks: { + async jwt({ token, account }) { + if (account) { + token.accessToken = account.access_token; + token.refreshToken = account.refresh_token; + token.expiresAt = account.expires_at; + } + return token; + }, + async session({ session, token }) { + session.accessToken = token.accessToken as string; + return session; + } + }, + pages: { + signIn: '/auth/signin' + }, + trustHost: true +}); diff --git a/apps/dashboard/src/hooks.server.ts b/apps/dashboard/src/hooks.server.ts new file mode 100644 index 0000000..e6726b0 --- /dev/null +++ b/apps/dashboard/src/hooks.server.ts @@ -0,0 +1 @@ +export { handle } from './auth'; diff --git a/apps/dashboard/src/lib/api.ts b/apps/dashboard/src/lib/api.ts new file mode 100644 index 0000000..6878c57 --- /dev/null +++ b/apps/dashboard/src/lib/api.ts @@ -0,0 +1,62 @@ +const API_BASE = import.meta.env.PUBLIC_API_URL || 'http://localhost:3001'; + +export class ApiClient { + private token: string; + + constructor(token: string) { + this.token = token; + } + + private async request(path: string, options?: RequestInit): Promise { + const res = await fetch(`${API_BASE}${path}`, { + ...options, + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.token}`, + ...options?.headers + } + }); + + if (!res.ok) { + throw new ApiError(res.status, await res.text()); + } + + return res.json(); + } + + async get(path: string): Promise { + return this.request(path); + } + + async post(path: string, body?: unknown): Promise { + return this.request(path, { + method: 'POST', + body: body ? JSON.stringify(body) : undefined + }); + } + + async put(path: string, body?: unknown): Promise { + return this.request(path, { + method: 'PUT', + body: body ? JSON.stringify(body) : undefined + }); + } + + async delete(path: string): Promise { + return this.request(path, { method: 'DELETE' }); + } +} + +export class ApiError extends Error { + status: number; + + constructor(status: number, message: string) { + super(message); + this.status = status; + this.name = 'ApiError'; + } +} + +export function createApiClient(token: string): ApiClient { + return new ApiClient(token); +} diff --git a/apps/dashboard/src/lib/assets/favicon.svg b/apps/dashboard/src/lib/assets/favicon.svg new file mode 100644 index 0000000..cc5dc66 --- /dev/null +++ b/apps/dashboard/src/lib/assets/favicon.svg @@ -0,0 +1 @@ +svelte-logo \ No newline at end of file diff --git a/apps/dashboard/src/lib/index.ts b/apps/dashboard/src/lib/index.ts new file mode 100644 index 0000000..856f2b6 --- /dev/null +++ b/apps/dashboard/src/lib/index.ts @@ -0,0 +1 @@ +// place files you want to import through the `$lib` alias in this folder. diff --git a/apps/dashboard/src/routes/+layout.server.ts b/apps/dashboard/src/routes/+layout.server.ts new file mode 100644 index 0000000..790f996 --- /dev/null +++ b/apps/dashboard/src/routes/+layout.server.ts @@ -0,0 +1,6 @@ +import type { LayoutServerLoad } from './$types'; + +export const load: LayoutServerLoad = async (event) => { + const session = await event.locals.auth(); + return { session }; +}; diff --git a/apps/dashboard/src/routes/+layout.svelte b/apps/dashboard/src/routes/+layout.svelte new file mode 100644 index 0000000..f322522 --- /dev/null +++ b/apps/dashboard/src/routes/+layout.svelte @@ -0,0 +1,13 @@ + + + + + PVM - Poker Venue Manager + + +{@render children()} diff --git a/apps/dashboard/src/routes/+page.svelte b/apps/dashboard/src/routes/+page.svelte new file mode 100644 index 0000000..5a4262b --- /dev/null +++ b/apps/dashboard/src/routes/+page.svelte @@ -0,0 +1,90 @@ + + +
+ + +
+

+ Manage your poker venue + with ease +

+

+ PVM helps poker rooms run smoother — manage tournaments, track players, + handle waitlists, and display live information. All from one dashboard. +

+ {#if session} + + Go to Dashboard + + {:else} + + {/if} + +
+
+
+ + + +
+

Player Management

+

Track players, manage waitlists, and keep everyone in the game.

+
+
+
+ + + +
+

Tournaments

+

Create and manage tournaments with automatic blind structures and payouts.

+
+
+
+ + + +
+

Live Displays

+

Show live tournament info, waitlists, and announcements on venue screens.

+
+
+
+
diff --git a/apps/dashboard/src/routes/auth/signin/+page.svelte b/apps/dashboard/src/routes/auth/signin/+page.svelte new file mode 100644 index 0000000..18860b6 --- /dev/null +++ b/apps/dashboard/src/routes/auth/signin/+page.svelte @@ -0,0 +1,83 @@ + + +
+
+
+ +
+ PVM +
+
+

Sign in to PVM

+

Poker Venue Manager

+
+ +
+ + + +
+
+
+
+
+ Social providers via Zitadel +
+
+ +
+ + + + + +
+ +

+ Social providers are configured through Zitadel. + All sign-in methods redirect to your identity provider. +

+
+ +

+ ← Back to home +

+
+
diff --git a/apps/dashboard/src/routes/dashboard/+layout.server.ts b/apps/dashboard/src/routes/dashboard/+layout.server.ts new file mode 100644 index 0000000..b1b21b7 --- /dev/null +++ b/apps/dashboard/src/routes/dashboard/+layout.server.ts @@ -0,0 +1,10 @@ +import { redirect } from '@sveltejs/kit'; +import type { LayoutServerLoad } from './$types'; + +export const load: LayoutServerLoad = async (event) => { + const session = await event.locals.auth(); + if (!session) { + throw redirect(303, '/auth/signin'); + } + return { session }; +}; diff --git a/apps/dashboard/src/routes/dashboard/+layout.svelte b/apps/dashboard/src/routes/dashboard/+layout.svelte new file mode 100644 index 0000000..8d4d780 --- /dev/null +++ b/apps/dashboard/src/routes/dashboard/+layout.svelte @@ -0,0 +1,151 @@ + + +
+ + {#if sidebarOpen} +
(sidebarOpen = false)} + onkeydown={(e) => e.key === 'Escape' && (sidebarOpen = false)} + role="button" + tabindex="-1" + aria-label="Close sidebar" + >
+ {/if} + + + + + +
+ +
+
+ + +
+ +
+ + {#if session?.user?.image} + Avatar + {:else} +
+ {session?.user?.name?.charAt(0)?.toUpperCase() || '?'} +
+ {/if} + +
+
+
+ + +
+ {@render children()} +
+
+
diff --git a/apps/dashboard/src/routes/dashboard/+page.svelte b/apps/dashboard/src/routes/dashboard/+page.svelte new file mode 100644 index 0000000..286cad9 --- /dev/null +++ b/apps/dashboard/src/routes/dashboard/+page.svelte @@ -0,0 +1,116 @@ + + +
+
+

+ Welcome back, {session?.user?.name || 'there'} +

+

+ Here's an overview of your poker venue. +

+
+ + +
+
+ {#if session?.user?.image} + Profile + {:else} +
+ {session?.user?.name?.charAt(0)?.toUpperCase() || '?'} +
+ {/if} +
+

+ {session?.user?.name || 'Unknown User'} +

+

+ {session?.user?.email || 'No email'} +

+
+
+
+ + +
+
+
+
+ + + +
+ Players +
+

--

+

Connect backend to see data

+
+ +
+
+
+ + + +
+ Active Tables +
+

--

+

Connect backend to see data

+
+ +
+
+
+ + + +
+ Tournaments Today +
+

--

+

Connect backend to see data

+
+
+ + +
+

Getting Started

+
+
+
+ + + +
+ Sign in to your account +
+
+
+ 2 +
+ Set up your venue profile +
+
+
+ 3 +
+ Configure your first tournament +
+
+
+ 4 +
+ Connect a display device +
+
+
+
diff --git a/apps/dashboard/src/routes/dashboard/account/+page.svelte b/apps/dashboard/src/routes/dashboard/account/+page.svelte new file mode 100644 index 0000000..2e5c586 --- /dev/null +++ b/apps/dashboard/src/routes/dashboard/account/+page.svelte @@ -0,0 +1,103 @@ + + +
+
+

Account Settings

+

+ Manage your account and security settings. +

+
+ + +
+

Profile

+
+ {#if session?.user?.image} + Profile + {:else} +
+ {session?.user?.name?.charAt(0)?.toUpperCase() || '?'} +
+ {/if} +
+

+ {session?.user?.name || 'Unknown User'} +

+

+ {session?.user?.email || 'No email'} +

+
+
+

+ Profile information is managed through your identity provider (Zitadel). +

+
+ + +
+

Password

+

+ Change your password through Zitadel's account management. +

+ + + + + Change Password in Zitadel + +
+ + +
+

+ Two-Factor Authentication (2FA) +

+

+ Add an extra layer of security to your account with TOTP-based two-factor authentication. + Managed through Zitadel. +

+ + + + + Manage 2FA in Zitadel + +
+ + +
+

Sign Out

+

+ Sign out of your PVM account on this device. +

+ +
+
diff --git a/apps/dashboard/static/robots.txt b/apps/dashboard/static/robots.txt new file mode 100644 index 0000000..b6dd667 --- /dev/null +++ b/apps/dashboard/static/robots.txt @@ -0,0 +1,3 @@ +# allow crawling everything by default +User-agent: * +Disallow: diff --git a/apps/dashboard/svelte.config.js b/apps/dashboard/svelte.config.js new file mode 100644 index 0000000..10c4eeb --- /dev/null +++ b/apps/dashboard/svelte.config.js @@ -0,0 +1,13 @@ +import adapter from '@sveltejs/adapter-auto'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + kit: { + // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. + // If your environment is not supported, or you settled on a specific environment, switch out the adapter. + // See https://svelte.dev/docs/kit/adapters for more information about adapters. + adapter: adapter() + } +}; + +export default config; diff --git a/apps/dashboard/tsconfig.json b/apps/dashboard/tsconfig.json new file mode 100644 index 0000000..2c2ed3c --- /dev/null +++ b/apps/dashboard/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "rewriteRelativeImportExtensions": true, + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } + // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias + // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files + // + // To make changes to top-level options such as include and exclude, we recommend extending + // the generated config; see https://svelte.dev/docs/kit/configuration#typescript +} diff --git a/apps/dashboard/vite.config.ts b/apps/dashboard/vite.config.ts new file mode 100644 index 0000000..bf699a8 --- /dev/null +++ b/apps/dashboard/vite.config.ts @@ -0,0 +1,7 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import tailwindcss from '@tailwindcss/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [tailwindcss(), sveltekit()] +}); diff --git a/crates/pvm-api/Cargo.toml b/crates/pvm-api/Cargo.toml new file mode 100644 index 0000000..982cb8c --- /dev/null +++ b/crates/pvm-api/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "pvm-api" +version.workspace = true +edition.workspace = true + +[dependencies] +pvm-core = { path = "../pvm-core" } +pvm-types = { path = "../pvm-types" } +pvm-auth = { path = "../pvm-auth" } + +axum.workspace = true +axum-extra.workspace = true +tower.workspace = true +tower-http.workspace = true +tokio.workspace = true +serde.workspace = true +serde_json.workspace = true +tracing.workspace = true +tracing-subscriber.workspace = true +utoipa.workspace = true +utoipa-axum.workspace = true +utoipa-swagger-ui.workspace = true +chrono.workspace = true +uuid.workspace = true +dotenvy.workspace = true +thiserror.workspace = true diff --git a/crates/pvm-api/src/main.rs b/crates/pvm-api/src/main.rs new file mode 100644 index 0000000..b561e91 --- /dev/null +++ b/crates/pvm-api/src/main.rs @@ -0,0 +1,113 @@ +use axum::extract::FromRef; +use axum::http::Method; +use pvm_auth::jwks::JwksCache; +use pvm_auth::middleware::AuthState; +use pvm_types::user::UserProfile; +use tokio::net::TcpListener; +use tokio::signal; +use tower_http::cors::{Any, CorsLayer}; +use tower_http::trace::TraceLayer; +use tracing::info; +use utoipa::OpenApi; + +mod routes; + +#[derive(Clone, FromRef)] +struct AppState { + auth: AuthState, +} + +#[derive(OpenApi)] +#[openapi( + info(title = "PVM API", version = "0.1.0", description = "Poker Venue Manager API"), + paths(routes::health, routes::user_profile), + components(schemas(routes::HealthResponse, UserProfile)), +)] +struct ApiDoc; + +#[tokio::main] +async fn main() { + // Load .env if present + let _ = dotenvy::dotenv(); + + // Init tracing + tracing_subscriber::fmt() + .with_env_filter( + tracing_subscriber::EnvFilter::try_from_default_env() + .unwrap_or_else(|_| "pvm_api=debug,pvm_auth=debug,tower_http=debug".parse().unwrap()), + ) + .init(); + + // Config from env + let port: u16 = std::env::var("PVM_API_PORT") + .ok() + .and_then(|v| v.parse().ok()) + .unwrap_or(3001); + + let issuer_url = std::env::var("PVM_ZITADEL_ISSUER_URL") + .unwrap_or_else(|_| "http://localhost:8080".into()); + + let audience = std::env::var("PVM_ZITADEL_AUDIENCE") + .unwrap_or_else(|_| "pvm-api".into()); + + // Build auth state + let jwks_cache = JwksCache::new(&issuer_url); + jwks_cache.spawn_refresh_task(); + + let auth_state = AuthState { + jwks_cache, + audience, + issuer: issuer_url, + }; + + let state = AppState { auth: auth_state }; + + // CORS + let cors = CorsLayer::new() + .allow_origin(Any) + .allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE]) + .allow_headers(Any); + + // Build router + let app = routes::router() + .merge( + utoipa_swagger_ui::SwaggerUi::new("/swagger-ui") + .url("/api-docs/openapi.json", ApiDoc::openapi()), + ) + .layer(TraceLayer::new_for_http()) + .layer(cors) + .with_state(state); + + // Start server + let addr = format!("0.0.0.0:{port}"); + info!(%addr, "Starting PVM API server"); + let listener = TcpListener::bind(&addr).await.expect("failed to bind"); + axum::serve(listener, app) + .with_graceful_shutdown(shutdown_signal()) + .await + .expect("server error"); +} + +async fn shutdown_signal() { + let ctrl_c = async { + signal::ctrl_c().await.expect("failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + _ = ctrl_c => {}, + _ = terminate => {}, + } + + info!("Shutdown signal received, starting graceful shutdown"); +} diff --git a/crates/pvm-api/src/routes.rs b/crates/pvm-api/src/routes.rs new file mode 100644 index 0000000..a90bccf --- /dev/null +++ b/crates/pvm-api/src/routes.rs @@ -0,0 +1,59 @@ +use axum::routing::get; +use axum::{Json, Router}; +use chrono::Utc; +use pvm_auth::middleware::AuthUser; +use pvm_types::user::UserProfile; +use serde::Serialize; +use utoipa::ToSchema; +use uuid::Uuid; + +use crate::AppState; + +pub fn router() -> Router { + Router::new() + .route("/api/health", get(health)) + .route("/api/user/profile", get(user_profile)) +} + +#[derive(Serialize, ToSchema)] +pub struct HealthResponse { + pub status: String, +} + +#[utoipa::path( + get, + path = "/api/health", + responses( + (status = 200, description = "Service is healthy", body = HealthResponse), + ), + tag = "health", +)] +pub async fn health() -> Json { + Json(HealthResponse { + status: "ok".into(), + }) +} + +#[utoipa::path( + get, + path = "/api/user/profile", + responses( + (status = 200, description = "User profile from JWT claims", body = UserProfile), + (status = 401, description = "Missing or invalid authentication"), + ), + security(("bearer_auth" = [])), + tag = "user", +)] +pub async fn user_profile(AuthUser(claims): AuthUser) -> Json { + let profile = UserProfile { + id: Uuid::parse_str(&claims.sub).unwrap_or_else(|_| Uuid::new_v4()), + email: claims.email.unwrap_or_default(), + name: claims.name, + picture: claims.picture, + email_verified: claims.email_verified.unwrap_or(false), + mfa_enabled: false, + created_at: Utc::now(), + }; + + Json(profile) +} diff --git a/crates/pvm-auth/Cargo.toml b/crates/pvm-auth/Cargo.toml new file mode 100644 index 0000000..4c40484 --- /dev/null +++ b/crates/pvm-auth/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "pvm-auth" +version.workspace = true +edition.workspace = true + +[dependencies] +pvm-types = { path = "../pvm-types" } +axum.workspace = true +jsonwebtoken.workspace = true +reqwest.workspace = true +serde.workspace = true +serde_json.workspace = true +thiserror.workspace = true +tokio.workspace = true +tracing.workspace = true +chrono.workspace = true +uuid.workspace = true diff --git a/crates/pvm-auth/src/claims.rs b/crates/pvm-auth/src/claims.rs new file mode 100644 index 0000000..be2a413 --- /dev/null +++ b/crates/pvm-auth/src/claims.rs @@ -0,0 +1,24 @@ +use serde::{Deserialize, Serialize}; + +/// JWT claims from Zitadel OIDC tokens +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PvmClaims { + /// Subject (user ID from Zitadel) + pub sub: String, + /// Issuer (Zitadel instance URL) + pub iss: String, + /// Audience + pub aud: Vec, + /// Expiration time (UTC timestamp) + pub exp: i64, + /// Issued at (UTC timestamp) + pub iat: i64, + /// Email address + pub email: Option, + /// Whether the email is verified + pub email_verified: Option, + /// Display name + pub name: Option, + /// Profile picture URL + pub picture: Option, +} diff --git a/crates/pvm-auth/src/jwks.rs b/crates/pvm-auth/src/jwks.rs new file mode 100644 index 0000000..5e2855a --- /dev/null +++ b/crates/pvm-auth/src/jwks.rs @@ -0,0 +1,105 @@ +use jsonwebtoken::jwk::JwkSet; +use std::sync::Arc; +use std::time::{Duration, Instant}; +use tokio::sync::RwLock; +use tracing::{info, warn}; + +const REFRESH_INTERVAL: Duration = Duration::from_secs(3600); // 1 hour + +#[derive(Clone)] +pub struct JwksCache { + inner: Arc>, + jwks_url: String, + client: reqwest::Client, +} + +struct CacheInner { + jwks: Option, + last_refresh: Option, +} + +impl JwksCache { + pub fn new(issuer_url: &str) -> Self { + let jwks_url = format!( + "{}/.well-known/jwks.json", + issuer_url.trim_end_matches('/') + ); + + Self { + inner: Arc::new(RwLock::new(CacheInner { + jwks: None, + last_refresh: None, + })), + jwks_url, + client: reqwest::Client::new(), + } + } + + pub async fn get_jwks(&self) -> Result { + // Check if we have a cached, non-stale JWKS + { + let cache = self.inner.read().await; + if let (Some(jwks), Some(last_refresh)) = (&cache.jwks, &cache.last_refresh) { + if last_refresh.elapsed() < REFRESH_INTERVAL { + return Ok(jwks.clone()); + } + } + } + + // Need to refresh + self.refresh().await + } + + async fn refresh(&self) -> Result { + info!(url = %self.jwks_url, "Fetching JWKS"); + + let response = self + .client + .get(&self.jwks_url) + .send() + .await + .map_err(|e| JwksError::Fetch(e.to_string()))?; + + if !response.status().is_success() { + return Err(JwksError::Fetch(format!( + "JWKS endpoint returned status {}", + response.status() + ))); + } + + let jwks: JwkSet = response + .json() + .await + .map_err(|e| JwksError::Parse(e.to_string()))?; + + info!(keys = jwks.keys.len(), "JWKS refreshed"); + + let mut cache = self.inner.write().await; + cache.jwks = Some(jwks.clone()); + cache.last_refresh = Some(Instant::now()); + + Ok(jwks) + } + + /// Spawn a background task that periodically refreshes the JWKS cache. + pub fn spawn_refresh_task(&self) { + let cache = self.clone(); + tokio::spawn(async move { + loop { + tokio::time::sleep(REFRESH_INTERVAL).await; + if let Err(e) = cache.refresh().await { + warn!(error = %e, "Background JWKS refresh failed"); + } + } + }); + } +} + +#[derive(Debug, thiserror::Error)] +pub enum JwksError { + #[error("Failed to fetch JWKS: {0}")] + Fetch(String), + + #[error("Failed to parse JWKS: {0}")] + Parse(String), +} diff --git a/crates/pvm-auth/src/lib.rs b/crates/pvm-auth/src/lib.rs new file mode 100644 index 0000000..02dfc5f --- /dev/null +++ b/crates/pvm-auth/src/lib.rs @@ -0,0 +1,3 @@ +pub mod claims; +pub mod jwks; +pub mod middleware; diff --git a/crates/pvm-auth/src/middleware.rs b/crates/pvm-auth/src/middleware.rs new file mode 100644 index 0000000..8f524d7 --- /dev/null +++ b/crates/pvm-auth/src/middleware.rs @@ -0,0 +1,121 @@ +use axum::extract::{FromRef, FromRequestParts}; +use axum::http::request::Parts; +use axum::http::StatusCode; +use axum::response::{IntoResponse, Response}; +use jsonwebtoken::{decode, decode_header, DecodingKey, Validation}; +use serde::Serialize; +use tracing::warn; + +use crate::claims::PvmClaims; +use crate::jwks::JwksCache; + +/// Axum extractor that validates a JWT Bearer token and yields PvmClaims. +/// +/// Usage in a handler: +/// ```ignore +/// async fn profile(AuthUser(claims): AuthUser) -> impl IntoResponse { ... } +/// ``` +pub struct AuthUser(pub PvmClaims); + +/// Shared auth state that must be added to the Axum router state. +#[derive(Clone)] +pub struct AuthState { + pub jwks_cache: JwksCache, + pub audience: String, + pub issuer: String, +} + +#[derive(Debug)] +pub enum AuthError { + MissingHeader, + InvalidHeader, + InvalidToken(String), + JwksError(String), +} + +#[derive(Serialize)] +struct AuthErrorBody { + error: String, +} + +impl IntoResponse for AuthError { + fn into_response(self) -> Response { + let msg = match &self { + AuthError::MissingHeader => "Missing Authorization header", + AuthError::InvalidHeader => "Invalid Authorization header format", + AuthError::InvalidToken(e) => e.as_str(), + AuthError::JwksError(e) => e.as_str(), + }; + + warn!(error = msg, "Auth rejection"); + + let body = AuthErrorBody { + error: msg.to_string(), + }; + + (StatusCode::UNAUTHORIZED, axum::Json(body)).into_response() + } +} + +impl FromRequestParts for AuthUser +where + S: Send + Sync, + AuthState: axum::extract::FromRef, +{ + type Rejection = AuthError; + + async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { + let auth_state = AuthState::from_ref(state); + + // Extract the Bearer token + let auth_header = parts + .headers + .get(axum::http::header::AUTHORIZATION) + .ok_or(AuthError::MissingHeader)? + .to_str() + .map_err(|_| AuthError::InvalidHeader)?; + + let token = auth_header + .strip_prefix("Bearer ") + .ok_or(AuthError::InvalidHeader)?; + + // Decode the JWT header to get the key ID (kid) + let header = + decode_header(token).map_err(|e| AuthError::InvalidToken(e.to_string()))?; + + let kid = header.kid.ok_or_else(|| { + AuthError::InvalidToken("Token header missing 'kid'".into()) + })?; + + // Fetch JWKS and find the matching key + let jwks: jsonwebtoken::jwk::JwkSet = auth_state + .jwks_cache + .get_jwks() + .await + .map_err(|e: crate::jwks::JwksError| AuthError::JwksError(e.to_string()))?; + + let jwk = jwks + .keys + .iter() + .find(|k| k.common.key_id.as_deref() == Some(&kid)) + .ok_or_else(|| { + AuthError::InvalidToken(format!("No JWK found for kid '{kid}'")) + })?; + + // Build the decoding key from the JWK + let decoding_key = DecodingKey::from_jwk(jwk) + .map_err(|e| AuthError::InvalidToken(e.to_string()))?; + + // Validate the token + let mut validation = Validation::new( + header.alg, + ); + validation.set_audience(&[&auth_state.audience]); + validation.set_issuer(&[&auth_state.issuer]); + + let token_data = decode::(token, &decoding_key, &validation) + .map_err(|e| AuthError::InvalidToken(e.to_string()))?; + + Ok(AuthUser(token_data.claims)) + } +} diff --git a/crates/pvm-core/Cargo.toml b/crates/pvm-core/Cargo.toml new file mode 100644 index 0000000..2430d64 --- /dev/null +++ b/crates/pvm-core/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "pvm-core" +version.workspace = true +edition.workspace = true + +[dependencies] +pvm-types = { path = "../pvm-types" } +axum.workspace = true +serde.workspace = true +serde_json.workspace = true +thiserror.workspace = true +uuid.workspace = true +chrono.workspace = true diff --git a/crates/pvm-core/src/error.rs b/crates/pvm-core/src/error.rs new file mode 100644 index 0000000..40726b8 --- /dev/null +++ b/crates/pvm-core/src/error.rs @@ -0,0 +1,41 @@ +use axum::http::StatusCode; +use axum::response::{IntoResponse, Response}; +use serde::Serialize; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum PvmError { + #[error("Authentication required")] + Unauthorized, + + #[error("Insufficient permissions")] + Forbidden, + + #[error("Resource not found: {0}")] + NotFound(String), + + #[error("Internal error: {0}")] + Internal(String), +} + +#[derive(Serialize)] +struct ErrorBody { + error: String, +} + +impl IntoResponse for PvmError { + fn into_response(self) -> Response { + let status = match &self { + PvmError::Unauthorized => StatusCode::UNAUTHORIZED, + PvmError::Forbidden => StatusCode::FORBIDDEN, + PvmError::NotFound(_) => StatusCode::NOT_FOUND, + PvmError::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR, + }; + + let body = ErrorBody { + error: self.to_string(), + }; + + (status, axum::Json(body)).into_response() + } +} diff --git a/crates/pvm-core/src/lib.rs b/crates/pvm-core/src/lib.rs new file mode 100644 index 0000000..a91e735 --- /dev/null +++ b/crates/pvm-core/src/lib.rs @@ -0,0 +1 @@ +pub mod error; diff --git a/crates/pvm-types/Cargo.toml b/crates/pvm-types/Cargo.toml new file mode 100644 index 0000000..4684da4 --- /dev/null +++ b/crates/pvm-types/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "pvm-types" +version.workspace = true +edition.workspace = true + +[dependencies] +serde.workspace = true +chrono.workspace = true +uuid.workspace = true +utoipa.workspace = true diff --git a/crates/pvm-types/src/lib.rs b/crates/pvm-types/src/lib.rs new file mode 100644 index 0000000..22d12a3 --- /dev/null +++ b/crates/pvm-types/src/lib.rs @@ -0,0 +1 @@ +pub mod user; diff --git a/crates/pvm-types/src/user.rs b/crates/pvm-types/src/user.rs new file mode 100644 index 0000000..df0d16a --- /dev/null +++ b/crates/pvm-types/src/user.rs @@ -0,0 +1,15 @@ +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; +use uuid::Uuid; + +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +pub struct UserProfile { + pub id: Uuid, + pub email: String, + pub name: Option, + pub picture: Option, + pub email_verified: bool, + pub mfa_enabled: bool, + pub created_at: DateTime, +} diff --git a/docker/.env.example b/docker/.env.example new file mode 100644 index 0000000..05ec75d --- /dev/null +++ b/docker/.env.example @@ -0,0 +1,7 @@ +# Zitadel +ZITADEL_MASTERKEY=a-32-character-masterkey-for-dev! +ZITADEL_DB_PASSWORD=zitadel-dev-password +ZITADEL_ADMIN_PASSWORD=Admin1234! + +# PVM Application Database +PVM_DB_PASSWORD=pvm-dev-password diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000..db65ed9 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,75 @@ +# PVM Docker Dev Environment + +Local development stack with Zitadel auth, PostgreSQL, and DragonflyDB. + +## Services + +| Service | Description | Port | +|---------|-------------|------| +| **zitadel** | Zitadel v3 identity provider (OIDC/OAuth2) | 8080 | +| **zitadel-db** | PostgreSQL 16 for Zitadel (internal, not exposed) | — | +| **pvm-db** | PostgreSQL 16 for PVM application data | 5432 | +| **dragonfly** | DragonflyDB (Redis-compatible cache) | 6379 | + +## Quick Start + +```bash +# Copy env file and adjust if needed +cp .env.example .env + +# Start all services +docker compose -f docker-compose.dev.yml up -d + +# Check status +docker compose -f docker-compose.dev.yml ps + +# View Zitadel logs (first startup takes ~30-60s) +docker compose -f docker-compose.dev.yml logs -f zitadel +``` + +## Zitadel Admin Console + +Once Zitadel finishes initializing (watch the logs for "server is listening"), open: + +- **Console URL:** http://localhost:8080/ui/console +- **Username:** `admin` +- **Password:** value of `ZITADEL_ADMIN_PASSWORD` in your `.env` (default: `Admin1234!`) + +## First-Time Zitadel Setup + +After the first `docker compose up`, configure Zitadel for PVM: + +1. **Log in** to the admin console at http://localhost:8080/ui/console +2. **Create a project** called "PVM" +3. **Create an application** within the project: + - Name: "PVM Web" + - Type: Web + - Auth method: PKCE (recommended for SvelteKit) + - Redirect URIs: `http://localhost:5173/auth/callback/zitadel` + - Post-logout URIs: `http://localhost:5173` +4. **Note the Client ID** — you'll need it for SvelteKit's `AUTH_ZITADEL_ID` +5. (Optional) **Configure social login** providers under Settings > Identity Providers: + - Google, Apple, Facebook — each requires an OAuth app from the respective developer console + +## Connecting from the PVM Backend + +``` +# PostgreSQL (PVM app database) +DATABASE_URL=postgres://pvm:pvm-dev-password@localhost:5432/pvm + +# DragonflyDB (Redis-compatible) +REDIS_URL=redis://localhost:6379 + +# Zitadel issuer (for OIDC/JWT validation) +ZITADEL_URL=http://localhost:8080 +``` + +## Stopping & Cleanup + +```bash +# Stop services (data is preserved in volumes) +docker compose -f docker-compose.dev.yml down + +# Stop and delete all data (fresh start) +docker compose -f docker-compose.dev.yml down -v +``` diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml new file mode 100644 index 0000000..e8f44f6 --- /dev/null +++ b/docker/docker-compose.dev.yml @@ -0,0 +1,73 @@ +services: + zitadel: + image: ghcr.io/zitadel/zitadel:v3-latest + command: start-from-init --masterkey "${ZITADEL_MASTERKEY}" --tlsMode disabled + environment: + ZITADEL_DATABASE_POSTGRES_HOST: zitadel-db + ZITADEL_DATABASE_POSTGRES_PORT: 5432 + ZITADEL_DATABASE_POSTGRES_DATABASE: zitadel + ZITADEL_DATABASE_POSTGRES_USER_USERNAME: zitadel + ZITADEL_DATABASE_POSTGRES_USER_PASSWORD: "${ZITADEL_DB_PASSWORD}" + ZITADEL_DATABASE_POSTGRES_USER_SSL_MODE: disable + ZITADEL_DATABASE_POSTGRES_ADMIN_USERNAME: zitadel + ZITADEL_DATABASE_POSTGRES_ADMIN_PASSWORD: "${ZITADEL_DB_PASSWORD}" + ZITADEL_DATABASE_POSTGRES_ADMIN_SSL_MODE: disable + ZITADEL_EXTERNALDOMAIN: localhost + ZITADEL_EXTERNALPORT: 8080 + ZITADEL_EXTERNALSECURE: "false" + ZITADEL_TLS_MODE: disabled + ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME: admin + ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD: "${ZITADEL_ADMIN_PASSWORD}" + ports: + - "8080:8080" + depends_on: + zitadel-db: + condition: service_healthy + restart: unless-stopped + + zitadel-db: + image: postgres:16-alpine + environment: + POSTGRES_DB: zitadel + POSTGRES_USER: zitadel + POSTGRES_PASSWORD: "${ZITADEL_DB_PASSWORD}" + volumes: + - zitadel-pg-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U zitadel -d zitadel"] + interval: 5s + timeout: 5s + retries: 10 + restart: unless-stopped + + pvm-db: + image: postgres:16-alpine + environment: + POSTGRES_DB: pvm + POSTGRES_USER: pvm + POSTGRES_PASSWORD: "${PVM_DB_PASSWORD}" + ports: + - "5432:5432" + volumes: + - pvm-pg-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U pvm -d pvm"] + interval: 5s + timeout: 5s + retries: 10 + restart: unless-stopped + + dragonfly: + image: docker.dragonflydb.io/dragonflydb/dragonfly:latest + ports: + - "6379:6379" + volumes: + - dragonfly-data:/data + ulimits: + memlock: -1 + restart: unless-stopped + +volumes: + zitadel-pg-data: + pvm-pg-data: + dragonfly-data: diff --git a/package.json b/package.json new file mode 100644 index 0000000..50941b5 --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "name": "pvm", + "private": true, + "scripts": { + "dev": "turbo dev", + "build": "turbo build", + "test": "turbo test", + "lint": "turbo lint" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..c0d3c90 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1612 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: {} + + apps/dashboard: + dependencies: + '@auth/core': + specifier: ^0.38.0 + version: 0.38.0 + '@auth/sveltekit': + specifier: ^1.7.0 + version: 1.11.1(@sveltejs/kit@2.50.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0) + tailwindcss: + specifier: ^4.0.0 + version: 4.1.18 + devDependencies: + '@sveltejs/adapter-auto': + specifier: ^7.0.0 + version: 7.0.0(@sveltejs/kit@2.50.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2))) + '@sveltejs/adapter-node': + specifier: ^5.0.0 + version: 5.5.2(@sveltejs/kit@2.50.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2))) + '@sveltejs/kit': + specifier: ^2.50.2 + version: 2.50.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)) + '@sveltejs/vite-plugin-svelte': + specifier: ^6.2.4 + version: 6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)) + '@tailwindcss/vite': + specifier: ^4.0.0 + version: 4.1.18(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)) + svelte: + specifier: ^5.49.2 + version: 5.50.0 + svelte-check: + specifier: ^4.3.6 + version: 4.3.6(picomatch@4.0.3)(svelte@5.50.0)(typescript@5.9.3) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vite: + specifier: ^7.3.1 + version: 7.3.1(jiti@2.6.1)(lightningcss@1.30.2) + +packages: + + '@auth/core@0.38.0': + resolution: {integrity: sha512-ClHl44x4cY3wfJmHLpW+XrYqED0fZIzbHmwbExltzroCjR5ts3DLTWzADRba8mJFYZ8JIEJDa+lXnGl0E9Bl7Q==} + peerDependencies: + '@simplewebauthn/browser': ^9.0.1 + '@simplewebauthn/server': ^9.0.2 + nodemailer: ^6.8.0 + peerDependenciesMeta: + '@simplewebauthn/browser': + optional: true + '@simplewebauthn/server': + optional: true + nodemailer: + optional: true + + '@auth/core@0.41.1': + resolution: {integrity: sha512-t9cJ2zNYAdWMacGRMT6+r4xr1uybIdmYa49calBPeTqwgAFPV/88ac9TEvCR85pvATiSPt8VaNf+Gt24JIT/uw==} + peerDependencies: + '@simplewebauthn/browser': ^9.0.1 + '@simplewebauthn/server': ^9.0.2 + nodemailer: ^7.0.7 + peerDependenciesMeta: + '@simplewebauthn/browser': + optional: true + '@simplewebauthn/server': + optional: true + nodemailer: + optional: true + + '@auth/sveltekit@1.11.1': + resolution: {integrity: sha512-cWNfXcKrNIVtJYOY1tq7H7m03j89Wg7xrTvOJALu18fZdYulzYCPIAdTw8XSEzOp6KyhOGo7tmW7VtzRNtr/8Q==} + peerDependencies: + '@simplewebauthn/browser': ^9.0.1 + '@simplewebauthn/server': ^9.0.3 + '@sveltejs/kit': ^1.0.0 || ^2.0.0 + nodemailer: ^7.0.7 + svelte: ^3.54.0 || ^4.0.0 || ^5.0.0-0 + peerDependenciesMeta: + '@simplewebauthn/browser': + optional: true + '@simplewebauthn/server': + optional: true + nodemailer: + optional: true + + '@esbuild/aix-ppc64@0.27.3': + resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.3': + resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.3': + resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.3': + resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.3': + resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.3': + resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.3': + resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.3': + resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.3': + resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.3': + resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.3': + resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.3': + resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.3': + resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.3': + resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.3': + resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.3': + resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.3': + resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.3': + resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.3': + resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.3': + resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.3': + resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.3': + resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.3': + resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.3': + resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.3': + resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.3': + resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@panva/hkdf@1.2.1': + resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@rollup/plugin-commonjs@28.0.9': + resolution: {integrity: sha512-PIR4/OHZ79romx0BVVll/PkwWpJ7e5lsqFa3gFfcrFPWwLXLV39JVUzQV9RKjWerE7B845Hqjj9VYlQeieZ2dA==} + engines: {node: '>=16.0.0 || 14 >= 14.17'} + peerDependencies: + rollup: ^2.68.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-json@6.1.0': + resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-node-resolve@16.0.3': + resolution: {integrity: sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.57.1': + resolution: {integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.57.1': + resolution: {integrity: sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.57.1': + resolution: {integrity: sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.57.1': + resolution: {integrity: sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.57.1': + resolution: {integrity: sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.57.1': + resolution: {integrity: sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.57.1': + resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.57.1': + resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.57.1': + resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.57.1': + resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.57.1': + resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.57.1': + resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.57.1': + resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.57.1': + resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.57.1': + resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.57.1': + resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.57.1': + resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.57.1': + resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.57.1': + resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.57.1': + resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.57.1': + resolution: {integrity: sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.57.1': + resolution: {integrity: sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.57.1': + resolution: {integrity: sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.57.1': + resolution: {integrity: sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.57.1': + resolution: {integrity: sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==} + cpu: [x64] + os: [win32] + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@sveltejs/acorn-typescript@1.0.8': + resolution: {integrity: sha512-esgN+54+q0NjB0Y/4BomT9samII7jGwNy/2a3wNZbT2A2RpmXsXwUt24LvLhx6jUq2gVk4cWEvcRO6MFQbOfNA==} + peerDependencies: + acorn: ^8.9.0 + + '@sveltejs/adapter-auto@7.0.0': + resolution: {integrity: sha512-ImDWaErTOCkRS4Gt+5gZuymKFBobnhChXUZ9lhUZLahUgvA4OOvRzi3sahzYgbxGj5nkA6OV0GAW378+dl/gyw==} + peerDependencies: + '@sveltejs/kit': ^2.0.0 + + '@sveltejs/adapter-node@5.5.2': + resolution: {integrity: sha512-L15Djwpr7HrSAPj/Z8PYfc0pa9A1tllrr18phKI0WJHJeoWw45yinPf0IGgVTmakqx1B3JQ+C/OFl9ZwmxHU1Q==} + peerDependencies: + '@sveltejs/kit': ^2.4.0 + + '@sveltejs/kit@2.50.2': + resolution: {integrity: sha512-875hTUkEbz+MyJIxWbQjfMaekqdmEKUUfR7JyKcpfMRZqcGyrO9Gd+iS1D/Dx8LpE5FEtutWGOtlAh4ReSAiOA==} + engines: {node: '>=18.13'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.0.0 + '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: ^5.3.3 + vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + typescript: + optional: true + + '@sveltejs/vite-plugin-svelte-inspector@5.0.2': + resolution: {integrity: sha512-TZzRTcEtZffICSAoZGkPSl6Etsj2torOVrx6Uw0KpXxrec9Gg6jFWQ60Q3+LmNGfZSxHRCZL7vXVZIWmuV50Ig==} + engines: {node: ^20.19 || ^22.12 || >=24} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^6.0.0-next.0 + svelte: ^5.0.0 + vite: ^6.3.0 || ^7.0.0 + + '@sveltejs/vite-plugin-svelte@6.2.4': + resolution: {integrity: sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA==} + engines: {node: ^20.19 || ^22.12 || >=24} + peerDependencies: + svelte: ^5.0.0 + vite: ^6.3.0 || ^7.0.0 + + '@tailwindcss/node@4.1.18': + resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==} + + '@tailwindcss/oxide-android-arm64@4.1.18': + resolution: {integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.18': + resolution: {integrity: sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.18': + resolution: {integrity: sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.18': + resolution: {integrity: sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': + resolution: {integrity: sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': + resolution: {integrity: sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': + resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': + resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.1.18': + resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.1.18': + resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': + resolution: {integrity: sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': + resolution: {integrity: sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.18': + resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==} + engines: {node: '>= 10'} + + '@tailwindcss/vite@4.1.18': + resolution: {integrity: sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 + + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/resolve@1.20.2': + resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + devalue@5.6.2: + resolution: {integrity: sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg==} + + enhanced-resolve@5.19.0: + resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==} + engines: {node: '>=10.13.0'} + + esbuild@0.27.3: + resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} + engines: {node: '>=18'} + hasBin: true + + esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + + esrap@2.2.2: + resolution: {integrity: sha512-zA6497ha+qKvoWIK+WM9NAh5ni17sKZKhbS5B3PoYbBvaYHZWoS33zmFybmyqpn07RLUxSmn+RCls2/XF+d0oQ==} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-module@1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + + is-reference@1.2.1: + resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + jose@6.1.3: + resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + lightningcss-android-arm64@1.30.2: + resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.30.2: + resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.2: + resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.2: + resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.2: + resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.2: + resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.30.2: + resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.30.2: + resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.30.2: + resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.30.2: + resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.2: + resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.2: + resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} + engines: {node: '>= 12.0.0'} + + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + oauth4webapi@3.8.4: + resolution: {integrity: sha512-EKlVEgav8zH31IXxvhCqjEgQws6S9QmnmJyLXmeV5REf59g7VmqRVa5l/rhGWtUqGm2rLVTNwukn9hla5kJ2WQ==} + + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + preact-render-to-string@6.5.11: + resolution: {integrity: sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==} + peerDependencies: + preact: '>=10' + + preact@10.24.3: + resolution: {integrity: sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + + rollup@4.57.1: + resolution: {integrity: sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + + set-cookie-parser@3.0.1: + resolution: {integrity: sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q==} + + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svelte-check@4.3.6: + resolution: {integrity: sha512-uBkz96ElE3G4pt9E1Tw0xvBfIUQkeH794kDQZdAUk795UVMr+NJZpuFSS62vcmO/DuSalK83LyOwhgWq8YGU1Q==} + engines: {node: '>= 18.0.0'} + hasBin: true + peerDependencies: + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: '>=5.0.0' + + svelte@5.50.0: + resolution: {integrity: sha512-FR9kTLmX5i0oyeQ5j/+w8DuagIkQ7MWMuPpPVioW2zx9Dw77q+1ufLzF1IqNtcTXPRnIIio4PlasliVn43OnbQ==} + engines: {node: '>=18'} + + tailwindcss@4.1.18: + resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==} + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + vite@7.3.1: + resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@1.1.1: + resolution: {integrity: sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + vite: + optional: true + + zimmerframe@1.1.4: + resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} + +snapshots: + + '@auth/core@0.38.0': + dependencies: + '@panva/hkdf': 1.2.1 + jose: 6.1.3 + oauth4webapi: 3.8.4 + preact: 10.24.3 + preact-render-to-string: 6.5.11(preact@10.24.3) + + '@auth/core@0.41.1': + dependencies: + '@panva/hkdf': 1.2.1 + jose: 6.1.3 + oauth4webapi: 3.8.4 + preact: 10.24.3 + preact-render-to-string: 6.5.11(preact@10.24.3) + + '@auth/sveltekit@1.11.1(@sveltejs/kit@2.50.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)': + dependencies: + '@auth/core': 0.41.1 + '@sveltejs/kit': 2.50.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)) + set-cookie-parser: 2.7.2 + svelte: 5.50.0 + + '@esbuild/aix-ppc64@0.27.3': + optional: true + + '@esbuild/android-arm64@0.27.3': + optional: true + + '@esbuild/android-arm@0.27.3': + optional: true + + '@esbuild/android-x64@0.27.3': + optional: true + + '@esbuild/darwin-arm64@0.27.3': + optional: true + + '@esbuild/darwin-x64@0.27.3': + optional: true + + '@esbuild/freebsd-arm64@0.27.3': + optional: true + + '@esbuild/freebsd-x64@0.27.3': + optional: true + + '@esbuild/linux-arm64@0.27.3': + optional: true + + '@esbuild/linux-arm@0.27.3': + optional: true + + '@esbuild/linux-ia32@0.27.3': + optional: true + + '@esbuild/linux-loong64@0.27.3': + optional: true + + '@esbuild/linux-mips64el@0.27.3': + optional: true + + '@esbuild/linux-ppc64@0.27.3': + optional: true + + '@esbuild/linux-riscv64@0.27.3': + optional: true + + '@esbuild/linux-s390x@0.27.3': + optional: true + + '@esbuild/linux-x64@0.27.3': + optional: true + + '@esbuild/netbsd-arm64@0.27.3': + optional: true + + '@esbuild/netbsd-x64@0.27.3': + optional: true + + '@esbuild/openbsd-arm64@0.27.3': + optional: true + + '@esbuild/openbsd-x64@0.27.3': + optional: true + + '@esbuild/openharmony-arm64@0.27.3': + optional: true + + '@esbuild/sunos-x64@0.27.3': + optional: true + + '@esbuild/win32-arm64@0.27.3': + optional: true + + '@esbuild/win32-ia32@0.27.3': + optional: true + + '@esbuild/win32-x64@0.27.3': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@panva/hkdf@1.2.1': {} + + '@polka/url@1.0.0-next.29': {} + + '@rollup/plugin-commonjs@28.0.9(rollup@4.57.1)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.57.1) + commondir: 1.0.1 + estree-walker: 2.0.2 + fdir: 6.5.0(picomatch@4.0.3) + is-reference: 1.2.1 + magic-string: 0.30.21 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.57.1 + + '@rollup/plugin-json@6.1.0(rollup@4.57.1)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.57.1) + optionalDependencies: + rollup: 4.57.1 + + '@rollup/plugin-node-resolve@16.0.3(rollup@4.57.1)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.57.1) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.11 + optionalDependencies: + rollup: 4.57.1 + + '@rollup/pluginutils@5.3.0(rollup@4.57.1)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.57.1 + + '@rollup/rollup-android-arm-eabi@4.57.1': + optional: true + + '@rollup/rollup-android-arm64@4.57.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.57.1': + optional: true + + '@rollup/rollup-darwin-x64@4.57.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.57.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.57.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.57.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.57.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.57.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.57.1': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.57.1': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.57.1': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.57.1': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.57.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.57.1': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.57.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.57.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.57.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.57.1': + optional: true + + '@rollup/rollup-openbsd-x64@4.57.1': + optional: true + + '@rollup/rollup-openharmony-arm64@4.57.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.57.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.57.1': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.57.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.57.1': + optional: true + + '@standard-schema/spec@1.1.0': {} + + '@sveltejs/acorn-typescript@1.0.8(acorn@8.15.0)': + dependencies: + acorn: 8.15.0 + + '@sveltejs/adapter-auto@7.0.0(@sveltejs/kit@2.50.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))': + dependencies: + '@sveltejs/kit': 2.50.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)) + + '@sveltejs/adapter-node@5.5.2(@sveltejs/kit@2.50.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))': + dependencies: + '@rollup/plugin-commonjs': 28.0.9(rollup@4.57.1) + '@rollup/plugin-json': 6.1.0(rollup@4.57.1) + '@rollup/plugin-node-resolve': 16.0.3(rollup@4.57.1) + '@sveltejs/kit': 2.50.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)) + rollup: 4.57.1 + + '@sveltejs/kit@2.50.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2))': + dependencies: + '@standard-schema/spec': 1.1.0 + '@sveltejs/acorn-typescript': 1.0.8(acorn@8.15.0) + '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)) + '@types/cookie': 0.6.0 + acorn: 8.15.0 + cookie: 0.6.0 + devalue: 5.6.2 + esm-env: 1.2.2 + kleur: 4.1.5 + magic-string: 0.30.21 + mrmime: 2.0.1 + sade: 1.8.1 + set-cookie-parser: 3.0.1 + sirv: 3.0.2 + svelte: 5.50.0 + vite: 7.3.1(jiti@2.6.1)(lightningcss@1.30.2) + optionalDependencies: + typescript: 5.9.3 + + '@sveltejs/vite-plugin-svelte-inspector@5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2))': + dependencies: + '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)) + obug: 2.1.1 + svelte: 5.50.0 + vite: 7.3.1(jiti@2.6.1)(lightningcss@1.30.2) + + '@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2))': + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)) + deepmerge: 4.3.1 + magic-string: 0.30.21 + obug: 2.1.1 + svelte: 5.50.0 + vite: 7.3.1(jiti@2.6.1)(lightningcss@1.30.2) + vitefu: 1.1.1(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)) + + '@tailwindcss/node@4.1.18': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.19.0 + jiti: 2.6.1 + lightningcss: 1.30.2 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.1.18 + + '@tailwindcss/oxide-android-arm64@4.1.18': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.18': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.18': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.18': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.18': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': + optional: true + + '@tailwindcss/oxide@4.1.18': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.18 + '@tailwindcss/oxide-darwin-arm64': 4.1.18 + '@tailwindcss/oxide-darwin-x64': 4.1.18 + '@tailwindcss/oxide-freebsd-x64': 4.1.18 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.18 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.18 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.18 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.18 + '@tailwindcss/oxide-linux-x64-musl': 4.1.18 + '@tailwindcss/oxide-wasm32-wasi': 4.1.18 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.18 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.18 + + '@tailwindcss/vite@4.1.18(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2))': + dependencies: + '@tailwindcss/node': 4.1.18 + '@tailwindcss/oxide': 4.1.18 + tailwindcss: 4.1.18 + vite: 7.3.1(jiti@2.6.1)(lightningcss@1.30.2) + + '@types/cookie@0.6.0': {} + + '@types/estree@1.0.8': {} + + '@types/resolve@1.20.2': {} + + acorn@8.15.0: {} + + aria-query@5.3.2: {} + + axobject-query@4.1.0: {} + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + clsx@2.1.1: {} + + commondir@1.0.1: {} + + cookie@0.6.0: {} + + deepmerge@4.3.1: {} + + detect-libc@2.1.2: {} + + devalue@5.6.2: {} + + enhanced-resolve@5.19.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + + esbuild@0.27.3: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.3 + '@esbuild/android-arm': 0.27.3 + '@esbuild/android-arm64': 0.27.3 + '@esbuild/android-x64': 0.27.3 + '@esbuild/darwin-arm64': 0.27.3 + '@esbuild/darwin-x64': 0.27.3 + '@esbuild/freebsd-arm64': 0.27.3 + '@esbuild/freebsd-x64': 0.27.3 + '@esbuild/linux-arm': 0.27.3 + '@esbuild/linux-arm64': 0.27.3 + '@esbuild/linux-ia32': 0.27.3 + '@esbuild/linux-loong64': 0.27.3 + '@esbuild/linux-mips64el': 0.27.3 + '@esbuild/linux-ppc64': 0.27.3 + '@esbuild/linux-riscv64': 0.27.3 + '@esbuild/linux-s390x': 0.27.3 + '@esbuild/linux-x64': 0.27.3 + '@esbuild/netbsd-arm64': 0.27.3 + '@esbuild/netbsd-x64': 0.27.3 + '@esbuild/openbsd-arm64': 0.27.3 + '@esbuild/openbsd-x64': 0.27.3 + '@esbuild/openharmony-arm64': 0.27.3 + '@esbuild/sunos-x64': 0.27.3 + '@esbuild/win32-arm64': 0.27.3 + '@esbuild/win32-ia32': 0.27.3 + '@esbuild/win32-x64': 0.27.3 + + esm-env@1.2.2: {} + + esrap@2.2.2: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + estree-walker@2.0.2: {} + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + graceful-fs@4.2.11: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-module@1.0.0: {} + + is-reference@1.2.1: + dependencies: + '@types/estree': 1.0.8 + + is-reference@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + jiti@2.6.1: {} + + jose@6.1.3: {} + + kleur@4.1.5: {} + + lightningcss-android-arm64@1.30.2: + optional: true + + lightningcss-darwin-arm64@1.30.2: + optional: true + + lightningcss-darwin-x64@1.30.2: + optional: true + + lightningcss-freebsd-x64@1.30.2: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.2: + optional: true + + lightningcss-linux-arm64-gnu@1.30.2: + optional: true + + lightningcss-linux-arm64-musl@1.30.2: + optional: true + + lightningcss-linux-x64-gnu@1.30.2: + optional: true + + lightningcss-linux-x64-musl@1.30.2: + optional: true + + lightningcss-win32-arm64-msvc@1.30.2: + optional: true + + lightningcss-win32-x64-msvc@1.30.2: + optional: true + + lightningcss@1.30.2: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.30.2 + lightningcss-darwin-arm64: 1.30.2 + lightningcss-darwin-x64: 1.30.2 + lightningcss-freebsd-x64: 1.30.2 + lightningcss-linux-arm-gnueabihf: 1.30.2 + lightningcss-linux-arm64-gnu: 1.30.2 + lightningcss-linux-arm64-musl: 1.30.2 + lightningcss-linux-x64-gnu: 1.30.2 + lightningcss-linux-x64-musl: 1.30.2 + lightningcss-win32-arm64-msvc: 1.30.2 + lightningcss-win32-x64-msvc: 1.30.2 + + locate-character@3.0.0: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + mri@1.2.0: {} + + mrmime@2.0.1: {} + + nanoid@3.3.11: {} + + oauth4webapi@3.8.4: {} + + obug@2.1.1: {} + + path-parse@1.0.7: {} + + picocolors@1.1.1: {} + + picomatch@4.0.3: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + preact-render-to-string@6.5.11(preact@10.24.3): + dependencies: + preact: 10.24.3 + + preact@10.24.3: {} + + readdirp@4.1.2: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + rollup@4.57.1: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.57.1 + '@rollup/rollup-android-arm64': 4.57.1 + '@rollup/rollup-darwin-arm64': 4.57.1 + '@rollup/rollup-darwin-x64': 4.57.1 + '@rollup/rollup-freebsd-arm64': 4.57.1 + '@rollup/rollup-freebsd-x64': 4.57.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.57.1 + '@rollup/rollup-linux-arm-musleabihf': 4.57.1 + '@rollup/rollup-linux-arm64-gnu': 4.57.1 + '@rollup/rollup-linux-arm64-musl': 4.57.1 + '@rollup/rollup-linux-loong64-gnu': 4.57.1 + '@rollup/rollup-linux-loong64-musl': 4.57.1 + '@rollup/rollup-linux-ppc64-gnu': 4.57.1 + '@rollup/rollup-linux-ppc64-musl': 4.57.1 + '@rollup/rollup-linux-riscv64-gnu': 4.57.1 + '@rollup/rollup-linux-riscv64-musl': 4.57.1 + '@rollup/rollup-linux-s390x-gnu': 4.57.1 + '@rollup/rollup-linux-x64-gnu': 4.57.1 + '@rollup/rollup-linux-x64-musl': 4.57.1 + '@rollup/rollup-openbsd-x64': 4.57.1 + '@rollup/rollup-openharmony-arm64': 4.57.1 + '@rollup/rollup-win32-arm64-msvc': 4.57.1 + '@rollup/rollup-win32-ia32-msvc': 4.57.1 + '@rollup/rollup-win32-x64-gnu': 4.57.1 + '@rollup/rollup-win32-x64-msvc': 4.57.1 + fsevents: 2.3.3 + + sade@1.8.1: + dependencies: + mri: 1.2.0 + + set-cookie-parser@2.7.2: {} + + set-cookie-parser@3.0.1: {} + + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + source-map-js@1.2.1: {} + + supports-preserve-symlinks-flag@1.0.0: {} + + svelte-check@4.3.6(picomatch@4.0.3)(svelte@5.50.0)(typescript@5.9.3): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + chokidar: 4.0.3 + fdir: 6.5.0(picomatch@4.0.3) + picocolors: 1.1.1 + sade: 1.8.1 + svelte: 5.50.0 + typescript: 5.9.3 + transitivePeerDependencies: + - picomatch + + svelte@5.50.0: + dependencies: + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.5.5 + '@sveltejs/acorn-typescript': 1.0.8(acorn@8.15.0) + '@types/estree': 1.0.8 + acorn: 8.15.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + clsx: 2.1.1 + devalue: 5.6.2 + esm-env: 1.2.2 + esrap: 2.2.2 + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.21 + zimmerframe: 1.1.4 + + tailwindcss@4.1.18: {} + + tapable@2.3.0: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + totalist@3.0.1: {} + + typescript@5.9.3: {} + + vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2): + dependencies: + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.57.1 + tinyglobby: 0.2.15 + optionalDependencies: + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.30.2 + + vitefu@1.1.1(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)): + optionalDependencies: + vite: 7.3.1(jiti@2.6.1)(lightningcss@1.30.2) + + zimmerframe@1.1.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..e31e0c9 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,5 @@ +packages: + - "apps/*" + - "packages/*" +onlyBuiltDependencies: + - esbuild