From 24a6bc62467aa571e4934bda4662ffabda5d1a39 Mon Sep 17 00:00:00 2001 From: The Howling Whispers Date: Tue, 30 Jun 2026 16:57:28 +0200 Subject: [PATCH] Initial commit: RP site --- .gitignore | 12 + package-lock.json | 2266 ++++++++++++++++++ package.json | 24 + run.sh | 18 + scripts/netcup-dns.sh | 268 +++ src/app.css | 57 + src/app.d.ts | 12 + src/app.html | 15 + src/hooks.server.ts | 43 + src/lib/components/Badge.svelte | 37 + src/lib/components/Button.svelte | 69 + src/lib/components/EmptyState.svelte | 47 + src/lib/components/PageHeader.svelte | 65 + src/lib/components/Panel.svelte | 18 + src/lib/design/editorial.css | 161 ++ src/lib/server/auth/index.ts | 18 + src/lib/server/auth/local.ts | 51 + src/lib/server/auth/providers/discord.ts | 125 + src/lib/server/auth/session.ts | 115 + src/lib/server/db/index.ts | 68 + src/routes/+error.svelte | 38 + src/routes/+layout.svelte | 307 +++ src/routes/+page.svelte | 96 + src/routes/api/auth/local-enabled/+server.ts | 6 + src/routes/api/auth/logout/+server.ts | 13 + src/routes/api/auth/me/+server.ts | 11 + src/routes/api/profile/+server.ts | 35 + src/routes/auth/discord/+server.ts | 14 + src/routes/auth/discord/callback/+server.ts | 64 + src/routes/auth/local/+server.ts | 47 + src/routes/characters/+page.svelte | 21 + src/routes/loading/+page.svelte | 198 ++ src/routes/login/+page.server.ts | 5 + src/routes/login/+page.svelte | 260 ++ src/routes/profile/+page.svelte | 106 + src/routes/rooms/+page.svelte | 21 + src/routes/settings/+page.svelte | 57 + static/favicon.ico | Bin 0 -> 62 bytes static/favicon.png | Bin 0 -> 68 bytes static/favicon.svg | 4 + svelte.config.js | 16 + tsconfig.json | 14 + vite.config.ts | 11 + 43 files changed, 4833 insertions(+) create mode 100644 .gitignore create mode 100644 package-lock.json create mode 100644 package.json create mode 100755 run.sh create mode 100755 scripts/netcup-dns.sh create mode 100644 src/app.css create mode 100644 src/app.d.ts create mode 100644 src/app.html create mode 100644 src/hooks.server.ts create mode 100644 src/lib/components/Badge.svelte create mode 100644 src/lib/components/Button.svelte create mode 100644 src/lib/components/EmptyState.svelte create mode 100644 src/lib/components/PageHeader.svelte create mode 100644 src/lib/components/Panel.svelte create mode 100644 src/lib/design/editorial.css create mode 100644 src/lib/server/auth/index.ts create mode 100644 src/lib/server/auth/local.ts create mode 100644 src/lib/server/auth/providers/discord.ts create mode 100644 src/lib/server/auth/session.ts create mode 100644 src/lib/server/db/index.ts create mode 100644 src/routes/+error.svelte create mode 100644 src/routes/+layout.svelte create mode 100644 src/routes/+page.svelte create mode 100644 src/routes/api/auth/local-enabled/+server.ts create mode 100644 src/routes/api/auth/logout/+server.ts create mode 100644 src/routes/api/auth/me/+server.ts create mode 100644 src/routes/api/profile/+server.ts create mode 100644 src/routes/auth/discord/+server.ts create mode 100644 src/routes/auth/discord/callback/+server.ts create mode 100644 src/routes/auth/local/+server.ts create mode 100644 src/routes/characters/+page.svelte create mode 100644 src/routes/loading/+page.svelte create mode 100644 src/routes/login/+page.server.ts create mode 100644 src/routes/login/+page.svelte create mode 100644 src/routes/profile/+page.svelte create mode 100644 src/routes/rooms/+page.svelte create mode 100644 src/routes/settings/+page.svelte create mode 100644 static/favicon.ico create mode 100644 static/favicon.png create mode 100644 static/favicon.svg create mode 100644 svelte.config.js create mode 100644 tsconfig.json create mode 100644 vite.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a422975 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +node_modules/ +build/ +.svelte-kit/ +data/ +*.db +.env +.env.local +.vite/ +dist/ +.tmp/ +*.log +.DS_Store diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..e6116d0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2266 @@ +{ + "name": "rp", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "rp", + "version": "0.0.1", + "dependencies": { + "better-sqlite3": "^11.5.0" + }, + "devDependencies": { + "@sveltejs/adapter-node": "^5.2.12", + "@sveltejs/kit": "^2.20.0", + "@sveltejs/vite-plugin-svelte": "^4.0.0", + "svelte": "^5.0.0", + "svelte-check": "^4.0.0", + "typescript": "^5.5.0", + "vite": "^5.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-29.0.3.tgz", + "integrity": "sha512-ZaOxZceP7SOUW7Lqw5IRVweSQYWaeIPnXIGLiB690EBA3FGJTO40EEr2L5yZplJWsgTCogILRSpcAe7+U0Otdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "fdir": "^6.2.0", + "is-reference": "1.2.1", + "magic-string": "^0.30.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=16.0.0 || 14 >= 14.17" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-json": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", + "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.3.tgz", + "integrity": "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-replace": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.3.tgz", + "integrity": "sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", + "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", + "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", + "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", + "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", + "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", + "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", + "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", + "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", + "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", + "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", + "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", + "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", + "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", + "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", + "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", + "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", + "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", + "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz", + "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", + "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", + "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", + "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", + "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", + "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", + "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sveltejs/acorn-typescript": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.10.tgz", + "integrity": "sha512-4WfKk68eTih+MiJD4fSbxN7E8kVBmTMPWHUPYjvl2N0rMs53YLTT8/YjKU5Dtnz5LqDjl7LEw4U7lXR2W3J5WA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8.9.0" + } + }, + "node_modules/@sveltejs/adapter-node": { + "version": "5.5.7", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.5.7.tgz", + "integrity": "sha512-uOfc9eVlI3A37RRSaKcgrheBYPrfJwC9VMqDp8x/O6tlKdcLLvHThSWD0KNIbjQ/d+7bwLGx3vx6aowAcRfd2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/plugin-commonjs": "^29.0.0", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^16.0.0", + "@rollup/plugin-replace": "^6.0.3", + "rollup": "^4.59.0" + }, + "peerDependencies": { + "@sveltejs/kit": "^2.4.0" + } + }, + "node_modules/@sveltejs/kit": { + "version": "2.68.0", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.68.0.tgz", + "integrity": "sha512-PdKiWsqinAoubVsSiRgVFkg3MHzGhQPnwQ8VxnGQKpZYijpapZ3UHHBje0GeByt2TvfjHPw+kxV+dNK2RIZg9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@sveltejs/acorn-typescript": "^1.0.9", + "@types/cookie": "^0.6.0", + "acorn": "^8.16.0", + "cookie": "^0.6.0", + "devalue": "^5.8.1", + "esm-env": "^1.2.2", + "kleur": "^4.1.5", + "magic-string": "^0.30.5", + "mrmime": "^2.0.0", + "set-cookie-parser": "^3.0.0", + "sirv": "^3.0.0" + }, + "bin": { + "svelte-kit": "svelte-kit.js" + }, + "engines": { + "node": ">=18.13" + }, + "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 || ^7.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": "^5.3.3 || ^6.0.0", + "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@sveltejs/load-config": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@sveltejs/load-config/-/load-config-0.2.0.tgz", + "integrity": "sha512-1LgZ/qUqSoq+QorD83lk2hka79Px0wXNW2q5V1nZlxGhQgw1jrsIbVz5YiCeucVLo4XvFLjXukUaQjIiqowkcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-4.0.4.tgz", + "integrity": "sha512-0ba1RQ/PHen5FGpdSrW7Y3fAMQjrXantECALeOiOdBdzR5+5vPP6HVZRLmZaQL+W8m++o+haIAKq5qT+MiZ7VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sveltejs/vite-plugin-svelte-inspector": "^3.0.0-next.0||^3.0.0", + "debug": "^4.3.7", + "deepmerge": "^4.3.1", + "kleur": "^4.1.5", + "magic-string": "^0.30.12", + "vitefu": "^1.0.3" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22" + }, + "peerDependencies": { + "svelte": "^5.0.0-next.96 || ^5.0.0", + "vite": "^5.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-3.0.1.tgz", + "integrity": "sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.7" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^4.0.0-next.0||^4.0.0", + "svelte": "^5.0.0-next.96 || ^5.0.0", + "vite": "^5.0.0" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/aria-query": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz", + "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/better-sqlite3": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz", + "integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/devalue": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.1.tgz", + "integrity": "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "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" + } + }, + "node_modules/esm-env": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esrap": { + "version": "2.2.13", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.13.tgz", + "integrity": "sha512-m8jH5hZgJE2RRUK/jjkGPcJEDAV+dYnZYFkosQaPTcE+Yw4xynXHOo6FUdwaWBtdR3b1MMa7wEDTSHeR2VWsGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "peerDependencies": { + "@typescript-eslint/types": "^8.2.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/types": { + "optional": true + } + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", + "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/node-abi": { + "version": "3.92.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.92.0.tgz", + "integrity": "sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", + "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/rollup": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", + "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.62.2", + "@rollup/rollup-android-arm64": "4.62.2", + "@rollup/rollup-darwin-arm64": "4.62.2", + "@rollup/rollup-darwin-x64": "4.62.2", + "@rollup/rollup-freebsd-arm64": "4.62.2", + "@rollup/rollup-freebsd-x64": "4.62.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", + "@rollup/rollup-linux-arm-musleabihf": "4.62.2", + "@rollup/rollup-linux-arm64-gnu": "4.62.2", + "@rollup/rollup-linux-arm64-musl": "4.62.2", + "@rollup/rollup-linux-loong64-gnu": "4.62.2", + "@rollup/rollup-linux-loong64-musl": "4.62.2", + "@rollup/rollup-linux-ppc64-gnu": "4.62.2", + "@rollup/rollup-linux-ppc64-musl": "4.62.2", + "@rollup/rollup-linux-riscv64-gnu": "4.62.2", + "@rollup/rollup-linux-riscv64-musl": "4.62.2", + "@rollup/rollup-linux-s390x-gnu": "4.62.2", + "@rollup/rollup-linux-x64-gnu": "4.62.2", + "@rollup/rollup-linux-x64-musl": "4.62.2", + "@rollup/rollup-openbsd-x64": "4.62.2", + "@rollup/rollup-openharmony-arm64": "4.62.2", + "@rollup/rollup-win32-arm64-msvc": "4.62.2", + "@rollup/rollup-win32-ia32-msvc": "4.62.2", + "@rollup/rollup-win32-x64-gnu": "4.62.2", + "@rollup/rollup-win32-x64-msvc": "4.62.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-cookie-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.1.tgz", + "integrity": "sha512-vM9SUhjsUYs6UeJUmygc5Ofm5eQGe85riob5ju6XCgFGJI5PLV4nrDAQpQjd+LkFBpAkADn5BQQpZ9EUNkyLuA==", + "dev": true, + "license": "MIT" + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svelte": { + "version": "5.56.4", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.56.4.tgz", + "integrity": "sha512-/d0QHehmRuJW8gVz395MTkPcPozxzdjBMBE8oEYGz8O3b9KTMzzQ9ZHJQLuFKOHOPQbU6kx/X4iid/EBBzH7iw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@sveltejs/acorn-typescript": "^1.0.10", + "@types/estree": "^1.0.5", + "@types/trusted-types": "^2.0.7", + "acorn": "^8.12.1", + "aria-query": "5.3.1", + "axobject-query": "^4.1.0", + "clsx": "^2.1.1", + "devalue": "^5.8.1", + "esm-env": "^1.2.1", + "esrap": "^2.2.12", + "is-reference": "^3.0.3", + "locate-character": "^3.0.0", + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/svelte-check": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.7.1.tgz", + "integrity": "sha512-FGUOmAqxXdN/H9Zm8slrqO7SLtFisXRB7rfOsHNJ3MLTD2po/+Stg8XyErkpumPHbuUiYTcqrEIzxpVWKTLqtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "@sveltejs/load-config": "^0.2.0", + "chokidar": "^4.0.1", + "fdir": "^6.2.0", + "picocolors": "^1.0.0", + "sade": "^1.7.4" + }, + "bin": { + "svelte-check": "bin/svelte-check" + }, + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": ">=5.0.0" + } + }, + "node_modules/svelte/node_modules/is-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/tar-fs": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.5.tgz", + "integrity": "sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "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 + } + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "dev": true, + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/zimmerframe": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", + "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..818ccb3 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "rp", + "version": "0.0.1", + "private": true, + "type": "module", + "scripts": { + "dev": "vite dev --port 3008 --host 127.0.0.1", + "build": "vite build", + "preview": "node build/index.js", + "start": "node build/index.js" + }, + "devDependencies": { + "@sveltejs/adapter-node": "^5.2.12", + "@sveltejs/kit": "^2.20.0", + "@sveltejs/vite-plugin-svelte": "^4.0.0", + "svelte": "^5.0.0", + "svelte-check": "^4.0.0", + "typescript": "^5.5.0", + "vite": "^5.4.0" + }, + "dependencies": { + "better-sqlite3": "^11.5.0" + } +} diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..ea1a2be --- /dev/null +++ b/run.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# Wrapper for the RP SvelteKit process. +# Loads /var/www/rp/.env into the environment, then execs node. +set -e +DIR="/var/www/rp" +PORT="${PORT:-2026}" +HOST="${HOST:-127.0.0.1}" +ORIGIN="${ORIGIN:-http://rp.thehowlingwhispers.com}" +export PORT HOST ORIGIN + +if [ -f "$DIR/.env" ]; then + set -a + # shellcheck disable=SC1090,SC1091 + source "$DIR/.env" + set +a +fi + +exec node "$DIR/build/index.js" diff --git a/scripts/netcup-dns.sh b/scripts/netcup-dns.sh new file mode 100755 index 0000000..6bfc0b7 --- /dev/null +++ b/scripts/netcup-dns.sh @@ -0,0 +1,268 @@ +#!/bin/bash +# /var/www/rp/scripts/netcup-dns.sh +# Reusable Netcup DNS tool for thehowlingwhispers.com (and any Netcup-managed zone). +# +# Credentials: /root/.netcup-creds (chmod 600, NETCUP_CUSTOMER / NETCUP_API_KEY / NETCUP_API_PASSWORD) +# Audit log: /var/log/netcup-dns.log +# +# Usage: +# netcup-dns.sh list # list all records +# netcup-dns.sh add # add a record (idempotent) +# netcup-dns.sh delete # delete a record +# netcup-dns.sh --dry-run add ... # preview without API calls +# netcup-dns.sh --yes add ... # skip confirmation prompt +# netcup-dns.sh --json list ... # raw JSON output +# +# Examples: +# netcup-dns.sh list thehowlingwhispers.com +# netcup-dns.sh add thehowlingwhispers.com rp A 159.195.194.180 +# netcup-dns.sh add thehowlingwhispers.com sandbox CNAME rp.thehowlingwhispers.com +# netcup-dns.sh add thehowlingwhispers.com @ TXT "v=spf1 -all" +# netcup-dns.sh --dry-run add thehowlingwhispers.com blog A 159.195.194.180 +# netcup-dns.sh --json list thehowlingwhispers.com + +set -euo pipefail + +# ─── config ──────────────────────────────────────────────────── +CREDS_FILE="${NETCUP_CREDS_FILE:-/root/.netcup-creds}" +LOG_FILE="${NETCUP_DNS_LOG:-/var/log/netcup-dns.log}" +API_BASE="https://ccp.netcup.net/api/v1" +JQ_FORMAT='@json' # not used; kept for clarity + +# ─── colors ─────────────────────────────────────────────────── +if [ -t 1 ]; then + C_RED=$'\e[31m'; C_GRN=$'\e[32m'; C_YEL=$'\e[33m'; C_DIM=$'\e[2m'; C_RST=$'\e[0m' +else + C_RED=''; C_GRN=''; C_YEL=''; C_DIM=''; C_RST='' +fi +ok() { printf '%s✓%s %s\n' "$C_GRN" "$C_RST" "$*"; } +warn() { printf '%s!%s %s\n' "$C_YEL" "$C_RST" "$*" >&2; } +err() { printf '%s✗%s %s\n' "$C_RED" "$C_RST" "$*" >&2; } + +# ─── flag parsing ───────────────────────────────────────────── +DRY_RUN=false +ASSUME_YES=false +JSON_OUT=false +SUBCMD="" +ARGS=() + +while [ $# -gt 0 ]; do + case "$1" in + --dry-run) DRY_RUN=true; shift ;; + --yes|-y) ASSUME_YES=true; shift ;; + --json) JSON_OUT=true; shift ;; + --help|-h) + sed -n '2,28p' "$0" + exit 0 + ;; + list|add|delete) + SUBCMD="$1"; shift; ARGS=("$@"); break ;; + *) + err "Unknown flag or command: $1"; exit 2 ;; + esac +done + +# ─── log helper ─────────────────────────────────────────────── +log() { + printf '%s [%s] %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$1" "$2" >> "$LOG_FILE" +} + +# ─── load creds (chmod 600 enforced) ──────────────────────── +if [ ! -f "$CREDS_FILE" ]; then + err "Credentials file not found: $CREDS_FILE" + err "Create it with NETCUP_CUSTOMER / NETCUP_API_KEY / NETCUP_API_PASSWORD (chmod 600)" + exit 1 +fi +PERMS=$(stat -c '%a' "$CREDS_FILE" 2>/dev/null || echo "") +if [ "$PERMS" != "600" ] && [ "$PERMS" != "400" ]; then + err "Credentials file must be chmod 600 or 400 (currently: $PERMS). Refusing to run." + exit 1 +fi +# shellcheck disable=SC1090 +source "$CREDS_FILE" + +if [ -z "${NETCUP_CUSTOMER:-}" ] || [ -z "${NETCUP_API_KEY:-}" ] || [ -z "${NETCUP_API_PASSWORD:-}" ]; then + err "Missing one of NETCUP_CUSTOMER / NETCUP_API_KEY / NETCUP_API_PASSWORD in $CREDS_FILE" + exit 1 +fi + +# ─── API helpers ───────────────────────────────────────────── +# Netcup requires X-Api-Key + X-Customer-Number headers, plus +# X-Session-Id for authenticated calls. Login returns sessionId. +SESSION_ID="" +SESSION_LOGGED_IN_AT=0 +SESSION_TTL=240 # refresh session every 4 minutes + +login() { + local now + now=$(date +%s) + if [ -n "$SESSION_ID" ] && [ $((now - SESSION_LOGGED_IN_AT)) -lt $SESSION_TTL ]; then + return 0 + fi + local response http_code body + response=$(curl -sS -w '\n%{http_code}' -X POST \ + -H "X-Api-Key: ${NETCUP_API_KEY}" \ + -H "X-Customer-Number: ${NETCUP_CUSTOMER}" \ + -H "Content-Type: application/json" \ + "${API_BASE}/login") + http_code=$(printf '%s' "$response" | tail -n1) + body=$(printf '%s' "$response" | sed '$d') + if [ "$http_code" != "200" ]; then + err "Login failed (HTTP $http_code): $body" + log "ERROR" "login failed: HTTP $http_code" + exit 1 + fi + SESSION_ID=$(printf '%s' "$body" | jq -r '.sessionId') + SESSION_LOGGED_IN_AT=$now + log "INFO" "login OK (session refreshed)" +} + +api() { + local method=$1; shift + local path=$1; shift + local body=${1:-} + login + local args=(-sS -w '\n%{http_code}' -X "$method" -H "X-Session-Id: $SESSION_ID" -H "Content-Type: application/json") + if [ -n "$body" ]; then + args+=(-d "$body") + fi + curl "${args[@]}" "${API_BASE}${path}" +} + +# ─── subdomain ────────────────────────────────────────────── +require_domain() { + if [ -z "${1:-}" ]; then err "Domain required"; exit 1; fi + echo "$1" +} +require_arg() { + if [ -z "${1:-}" ]; then err "$2 required"; exit 1; fi + echo "$1" +} + +# ─── subcommands ───────────────────────────────────────────── +cmd_list() { + local domain + domain=$(require_domain "${ARGS[0]:-}") + local response http_code body + response=$(api GET "/dns/${domain}") + http_code=$(printf '%s' "$response" | tail -n1) + body=$(printf '%s' "$response" | sed '$d') + if [ "$http_code" != "200" ]; then + err "List failed (HTTP $http_code): $body" + log "ERROR" "list $domain failed: HTTP $http_code" + exit 1 + fi + if $JSON_OUT; then + printf '%s\n' "$body" + else + printf '%s%-8s %-30s %-25s %s%s\n' "$C_DIM" "TYPE" "HOST" "VALUE" "TTL" "$C_RST" + printf '%s\n' "$body" | jq -r '.[] | [.type, .hostname, (.value|tostring|.[0:25]), .ttl] | @tsv' | \ + while IFS=$'\t' read -r t h v ttl; do + printf '%-8s %-30s %-25s %s\n' "$t" "$h" "$v" "$ttl" + done + fi + log "INFO" "list $domain (HTTP $http_code, $(printf '%s' "$body" | jq 'length') records)" +} + +cmd_add() { + local domain host type value + domain=$(require_domain "${ARGS[0]:-}") + host=$(require_arg "${ARGS[1]:-}" "host") + type=$(require_arg "${ARGS[2]:-}" "type") + value=$(require_arg "${ARGS[3]:-}" "value") + + # Idempotency check: list first + local list_body + list_body=$(api GET "/dns/${domain}" | sed '$d') + local existing + existing=$(printf '%s' "$list_body" | jq -r --arg h "$host" --arg t "$type" --arg v "$value" \ + '.[] | select(.hostname == $h and .type == $t and (.value|tostring) == $v) | .id') + if [ -n "$existing" ] && [ "$existing" != "null" ]; then + ok "Already exists: $domain $host $type $value (recordId=$existing) — no-op" + log "INFO" "add $domain $host $type $value — no-op (already exists, id=$existing)" + return 0 + fi + + warn "About to add: $domain $host $type $value" + if ! $ASSUME_YES && [ -t 0 ]; then + read -r -p "Continue? [y/N] " reply + case "$reply" in + y|Y|yes|YES) ;; + *) err "Aborted."; exit 1 ;; + esac + fi + + if $DRY_RUN; then + ok "[dry-run] would POST $host $type $value to $domain" + log "INFO" "add $domain $host $type $value — DRY RUN" + return 0 + fi + + # Netcup payload format + local payload + payload=$(jq -n --arg h "$host" --arg t "$type" --arg v "$value" \ + '{hostname:$h, type:$t, value:$v, ttl:3600}') + + local response http_code body + response=$(api POST "/dns/${domain}" "$payload") + http_code=$(printf '%s' "$response" | tail -n1) + body=$(printf '%s' "$response" | sed '$d') + if [ "$http_code" != "200" ] && [ "$http_code" != "201" ]; then + err "Add failed (HTTP $http_code): $body" + log "ERROR" "add $domain $host $type $value failed: HTTP $http_code" + exit 1 + fi + local new_id + new_id=$(printf '%s' "$body" | jq -r '.id // .recordId // empty') + ok "Added: $domain $host $type $value (recordId=$new_id)" + log "INFO" "add $domain $host $type $value OK (id=$new_id)" +} + +cmd_delete() { + local domain record_id + domain=$(require_domain "${ARGS[0]:-}") + record_id=$(require_arg "${ARGS[1]:-}" "recordId") + + warn "About to delete: $domain recordId=$record_id" + if ! $ASSUME_YES && [ -t 0 ]; then + read -r -p "Continue? [y/N] " reply + case "$reply" in + y|Y|yes|YES) ;; + *) err "Aborted."; exit 1 ;; + esac + fi + + if $DRY_RUN; then + ok "[dry-run] would DELETE recordId=$record_id from $domain" + log "INFO" "delete $domain $record_id — DRY RUN" + return 0 + fi + + local response http_code body + response=$(api DELETE "/dns/${domain}/${record_id}") + http_code=$(printf '%s' "$response" | tail -n1) + body=$(printf '%s' "$response" | sed '$d') + if [ "$http_code" != "200" ] && [ "$http_code" != "204" ]; then + err "Delete failed (HTTP $http_code): $body" + log "ERROR" "delete $domain $record_id failed: HTTP $http_code" + exit 1 + fi + ok "Deleted: $domain recordId=$record_id" + log "INFO" "delete $domain $record_id OK" +} + +# ─── dispatch ──────────────────────────────────────────────── +case "$SUBCMD" in + list) cmd_list ;; + add) cmd_add ;; + delete) cmd_delete ;; + "") + err "Subcommand required (list|add|delete). Try --help." + sed -n '2,28p' "$0" >&2 + exit 2 + ;; + *) + err "Unknown subcommand: $SUBCMD" + exit 2 + ;; +esac diff --git a/src/app.css b/src/app.css new file mode 100644 index 0000000..3b70ced --- /dev/null +++ b/src/app.css @@ -0,0 +1,57 @@ +/* Editorial — Phase 0 placeholder styles. + The full Editorial design system ships in Phase 1. + This file gives the hello-world enough typography to look intentional. */ + +:root { + --bg-void: #050312; + --bg-panel: #16213e; + --text-main: #e0e0e0; + --text-muted: #a0a0b0; + --accent-blush: #e0709a; + --accent-neon: #bc13fe; + --border-glow: #d4adfc33; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html, body { + height: 100%; + background: var(--bg-void); + color: var(--text-main); + font-family: 'Georgia', 'Times New Roman', 'Iowan Old Style', serif; + font-size: 17px; + line-height: 1.7; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + color: var(--accent-blush); + text-decoration: none; + border-bottom: 1px solid rgba(224, 112, 154, 0.4); + transition: border-color 0.15s, color 0.15s; +} + +a:hover { + color: #ffb3d9; + border-bottom-color: var(--accent-blush); +} + +h1, h2, h3, h4 { + font-family: 'Georgia', 'Times New Roman', serif; + color: #fff; + font-weight: 600; + letter-spacing: -0.01em; +} + +code { + font-family: 'SF Mono', 'Consolas', monospace; + font-size: 0.9em; + background: rgba(255, 255, 255, 0.05); + padding: 1px 6px; + border-radius: 3px; +} diff --git a/src/app.d.ts b/src/app.d.ts new file mode 100644 index 0000000..ade0d7b --- /dev/null +++ b/src/app.d.ts @@ -0,0 +1,12 @@ +// See https://kit.svelte.dev/docs/types#app +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {}; diff --git a/src/app.html b/src/app.html new file mode 100644 index 0000000..b86cb39 --- /dev/null +++ b/src/app.html @@ -0,0 +1,15 @@ + + + + + + + + + Project RP + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/src/hooks.server.ts b/src/hooks.server.ts new file mode 100644 index 0000000..c45a78b --- /dev/null +++ b/src/hooks.server.ts @@ -0,0 +1,43 @@ +import { redirect, type Handle } from '@sveltejs/kit'; +import { getSessionUser, SESSION_COOKIE } from '$lib/server/auth'; + +const PUBLIC_PATHS = new Set([ + '/login', + '/auth/discord', + '/auth/discord/callback', + '/auth/local', + '/api/auth/me', + '/api/auth/local-enabled', + '/api/health' +]); + +const PUBLIC_PREFIXES = ['/auth/discord/callback', '/_app/', '/favicon']; + +function isPublic(pathname: string): boolean { + if (PUBLIC_PATHS.has(pathname)) return true; + return PUBLIC_PREFIXES.some((p) => pathname === p || pathname.startsWith(p)); +} + +export const handle: Handle = async ({ event, resolve }) => { + const { url, cookies } = event; + const cookie = cookies.get(SESSION_COOKIE); + const user = getSessionUser(cookie); + + // Expose the user to all routes via event.locals + (event.locals as any).user = user; + + if (!user && !isPublic(url.pathname)) { + // API routes: 401 JSON + if (url.pathname.startsWith('/api/')) { + return new Response(JSON.stringify({ error: 'authentication required' }), { + status: 401, + headers: { 'content-type': 'application/json' } + }); + } + // Page routes: redirect to /login + const next = url.pathname + url.search; + throw redirect(302, `/login?next=${encodeURIComponent(next)}`); + } + + return resolve(event); +}; diff --git a/src/lib/components/Badge.svelte b/src/lib/components/Badge.svelte new file mode 100644 index 0000000..638fa5a --- /dev/null +++ b/src/lib/components/Badge.svelte @@ -0,0 +1,37 @@ + + + + + + + diff --git a/src/lib/components/Button.svelte b/src/lib/components/Button.svelte new file mode 100644 index 0000000..ac1a1a3 --- /dev/null +++ b/src/lib/components/Button.svelte @@ -0,0 +1,69 @@ + + +{#if href} + + + +{:else} + +{/if} + + diff --git a/src/lib/components/EmptyState.svelte b/src/lib/components/EmptyState.svelte new file mode 100644 index 0000000..0afd513 --- /dev/null +++ b/src/lib/components/EmptyState.svelte @@ -0,0 +1,47 @@ + + +
+ {#if icon} + + {/if} +

{title}

+ {#if description} +

{description}

+ {/if} +
+ +
+
+ + diff --git a/src/lib/components/PageHeader.svelte b/src/lib/components/PageHeader.svelte new file mode 100644 index 0000000..d549a3d --- /dev/null +++ b/src/lib/components/PageHeader.svelte @@ -0,0 +1,65 @@ + + + + + diff --git a/src/lib/components/Panel.svelte b/src/lib/components/Panel.svelte new file mode 100644 index 0000000..39e3365 --- /dev/null +++ b/src/lib/components/Panel.svelte @@ -0,0 +1,18 @@ + + +
+ +
+ + diff --git a/src/lib/design/editorial.css b/src/lib/design/editorial.css new file mode 100644 index 0000000..f009f65 --- /dev/null +++ b/src/lib/design/editorial.css @@ -0,0 +1,161 @@ +/* Editorial — the design system for rp / sandbox / play. + Typography-led, dark background, blush-violet accent. No glassmorphic, no glow. */ + +:root { + /* Surface */ + --bg-void: #050312; + --bg-deep: #0a0518; + --bg-panel: #14101f; + --bg-elevated: #1c1730; + --bg-hover: #251f3d; + + /* Border */ + --border-faint: rgba(255, 255, 255, 0.06); + --border-glow: #d4adfc33; + --border-strong: rgba(255, 255, 255, 0.12); + + /* Text */ + --text-main: #e8e6ed; + --text-soft: #c8c5d0; + --text-muted: #8a8693; + --text-faint: #5a5663; + --text-on-accent: #0a0518; + + /* Accent (blush-violet, used sparingly) */ + --accent-blush: #e0709a; + --accent-blush-soft: rgba(224, 112, 154, 0.15); + --accent-blush-faint: rgba(224, 112, 154, 0.06); + --accent-neon: #bc13fe; + --accent-gold: #e8c547; + + /* Semantic */ + --success: #7fbf7f; + --warning: #e8c547; + --danger: #d96a7a; + + /* Type */ + --font-serif: 'Iowan Old Style', 'Georgia', 'Times New Roman', serif; + --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; + --font-mono: 'SF Mono', 'Monaco', 'Consolas', 'Courier New', monospace; + + /* Type scale (modular, ratio 1.25) */ + --text-xs: 12px; + --text-sm: 13px; + --text-base: 15px; + --text-md: 17px; + --text-lg: 20px; + --text-xl: 26px; + --text-2xl: 34px; + --text-3xl: 44px; + --text-4xl: 56px; + + /* Spacing */ + --space-1: 4px; + --space-2: 8px; + --space-3: 12px; + --space-4: 16px; + --space-5: 20px; + --space-6: 24px; + --space-8: 32px; + --space-10: 40px; + --space-12: 48px; + --space-16: 64px; + + /* Radius */ + --radius-sm: 4px; + --radius-md: 6px; + --radius-lg: 10px; + + /* Layout */ + --topbar-height: 56px; + --sidenav-width: 240px; + --footer-height: 44px; + --content-max-width: 920px; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html, body { + height: 100%; + background: var(--bg-void); + color: var(--text-main); + font-family: var(--font-sans); + font-size: var(--text-base); + line-height: 1.55; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +h1, h2, h3, h4 { + font-family: var(--font-serif); + font-weight: 600; + color: #fff; + letter-spacing: -0.01em; + line-height: 1.2; +} + +h1 { font-size: var(--text-3xl); margin-bottom: var(--space-3); } +h2 { font-size: var(--text-2xl); margin-bottom: var(--space-3); } +h3 { font-size: var(--text-xl); margin-bottom: var(--space-2); } +h4 { font-size: var(--text-lg); margin-bottom: var(--space-2); } + +p { color: var(--text-soft); margin-bottom: var(--space-3); } + +a { + color: var(--accent-blush); + text-decoration: none; + border-bottom: 1px solid var(--accent-blush-soft); + transition: border-color 0.15s; +} +a:hover { + border-bottom-color: var(--accent-blush); +} + +code { + font-family: var(--font-mono); + font-size: 0.88em; + background: rgba(255, 255, 255, 0.05); + padding: 1px 5px; + border-radius: var(--radius-sm); + color: var(--text-main); +} + +hr { + border: none; + border-top: 1px solid var(--border-faint); + margin: var(--space-6) 0; +} + +button, input, select, textarea { + font-family: inherit; + font-size: inherit; + color: inherit; +} + +input, textarea, select { + background: var(--bg-deep); + border: 1px solid var(--border-strong); + border-radius: var(--radius-md); + padding: 8px 12px; + width: 100%; + transition: border-color 0.15s; +} +input:focus, textarea:focus, select:focus { + outline: none; + border-color: var(--accent-blush); +} +label { + display: block; + font-size: var(--text-sm); + color: var(--text-muted); + margin-bottom: var(--space-1); +} + +::-webkit-scrollbar { width: 8px; height: 8px; } +::-webkit-scrollbar-track { background: transparent; } +::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 4px; } +::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.18); } diff --git a/src/lib/server/auth/index.ts b/src/lib/server/auth/index.ts new file mode 100644 index 0000000..d2f4c6b --- /dev/null +++ b/src/lib/server/auth/index.ts @@ -0,0 +1,18 @@ +// /var/www/rp/src/lib/server/auth/index.ts +// Re-exports for convenience. + +export { + getSessionUser, + createSession, + destroySession, + upsertUser, + SESSION_COOKIE, + SESSION_TTL_DURATION, + type SessionUser +} from './session'; + +export { + buildAuthorizeUrl, + exchangeCodeForUser, + isUserAllowed +} from './providers/discord'; diff --git a/src/lib/server/auth/local.ts b/src/lib/server/auth/local.ts new file mode 100644 index 0000000..2b551f0 --- /dev/null +++ b/src/lib/server/auth/local.ts @@ -0,0 +1,51 @@ +// /var/www/rp/src/lib/server/auth/local.ts +// Local password login — fallback for when Discord OAuth is unavailable. +// Reads LOCAL_ADMIN_PASSWORD from env. If unset, the local login is disabled. +// +// The session created here is identical to a Discord session: same cookie, +// same DB row, same user record. So the user has full app access either way. +// +// SECURITY: this is a development convenience, not a public auth flow. +// Leave LOCAL_ADMIN_PASSWORD unset in production deployments. + +import { timingSafeEqual } from 'node:crypto'; +import { env } from '$env/dynamic/private'; +import { getConfig } from '../db'; + +const LOCAL_USER_ID = 'local-admin'; + +export interface LocalUser { + id: string; + username: string; + global_name: string | null; + avatar: string | null; + email: string | null; + display_name: string | null; +} + +export function isLocalLoginEnabled(): boolean { + return !!env.LOCAL_ADMIN_PASSWORD && env.LOCAL_ADMIN_PASSWORD.length > 0; +} + +export function verifyLocalPassword(submitted: string): LocalUser | null { + if (!isLocalLoginEnabled()) return null; + const expected = env.LOCAL_ADMIN_PASSWORD!; + if (submitted.length !== expected.length) { + // Even on a length mismatch, do a constant-time-ish compare to avoid leaking length + timingSafeEqual(Buffer.from(submitted), Buffer.from(submitted)); + return null; + } + const ok = timingSafeEqual(Buffer.from(submitted), Buffer.from(expected)); + if (!ok) return null; + + // Read the local user's display name from the config table if previously saved + const displayName = getConfig('local_admin_display_name'); + return { + id: LOCAL_USER_ID, + username: 'local-admin', + global_name: 'Local Admin', + avatar: null, + email: null, + display_name: displayName ?? null + }; +} diff --git a/src/lib/server/auth/providers/discord.ts b/src/lib/server/auth/providers/discord.ts new file mode 100644 index 0000000..3720f7c --- /dev/null +++ b/src/lib/server/auth/providers/discord.ts @@ -0,0 +1,125 @@ +// /var/www/rp/src/lib/server/auth/providers/discord.ts +// Discord OAuth2 — "identify" scope only. +// Used for rp / sandbox / play subdomain login. + +import { randomBytes } from 'node:crypto'; +import { getConfig, setConfig } from '../../db'; + +const DISCORD_API = 'https://discord.com/api'; + +interface DiscordTokenResponse { + access_token: string; + token_type: string; + expires_in: number; + refresh_token: string; + scope: string; +} + +interface DiscordUser { + id: string; + username: string; + global_name: string | null; + avatar: string | null; + email: string | null; + verified: boolean; +} + +function requireEnv(name: string): string { + const v = process.env[name]; + if (!v) throw new Error(`Missing env: ${name}`); + return v; +} + +function getRedirectUri(): string { + return requireEnv('DISCORD_REDIRECT_URI'); +} + +function getClientId(): string { + return requireEnv('DISCORD_CLIENT_ID'); +} + +function getClientSecret(): string { + return requireEnv('DISCORD_CLIENT_SECRET'); +} + +const STATE_TTL_MS = 10 * 60 * 1000; // 10 minutes + +function storeState(state: string, redirectAfter: string): void { + setConfig(`discord_oauth_state:${state}`, JSON.stringify({ redirect: redirectAfter, created_at: Date.now() })); +} + +function consumeState(state: string): { redirect: string } | null { + const raw = getConfig(`discord_oauth_state:${state}`); + if (!raw) return null; + setConfig(`discord_oauth_state:${state}`, ''); // one-shot + try { + const parsed = JSON.parse(raw) as { redirect: string; created_at: number }; + if (Date.now() - parsed.created_at > STATE_TTL_MS) return null; + return { redirect: parsed.redirect }; + } catch { + return null; + } +} + +export function buildAuthorizeUrl(redirectAfter: string): string { + const state = randomBytes(24).toString('hex'); + storeState(state, redirectAfter); + const params = new URLSearchParams({ + client_id: getClientId(), + redirect_uri: getRedirectUri(), + response_type: 'code', + scope: 'identify', + state, + prompt: 'none' + }); + return `${DISCORD_API}/oauth2/authorize?${params.toString()}`; +} + +export async function exchangeCodeForUser( + code: string, + state: string +): Promise<{ user: DiscordUser; redirect: string }> { + const stateData = consumeState(state); + if (!stateData) { + throw new Error('Invalid or expired OAuth state. Please try logging in again.'); + } + + const tokenRes = await fetch(`${DISCORD_API}/oauth2/token`, { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: new URLSearchParams({ + client_id: getClientId(), + client_secret: getClientSecret(), + grant_type: 'authorization_code', + code, + redirect_uri: getRedirectUri() + }).toString() + }); + if (!tokenRes.ok) { + const text = await tokenRes.text(); + throw new Error(`Discord token exchange failed (${tokenRes.status}): ${text}`); + } + const token = (await tokenRes.json()) as DiscordTokenResponse; + + const userRes = await fetch(`${DISCORD_API}/users/@me`, { + headers: { Authorization: `Bearer ${token.access_token}` } + }); + if (!userRes.ok) { + const text = await userRes.text(); + throw new Error(`Discord user fetch failed (${userRes.status}): ${text}`); + } + const user = (await userRes.json()) as DiscordUser; + + return { user, redirect: stateData.redirect || '/' }; +} + +export function isUserAllowed(discordId: string): boolean { + const raw = getConfig('allowed_discord_ids'); + if (!raw) return false; + try { + const ids = JSON.parse(raw) as string[]; + return ids.includes(discordId); + } catch { + return false; + } +} diff --git a/src/lib/server/auth/session.ts b/src/lib/server/auth/session.ts new file mode 100644 index 0000000..1c1254c --- /dev/null +++ b/src/lib/server/auth/session.ts @@ -0,0 +1,115 @@ +// /var/www/rp/src/lib/server/auth/session.ts +// Signed cookie sessions. SESSION_SECRET must be set in .env. +// Session ID is a random 32-byte hex string. The session row in the DB +// carries the user_id and expires_at. The cookie carries only the session ID. + +import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto'; +import { db } from '../db'; + +const SESSION_TTL_DAYS = 30; +const SESSION_TTL_MS = SESSION_TTL_DAYS * 24 * 60 * 60 * 1000; +const COOKIE_NAME = 'thw_rp_session'; + +function getSessionSecret(): string { + const s = process.env.SESSION_SECRET; + if (!s || s.length < 32) { + throw new Error('SESSION_SECRET must be set in .env (32+ chars)'); + } + return s; +} + +function sign(value: string): string { + return createHmac('sha256', getSessionSecret()).update(value).digest('hex'); +} + +function makeCookieValue(sessionId: string): string { + return `${sessionId}.${sign(sessionId)}`; +} + +function verifyCookieValue(cookieValue: string): string | null { + const dotIdx = cookieValue.lastIndexOf('.'); + if (dotIdx < 1) return null; + const sessionId = cookieValue.slice(0, dotIdx); + const providedSig = cookieValue.slice(dotIdx + 1); + const expectedSig = sign(sessionId); + if (providedSig.length !== expectedSig.length) return null; + try { + if (!timingSafeEqual(Buffer.from(providedSig, 'hex'), Buffer.from(expectedSig, 'hex'))) { + return null; + } + } catch { + return null; + } + return sessionId; +} + +export interface SessionUser { + id: string; + username: string; + global_name: string | null; + avatar: string | null; + email: string | null; + display_name: string | null; + created_at?: string; + last_login_at?: string; +} + +export function createSession(userId: string): { id: string; cookie: string; expiresAt: Date } { + const id = randomBytes(32).toString('hex'); + const expiresAt = new Date(Date.now() + SESSION_TTL_MS); + db.prepare('INSERT INTO sessions (id, user_id, expires_at) VALUES (?, ?, ?)').run( + id, + userId, + expiresAt.toISOString() + ); + return { id, cookie: makeCookieValue(id), expiresAt }; +} + +export function destroySession(sessionId: string): void { + db.prepare('DELETE FROM sessions WHERE id = ?').run(sessionId); +} + +export function getSessionUser(cookieValue: string | undefined | null): SessionUser | null { + if (!cookieValue) return null; + const sessionId = verifyCookieValue(cookieValue); + if (!sessionId) return null; + const row = db + .prepare( + `SELECT u.id, u.username, u.global_name, u.avatar, u.email, u.display_name, + u.created_at, u.last_login_at, s.expires_at + FROM sessions s + JOIN users u ON u.id = s.user_id + WHERE s.id = ? AND s.expires_at > datetime('now')` + ) + .get(sessionId) as + | (SessionUser & { expires_at: string }) + | undefined; + if (!row) return null; + return { + id: row.id, + username: row.username, + global_name: row.global_name, + avatar: row.avatar, + email: row.email, + display_name: row.display_name ?? null, + created_at: row.created_at, + last_login_at: row.last_login_at + }; +} + +export function upsertUser(user: SessionUser): void { + db.prepare( + `INSERT INTO users (id, username, global_name, avatar, email, display_name, last_login_at) + VALUES (?, ?, ?, ?, ?, ?, datetime('now')) + ON CONFLICT(id) DO UPDATE SET + username = excluded.username, + global_name = excluded.global_name, + avatar = excluded.avatar, + email = excluded.email, + display_name = excluded.display_name, + last_login_at = datetime('now')` + ).run(user.id, user.username, user.global_name, user.avatar, user.email, user.display_name ?? null); +} + +export const SESSION_COOKIE = COOKIE_NAME; +export const SESSION_TTL_DURATION = SESSION_TTL_DAYS * 24 * 60 * 60; diff --git a/src/lib/server/db/index.ts b/src/lib/server/db/index.ts new file mode 100644 index 0000000..3400bfd --- /dev/null +++ b/src/lib/server/db/index.ts @@ -0,0 +1,68 @@ +// /var/www/rp/src/lib/server/db/index.ts +// Per-subdomain SQLite connection. +// One DB per subdomain (rp.db, sandbox.db, play.db) — separate from the main site's srh.db. + +import Database from 'better-sqlite3'; +import { mkdirSync } from 'node:fs'; +import { dirname } from 'node:path'; + +const DB_PATH = process.env.DATABASE_PATH || './data/app.db'; + +mkdirSync(dirname(DB_PATH), { recursive: true }); + +export const db = new Database(DB_PATH); +db.pragma('journal_mode = WAL'); +db.pragma('foreign_keys = ON'); + +// Schema: run on every boot, idempotent +db.exec(` + CREATE TABLE IF NOT EXISTS users ( + id TEXT PRIMARY KEY, + username TEXT NOT NULL, + global_name TEXT, + avatar TEXT, + email TEXT, + display_name TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + last_login_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + + -- Idempotent migration: add display_name column if it doesn't exist + -- (SQLite has no IF NOT EXISTS for ADD COLUMN before 3.35; we use try/catch via the wrapper) + PRAGMA table_info(users); + + CREATE TABLE IF NOT EXISTS sessions ( + id TEXT PRIMARY KEY, + user_id TEXT NOT NULL, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + expires_at TEXT NOT NULL, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE + ); + + CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions(user_id); + CREATE INDEX IF NOT EXISTS idx_sessions_expires ON sessions(expires_at); + + CREATE TABLE IF NOT EXISTS config ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL + ); +`); + +// Apply the display_name column migration. The CREATE TABLE above already +// includes display_name for fresh DBs, but for existing DBs from Phase 0 +// we need to ALTER. SQLite raises a "duplicate column" error if it exists, +// which we catch and ignore. +try { + db.exec('ALTER TABLE users ADD COLUMN display_name TEXT'); +} catch { + // Column already exists — fine. +} + +export function getConfig(key: string): string | null { + const row = db.prepare('SELECT value FROM config WHERE key = ?').get(key) as { value: string } | undefined; + return row?.value ?? null; +} + +export function setConfig(key: string, value: string): void { + db.prepare('INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)').run(key, value); +} diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte new file mode 100644 index 0000000..4e68dd5 --- /dev/null +++ b/src/routes/+error.svelte @@ -0,0 +1,38 @@ + + +
+
+

{$page.status} · Error

+

{$page.status === 404 ? 'Page not found' : 'Something went wrong'}

+

+ {$page.error?.message || 'The page you requested does not exist or could not be loaded.'} +

+
+ +
+
+
+ + diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte new file mode 100644 index 0000000..1574ac1 --- /dev/null +++ b/src/routes/+layout.svelte @@ -0,0 +1,307 @@ + + +{#if isChromeLess} + + +{:else} +
+
+
+ + + TheHowlingWhispers + · rp + +
+
+ {#if user} + + {#if userMenuOpen} +
(userMenuOpen = false)} role="menu"> +
+
{user.display_name || user.global_name || user.username}
+
@{user.username}
+
+ (userMenuOpen = false)}>Profile + (userMenuOpen = false)}>Settings + +
+ {/if} + {/if} +
+
+ +
+ + +
{ if (sidenavOpen) sidenavOpen = false; if (userMenuOpen) userMenuOpen = false; }}> + +
+
+ +
+ TheHowlingWhispers · rp.thehowlingwhispers.com +
+
+{/if} + + diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte new file mode 100644 index 0000000..7207781 --- /dev/null +++ b/src/routes/+page.svelte @@ -0,0 +1,96 @@ + + +{#if user} + +
+ +
+
+ +
+ + + + + + + + + + + + + + + +
+ +
+

+ Member since {joinedAgo(user.created_at)} · last sign-in {joinedAgo(user.last_login_at)} · + Discord ID {user.id} +

+
+{:else} +

Loading…

+{/if} + + diff --git a/src/routes/api/auth/local-enabled/+server.ts b/src/routes/api/auth/local-enabled/+server.ts new file mode 100644 index 0000000..422b948 --- /dev/null +++ b/src/routes/api/auth/local-enabled/+server.ts @@ -0,0 +1,6 @@ +import { json, type RequestHandler } from '@sveltejs/kit'; + +export const GET: RequestHandler = async () => { + const { isLocalLoginEnabled } = await import('$lib/server/auth/local'); + return json({ enabled: isLocalLoginEnabled() }); +}; diff --git a/src/routes/api/auth/logout/+server.ts b/src/routes/api/auth/logout/+server.ts new file mode 100644 index 0000000..2960bf3 --- /dev/null +++ b/src/routes/api/auth/logout/+server.ts @@ -0,0 +1,13 @@ +import { redirect, type RequestHandler } from '@sveltejs/kit'; + +export const POST: RequestHandler = async ({ cookies }) => { + const { destroySession, SESSION_COOKIE } = await import('$lib/server/auth'); + const cookie = cookies.get(SESSION_COOKIE); + if (cookie) { + const dotIdx = cookie.lastIndexOf('.'); + const sessionId = dotIdx > 0 ? cookie.slice(0, dotIdx) : cookie; + destroySession(sessionId); + } + cookies.delete(SESSION_COOKIE, { path: '/' }); + throw redirect(302, '/login'); +}; diff --git a/src/routes/api/auth/me/+server.ts b/src/routes/api/auth/me/+server.ts new file mode 100644 index 0000000..6c6f5a2 --- /dev/null +++ b/src/routes/api/auth/me/+server.ts @@ -0,0 +1,11 @@ +import { json, type RequestHandler } from '@sveltejs/kit'; + +export const GET: RequestHandler = async ({ cookies }) => { + const { getSessionUser, SESSION_COOKIE } = await import('$lib/server/auth'); + const cookie = cookies.get(SESSION_COOKIE); + const user = getSessionUser(cookie); + if (!user) { + return json({ user: null }, { status: 200 }); + } + return json({ user }); +}; diff --git a/src/routes/api/profile/+server.ts b/src/routes/api/profile/+server.ts new file mode 100644 index 0000000..08a0f71 --- /dev/null +++ b/src/routes/api/profile/+server.ts @@ -0,0 +1,35 @@ +import { json, type RequestHandler } from '@sveltejs/kit'; +import { getSessionUser, SESSION_COOKIE, upsertUser } from '$lib/server/auth'; + +export const GET: RequestHandler = async ({ cookies }) => { + const user = getSessionUser(cookies.get(SESSION_COOKIE)); + return json({ user }); +}; + +export const PUT: RequestHandler = async ({ cookies, request }) => { + const user = getSessionUser(cookies.get(SESSION_COOKIE)); + if (!user) return json({ error: 'authentication required' }, { status: 401 }); + + let payload: { display_name?: string }; + try { + payload = await request.json(); + } catch { + return json({ error: 'invalid json' }, { status: 400 }); + } + + const newName = (payload.display_name ?? '').toString().trim(); + if (newName.length > 80) { + return json({ error: 'display_name must be ≤80 chars' }, { status: 400 }); + } + + upsertUser({ + id: user.id, + username: user.username, + global_name: user.global_name, + avatar: user.avatar, + email: user.email, + display_name: newName || null + } as any); + + return json({ user: { ...user, display_name: newName || null } }); +}; diff --git a/src/routes/auth/discord/+server.ts b/src/routes/auth/discord/+server.ts new file mode 100644 index 0000000..508f93d --- /dev/null +++ b/src/routes/auth/discord/+server.ts @@ -0,0 +1,14 @@ +import { redirect, type RequestHandler } from '@sveltejs/kit'; + +export const GET: RequestHandler = async ({ url }) => { + // The redirect target after a successful login. + // Default to root; can be overridden via ?next= for deep links. + const next = url.searchParams.get('next'); + const safeNext = next && next.startsWith('/') && !next.startsWith('//') ? next : '/'; + const stateRedirect = encodeURIComponent(safeNext); + + // Lazy import to avoid loading the OAuth module unless we need it. + const { buildAuthorizeUrl } = await import('$lib/server/auth'); + const authorizeUrl = buildAuthorizeUrl(stateRedirect); + throw redirect(302, authorizeUrl); +}; diff --git a/src/routes/auth/discord/callback/+server.ts b/src/routes/auth/discord/callback/+server.ts new file mode 100644 index 0000000..5ddf112 --- /dev/null +++ b/src/routes/auth/discord/callback/+server.ts @@ -0,0 +1,64 @@ +import { redirect, type RequestHandler } from '@sveltejs/kit'; +import { env } from '$env/dynamic/private'; + +export const GET: RequestHandler = async ({ url, cookies }) => { + const code = url.searchParams.get('code'); + const state = url.searchParams.get('state'); + const error = url.searchParams.get('error'); + + if (error) { + throw redirect(302, `/login?error=${encodeURIComponent(error)}`); + } + if (!code || !state) { + throw redirect(302, '/login?error=missing_code_or_state'); + } + + const { exchangeCodeForUser, isUserAllowed, createSession, upsertUser, SESSION_COOKIE, SESSION_TTL_DURATION } = await import('$lib/server/auth'); + + let user, redirectTo; + try { + const result = await exchangeCodeForUser(code, state); + user = result.user; + redirectTo = result.redirect; + } catch (e) { + const msg = e instanceof Error ? e.message : 'unknown error'; + throw redirect(302, `/login?error=${encodeURIComponent(msg)}`); + } + + // Allowlist check — the user must be in ALLOWED_DISCORD_IDS to proceed + if (!isUserAllowed(user.id)) { + throw redirect(302, `/login?error=not_on_allowlist&discord_id=${encodeURIComponent(user.id)}`); + } + + // Upsert the user and create a session + upsertUser({ + id: user.id, + username: user.username, + global_name: user.global_name, + avatar: user.avatar, + email: user.email, + display_name: null + }); + const session = createSession(user.id); + + // Set the session cookie via SvelteKit's cookies API. + // SvelteKit's redirect() takes (status, location) — it does NOT accept headers. + // (The previous `throw redirect(302, loc, { 'set-cookie': ... })` silently + // dropped the cookie, which is why users got bounced back to /login after auth.) + // secure: true is conditional on the request scheme — allow http://127.0.0.1 + // testing to set the cookie, force https:// in production. + const isHttps = env.ORIGIN?.startsWith('https://') ?? false; + cookies.set(SESSION_COOKIE, session.cookie, { + path: '/', + httpOnly: true, + sameSite: 'lax', + secure: isHttps, + maxAge: SESSION_TTL_DURATION + }); + + // Route through /loading so the user sees a brief "Verifying your session…" buffer + // before landing on the destination. This replaces the previous direct-to-destination + // redirect, which showed a brief "Loading…" flash on the home page while the + // client-side fetch resolved. + throw redirect(302, `/loading?next=${encodeURIComponent(redirectTo)}`); +}; diff --git a/src/routes/auth/local/+server.ts b/src/routes/auth/local/+server.ts new file mode 100644 index 0000000..bde0780 --- /dev/null +++ b/src/routes/auth/local/+server.ts @@ -0,0 +1,47 @@ +// /var/www/rp/src/routes/auth/local/+server.ts +// Local password login endpoint. POST /auth/local with form-encoded `password`. +// On success, sets the same session cookie as Discord and redirects to next or /. + +import { redirect, type RequestHandler } from '@sveltejs/kit'; +import { env } from '$env/dynamic/private'; + +export const POST: RequestHandler = async ({ request, cookies, url }) => { + const { isLocalLoginEnabled, verifyLocalPassword } = await import('$lib/server/auth/local'); + const { createSession, upsertUser, SESSION_COOKIE, SESSION_TTL_DURATION } = await import('$lib/server/auth'); + + if (!isLocalLoginEnabled()) { + throw redirect(302, '/login?error=local_login_disabled'); + } + + const formData = await request.formData(); + const password = (formData.get('password') ?? '').toString(); + + const user = verifyLocalPassword(password); + if (!user) { + throw redirect(302, '/login?error=local_login_invalid'); + } + + upsertUser({ + id: user.id, + username: user.username, + global_name: user.global_name, + avatar: user.avatar, + email: user.email, + display_name: user.display_name + }); + const session = createSession(user.id); + + const isHttps = env.ORIGIN?.startsWith('https://') ?? false; + cookies.set(SESSION_COOKIE, session.cookie, { + path: '/', + httpOnly: true, + sameSite: 'lax', + secure: isHttps, + maxAge: SESSION_TTL_DURATION + }); + + const next = url.searchParams.get('next'); + const safeNext = next && next.startsWith('/') && !next.startsWith('//') ? next : '/'; + // Route through /loading — same buffer as the Discord flow. + throw redirect(302, `/loading?next=${encodeURIComponent(safeNext)}`); +}; diff --git a/src/routes/characters/+page.svelte b/src/routes/characters/+page.svelte new file mode 100644 index 0000000..f1bf6ac --- /dev/null +++ b/src/routes/characters/+page.svelte @@ -0,0 +1,21 @@ + + + + Coming soon + + + + + + + diff --git a/src/routes/loading/+page.svelte b/src/routes/loading/+page.svelte new file mode 100644 index 0000000..8094ca5 --- /dev/null +++ b/src/routes/loading/+page.svelte @@ -0,0 +1,198 @@ + + + + Verifying — TheHowlingWhispers + + +
+
+

TheHowlingWhispers

+ +
+ {#if status === 'verifying'} + + {:else if status === 'success'} + + {:else} + + {/if} +
+ +

+ {#if status === 'verifying'} + Verifying your session… + {:else if status === 'success'} + Signed in{#if user} as {user.display_name || user.global_name || user.username}{/if} + {:else} + Could not verify session + {/if} +

+ + {#if user} +

+ {#if user.avatar} + + {:else} + {(user.display_name || user.global_name || user.username).charAt(0).toUpperCase()} + {/if} + {user.display_name || user.global_name || user.username} +

+ {/if} + + {#if status === 'error'} +

{errorMsg}

+ {/if} +
+
+ + diff --git a/src/routes/login/+page.server.ts b/src/routes/login/+page.server.ts new file mode 100644 index 0000000..3b2183e --- /dev/null +++ b/src/routes/login/+page.server.ts @@ -0,0 +1,5 @@ +import { isLocalLoginEnabled } from '$lib/server/auth/local'; + +export const load = () => { + return { localLoginEnabled: isLocalLoginEnabled() }; +}; diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte new file mode 100644 index 0000000..74d2463 --- /dev/null +++ b/src/routes/login/+page.svelte @@ -0,0 +1,260 @@ + + + + Sign in — TheHowlingWhispers + + +
+
+

TheHowlingWhispers

+

Sign in

+

+ This site is locked away from normal users. Sign in to continue. +

+ + {#if errorMessage} + + {/if} + +
+ + + Continue with Discord + + + + + +
+ + {#if localLoginEnabled} +
or
+ +
+ + + +
+ {/if} + +

+ Access is restricted to a small allowlist. If you don't have access, you'll see + "Not on the allowlist" after signing in. +

+
+
+ + diff --git a/src/routes/profile/+page.svelte b/src/routes/profile/+page.svelte new file mode 100644 index 0000000..aba3c11 --- /dev/null +++ b/src/routes/profile/+page.svelte @@ -0,0 +1,106 @@ + + + + +{#if user} +
+ +
+ {#if user.avatar} + + {:else} +
{(user.global_name || user.username || '?').charAt(0).toUpperCase()}
+ {/if} +
+

{user.global_name || user.username}

+

@{user.username}

+

Discord ID {user.id}

+
+
+
+ + +

Display name

+

How you'd like to be addressed across the platform. Falls back to your Discord global name if blank.

+
+ + +
+ + {#if saveMessage} + {saveMessage} + {/if} +
+
+
+
+{:else} +

Loading…

+{/if} + + diff --git a/src/routes/rooms/+page.svelte b/src/routes/rooms/+page.svelte new file mode 100644 index 0000000..17609c4 --- /dev/null +++ b/src/routes/rooms/+page.svelte @@ -0,0 +1,21 @@ + + + + Coming soon + + + + + + + diff --git a/src/routes/settings/+page.svelte b/src/routes/settings/+page.svelte new file mode 100644 index 0000000..79f78a4 --- /dev/null +++ b/src/routes/settings/+page.svelte @@ -0,0 +1,57 @@ + + + + +
+ +
+

Account

+ Discord OAuth +
+

You sign in with Discord. Account-linking (email/password) ships in a later phase.

+
+ + +
+

Appearance

+ Editorial +
+

The Editorial design system is locked. A 3-design vote was scoped out in Phase 0 — re-introducing it would ship the other two (Velvet Refined, Neon Noir) as alternates.

+
+ + +

Session

+

Sign out of this device. To sign out of all devices, clear the cookies in your browser.

+
+ +
+
+ + +

Danger zone

+

Account deletion is disabled in this phase. Each subdomain keeps its own user table; deleting an account here only removes the row in this subdomain's database.

+
+ +
+
+
+ + diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a7a82ed3e4821bca1a1bff147af03efdd2c0c86e GIT binary patch literal 62 qcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x#lI52`pQ~&@w3ju%t literal 0 HcmV?d00001 diff --git a/static/favicon.png b/static/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..b21357cae08048bf01abf008d173e174925b536e GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1|;Q0k92}1TpU9x<|P05|KFa0^&iu}WpkQ< O!VI3SelF{r5}E+R^$`aE literal 0 HcmV?d00001 diff --git a/static/favicon.svg b/static/favicon.svg new file mode 100644 index 0000000..7d31d86 --- /dev/null +++ b/static/favicon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/svelte.config.js b/svelte.config.js new file mode 100644 index 0000000..511cc14 --- /dev/null +++ b/svelte.config.js @@ -0,0 +1,16 @@ +import adapter from '@sveltejs/adapter-node'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + preprocess: vitePreprocess(), + kit: { + adapter: adapter({ + out: 'build', + precompress: false + }), + csrf: { checkOrigin: false } + } +}; + +export default config; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..4344710 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..1ca7f9d --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,11 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [sveltekit()], + server: { + port: 3008, + host: '127.0.0.1', + strictPort: true + } +});