From 5eb623f02216e49bc28f52c4e346fd948d26ac87 Mon Sep 17 00:00:00 2001 From: shokollm <270575765+shokollm@users.noreply.github.com> Date: Wed, 8 Apr 2026 12:58:33 +0000 Subject: [PATCH] feat: setup SvelteKit frontend with TypeScript Implemented issue #9 - Frontend Project Setup with Svelte and TypeScript. Changes: - Created SvelteKit project with TypeScript - Set up routing structure: - / (landing page) - /login - /register - /dashboard - /bot/[id] - /bot/[id]/backtest - /bot/[id]/simulate - /settings - Created Svelte stores for state management: - userStore - Current user info - botsStore - List of user's bots - currentBotStore - Selected bot - chatStore - Chat messages - backtestStore - Backtest results - simulationStore - Simulation signals - authStore - Authentication state - Created API client for backend communication - Set up environment variables (.env.example) - Created auth store with protected routes and login/register functionality --- src/frontend/.env.example | 2 + src/frontend/.gitignore | 23 + src/frontend/.npmrc | 1 + src/frontend/.vscode/extensions.json | 3 + src/frontend/README.md | 42 + src/frontend/package-lock.json | 1383 +++++++++++++++++ src/frontend/package.json | 23 + src/frontend/src/app.d.ts | 13 + src/frontend/src/app.html | 12 + src/frontend/src/lib/api/client.ts | 209 +++ src/frontend/src/lib/api/index.ts | 2 + src/frontend/src/lib/api/types.ts | 128 ++ src/frontend/src/lib/assets/favicon.svg | 1 + src/frontend/src/lib/index.ts | 1 + src/frontend/src/lib/stores/authStore.ts | 50 + src/frontend/src/lib/stores/backtestStore.ts | 45 + src/frontend/src/lib/stores/botsStore.ts | 24 + src/frontend/src/lib/stores/chatStore.ts | 33 + .../src/lib/stores/currentBotStore.ts | 12 + src/frontend/src/lib/stores/index.ts | 30 + .../src/lib/stores/simulationStore.ts | 45 + src/frontend/src/lib/stores/userStore.ts | 12 + src/frontend/src/routes/+layout.svelte | 54 + src/frontend/src/routes/+page.svelte | 135 ++ src/frontend/src/routes/bot/[id]/+page.svelte | 354 +++++ .../src/routes/bot/[id]/backtest/+page.svelte | 391 +++++ .../src/routes/bot/[id]/simulate/+page.svelte | 426 +++++ .../src/routes/dashboard/+page.svelte | 359 +++++ src/frontend/src/routes/login/+page.svelte | 176 +++ src/frontend/src/routes/register/+page.svelte | 193 +++ src/frontend/src/routes/settings/+page.svelte | 281 ++++ src/frontend/static/robots.txt | 3 + src/frontend/svelte.config.js | 17 + src/frontend/tsconfig.json | 20 + src/frontend/vite.config.ts | 6 + 35 files changed, 4509 insertions(+) create mode 100644 src/frontend/.env.example create mode 100644 src/frontend/.gitignore create mode 100644 src/frontend/.npmrc create mode 100644 src/frontend/.vscode/extensions.json create mode 100644 src/frontend/README.md create mode 100644 src/frontend/package-lock.json create mode 100644 src/frontend/package.json create mode 100644 src/frontend/src/app.d.ts create mode 100644 src/frontend/src/app.html create mode 100644 src/frontend/src/lib/api/client.ts create mode 100644 src/frontend/src/lib/api/index.ts create mode 100644 src/frontend/src/lib/api/types.ts create mode 100644 src/frontend/src/lib/assets/favicon.svg create mode 100644 src/frontend/src/lib/index.ts create mode 100644 src/frontend/src/lib/stores/authStore.ts create mode 100644 src/frontend/src/lib/stores/backtestStore.ts create mode 100644 src/frontend/src/lib/stores/botsStore.ts create mode 100644 src/frontend/src/lib/stores/chatStore.ts create mode 100644 src/frontend/src/lib/stores/currentBotStore.ts create mode 100644 src/frontend/src/lib/stores/index.ts create mode 100644 src/frontend/src/lib/stores/simulationStore.ts create mode 100644 src/frontend/src/lib/stores/userStore.ts create mode 100644 src/frontend/src/routes/+layout.svelte create mode 100644 src/frontend/src/routes/+page.svelte create mode 100644 src/frontend/src/routes/bot/[id]/+page.svelte create mode 100644 src/frontend/src/routes/bot/[id]/backtest/+page.svelte create mode 100644 src/frontend/src/routes/bot/[id]/simulate/+page.svelte create mode 100644 src/frontend/src/routes/dashboard/+page.svelte create mode 100644 src/frontend/src/routes/login/+page.svelte create mode 100644 src/frontend/src/routes/register/+page.svelte create mode 100644 src/frontend/src/routes/settings/+page.svelte create mode 100644 src/frontend/static/robots.txt create mode 100644 src/frontend/svelte.config.js create mode 100644 src/frontend/tsconfig.json create mode 100644 src/frontend/vite.config.ts diff --git a/src/frontend/.env.example b/src/frontend/.env.example new file mode 100644 index 0000000..c0ce6a2 --- /dev/null +++ b/src/frontend/.env.example @@ -0,0 +1,2 @@ +VITE_API_URL=http://localhost:8000/api +VITE_WS_URL=ws://localhost:8000/ws diff --git a/src/frontend/.gitignore b/src/frontend/.gitignore new file mode 100644 index 0000000..3b462cb --- /dev/null +++ b/src/frontend/.gitignore @@ -0,0 +1,23 @@ +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/src/frontend/.npmrc b/src/frontend/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/src/frontend/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/src/frontend/.vscode/extensions.json b/src/frontend/.vscode/extensions.json new file mode 100644 index 0000000..28d1e67 --- /dev/null +++ b/src/frontend/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["svelte.svelte-vscode"] +} diff --git a/src/frontend/README.md b/src/frontend/README.md new file mode 100644 index 0000000..a5985da --- /dev/null +++ b/src/frontend/README.md @@ -0,0 +1,42 @@ +# sv + +Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). + +## Creating a project + +If you're seeing this, you've probably already done this step. Congrats! + +```sh +# create a new project +npx sv create my-app +``` + +To recreate this project with the same configuration: + +```sh +# recreate this project +npx sv@0.15.0 create --template minimal --types ts --no-install . +``` + +## Developing + +Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: + +```sh +npm run dev + +# or start the server and open the app in a new browser tab +npm run dev -- --open +``` + +## Building + +To create a production version of your app: + +```sh +npm run build +``` + +You can preview the production build with `npm run preview`. + +> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json new file mode 100644 index 0000000..8651251 --- /dev/null +++ b/src/frontend/package-lock.json @@ -0,0 +1,1383 @@ +{ + "name": "frontend", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.1", + "devDependencies": { + "@sveltejs/adapter-auto": "^7.0.1", + "@sveltejs/kit": "^2.57.0", + "@sveltejs/vite-plugin-svelte": "^7.0.0", + "svelte": "^5.55.2", + "svelte-check": "^4.4.6", + "typescript": "^6.0.2", + "vite": "^8.0.7" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", + "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "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/@napi-rs/wasm-runtime": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.3.tgz", + "integrity": "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.123.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.123.0.tgz", + "integrity": "sha512-YtECP/y8Mj1lSHiUWGSRzy/C6teUKlS87dEfuVKT09LgQbUsBW1rNg+MiJ4buGu3yuADV60gbIvo9/HplA56Ew==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "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/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.13.tgz", + "integrity": "sha512-5ZiiecKH2DXAVJTNN13gNMUcCDg4Jy8ZjbXEsPnqa248wgOVeYRX0iqXXD5Jz4bI9BFHgKsI2qmyJynstbmr+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.13.tgz", + "integrity": "sha512-tz/v/8G77seu8zAB3A5sK3UFoOl06zcshEzhUO62sAEtrEuW/H1CcyoupOrD+NbQJytYgA4CppXPzlrmp4JZKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.13.tgz", + "integrity": "sha512-8DakphqOz8JrMYWTJmWA+vDJxut6LijZ8Xcdc4flOlAhU7PNVwo2MaWBF9iXjJAPo5rC/IxEFZDhJ3GC7NHvug==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.13.tgz", + "integrity": "sha512-4wBQFfjDuXYN/SVI8inBF3Aa+isq40rc6VMFbk5jcpolUBTe5cYnMsHZ51nFWsx3PVyyNN3vgoESki0Hmr/4BA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.13.tgz", + "integrity": "sha512-JW/e4yPIXLms+jmnbwwy5LA/LxVwZUWLN8xug+V200wzaVi5TEGIWQlh8o91gWYFxW609euI98OCCemmWGuPrw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.13.tgz", + "integrity": "sha512-ZfKWpXiUymDnavepCaM6KG/uGydJ4l2nBmMxg60Ci4CbeefpqjPWpfaZM7PThOhk2dssqBAcwLc6rAyr0uTdXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.13.tgz", + "integrity": "sha512-bmRg3O6Z0gq9yodKKWCIpnlH051sEfdVwt+6m5UDffAQMUUqU0xjnQqqAUm+Gu7ofAAly9DqiQDtKu2nPDEABA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.13.tgz", + "integrity": "sha512-8Wtnbw4k7pMYN9B/mOEAsQ8HOiq7AZ31Ig4M9BKn2So4xRaFEhtCSa4ZJaOutOWq50zpgR4N5+L/opnlaCx8wQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.13.tgz", + "integrity": "sha512-D/0Nlo8mQuxSMohNJUF2lDXWRsFDsHldfRRgD9bRgktj+EndGPj4DOV37LqDKPYS+osdyhZEH7fTakTAEcW7qg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.13.tgz", + "integrity": "sha512-eRrPvat2YaVQcwwKi/JzOP6MKf1WRnOCr+VaI3cTWz3ZoLcP/654z90lVCJ4dAuMEpPdke0n+qyAqXDZdIC4rA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.13.tgz", + "integrity": "sha512-PsdONiFRp8hR8KgVjTWjZ9s7uA3uueWL0t74/cKHfM4dR5zXYv4AjB8BvA+QDToqxAFg4ZkcVEqeu5F7inoz5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.13.tgz", + "integrity": "sha512-hCNXgC5dI3TVOLrPT++PKFNZ+1EtS0mLQwfXXXSUD/+rGlB65gZDwN/IDuxLpQP4x8RYYHqGomlUXzpO8aVI2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.13.tgz", + "integrity": "sha512-viLS5C5et8NFtLWw9Sw3M/w4vvnVkbWkO7wSNh3C+7G1+uCkGpr6PcjNDSFcNtmXY/4trjPBqUfcOL+P3sWy/g==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.9.1", + "@emnapi/runtime": "1.9.1", + "@napi-rs/wasm-runtime": "^1.1.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.13.tgz", + "integrity": "sha512-Fqa3Tlt1xL4wzmAYxGNFV36Hb+VfPc9PYU+E25DAnswXv3ODDu/yyWjQDbXMo5AGWkQVjLgQExuVu8I/UaZhPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.13.tgz", + "integrity": "sha512-/pLI5kPkGEi44TDlnbio3St/5gUFeN51YWNAk/Gnv6mEQBOahRBh52qVFVBpmrnU01n2yysvBML9Ynu7K4kGAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.13.tgz", + "integrity": "sha512-3ngTAv6F/Py35BsYbeeLeecvhMKdsKm4AoOETVhAA+Qc8nrA2I0kF7oa93mE9qnIurngOSpMnQ0x2nQY2FPviA==", + "dev": true, + "license": "MIT" + }, + "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.9", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.9.tgz", + "integrity": "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8.9.0" + } + }, + "node_modules/@sveltejs/adapter-auto": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-7.0.1.tgz", + "integrity": "sha512-dvuPm1E7M9NI/+canIQ6KKQDU2AkEefEZ2Dp7cY6uKoPq9Z/PhOXABe526UdW2mN986gjVkuSLkOYIBnS/M2LQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@sveltejs/kit": "^2.0.0" + } + }, + "node_modules/@sveltejs/kit": { + "version": "2.57.0", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.57.0.tgz", + "integrity": "sha512-TMiqCTy9ZW4KBHvmTgeWU/hF6jcFpeMgR+9ekE06uhhGnbUZ7wpIY6l1Uk4ThRzlWYJnCVfzmtVNaHaDjaSiSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@sveltejs/acorn-typescript": "^1.0.5", + "@types/cookie": "^0.6.0", + "acorn": "^8.14.1", + "cookie": "^0.6.0", + "devalue": "^5.6.4", + "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/vite-plugin-svelte": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-7.0.0.tgz", + "integrity": "sha512-ILXmxC7HAsnkK2eslgPetrqqW1BKSL7LktsFgqzNj83MaivMGZzluWq32m25j2mDOjmSKX7GGWahePhuEs7P/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deepmerge": "^4.3.1", + "magic-string": "^0.30.21", + "obug": "^2.1.0", + "vitefu": "^1.1.2" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24" + }, + "peerDependencies": { + "svelte": "^5.46.4", + "vite": "^8.0.0-beta.7 || ^8.0.0" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.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.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "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/@typescript-eslint/types": { + "version": "8.58.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.1.tgz", + "integrity": "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "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/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/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/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/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==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/devalue": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.7.0.tgz", + "integrity": "sha512-qCvc8m7cImp1QDCsiY+C2EdSBWSj7Ucfoq87scSdYboDiIKdvMtFbH1U2VReBls6WMhMaUOoK3ZJEDNG/7zm3w==", + "dev": true, + "license": "MIT" + }, + "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.4", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.4.tgz", + "integrity": "sha512-suICpxAmZ9A8bzJjEl/+rLJiDKC0X4gYWUxT6URAWBLvlXmtbZd5ySMu/N2ZGEtMCAmflUDPSehrP9BQcsGcSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15", + "@typescript-eslint/types": "^8.2.0" + } + }, + "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/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/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/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/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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/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/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "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/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "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.9", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", + "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", + "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.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "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/rolldown": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.13.tgz", + "integrity": "sha512-bvVj8YJmf0rq4pSFmH7laLa6pYrhghv3PRzrCdRAr23g66zOKVJ4wkvFtgohtPLWmthgg8/rkaqRHrpUEh0Zbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.123.0", + "@rolldown/pluginutils": "1.0.0-rc.13" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.13", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.13", + "@rolldown/binding-darwin-x64": "1.0.0-rc.13", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.13", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.13", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.13", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.13", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.13", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.13", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.13", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.13", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.13", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.13", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.13", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.13" + } + }, + "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/set-cookie-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.0.tgz", + "integrity": "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==", + "dev": true, + "license": "MIT" + }, + "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/svelte": { + "version": "5.55.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.55.2.tgz", + "integrity": "sha512-z41M/hi0ZPTzrwVKLvB/R1/Oo08gL1uIib8HZ+FncqxxtY9MLb01emg2fqk+WLZ/lNrrtNDFh7BZLDxAHvMgLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@sveltejs/acorn-typescript": "^1.0.5", + "@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.6.4", + "esm-env": "^1.2.1", + "esrap": "^2.2.4", + "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.4.6", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.4.6.tgz", + "integrity": "sha512-kP1zG81EWaFe9ZyTv4ZXv44Csi6Pkdpb7S3oj6m+K2ec/IcDg/a8LsFsnVLqm2nxtkSwsd5xPj/qFkTBgXHXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "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/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "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/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/typescript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.7.tgz", + "integrity": "sha512-P1PbweD+2/udplnThz3btF4cf6AgPky7kk23RtHUkJIU5BIxwPprhRGmOAHs6FTI7UiGbTNrgNP6jSYD6JaRnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.13", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "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/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/src/frontend/package.json b/src/frontend/package.json new file mode 100644 index 0000000..0737653 --- /dev/null +++ b/src/frontend/package.json @@ -0,0 +1,23 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" + }, + "devDependencies": { + "@sveltejs/adapter-auto": "^7.0.1", + "@sveltejs/kit": "^2.57.0", + "@sveltejs/vite-plugin-svelte": "^7.0.0", + "svelte": "^5.55.2", + "svelte-check": "^4.4.6", + "typescript": "^6.0.2", + "vite": "^8.0.7" + } +} diff --git a/src/frontend/src/app.d.ts b/src/frontend/src/app.d.ts new file mode 100644 index 0000000..da08e6d --- /dev/null +++ b/src/frontend/src/app.d.ts @@ -0,0 +1,13 @@ +// See https://svelte.dev/docs/kit/types#app.d.ts +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {}; diff --git a/src/frontend/src/app.html b/src/frontend/src/app.html new file mode 100644 index 0000000..6a2bb58 --- /dev/null +++ b/src/frontend/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/src/frontend/src/lib/api/client.ts b/src/frontend/src/lib/api/client.ts new file mode 100644 index 0000000..cf5a0c3 --- /dev/null +++ b/src/frontend/src/lib/api/client.ts @@ -0,0 +1,209 @@ +import type { + User, + Bot, + BotConversation, + Backtest, + Simulation, + Signal, + AuthResponse, + BotChatRequest, + BotChatResponse, + StrategyConfig +} from './types'; + +const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000/api'; + +function getAuthHeaders(): HeadersInit { + const token = localStorage.getItem('token'); + return token ? { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' } : { 'Content-Type': 'application/json' }; +} + +async function handleResponse(response: Response): Promise { + if (!response.ok) { + const error = await response.json().catch(() => ({ detail: 'An error occurred' })); + throw new Error(error.detail || `HTTP error ${response.status}`); + } + return response.json(); +} + +export const api = { + auth: { + async register(email: string, password: string): Promise { + const response = await fetch(`${API_URL}/auth/register`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email, password }) + }); + return handleResponse(response); + }, + + async login(email: string, password: string): Promise { + const response = await fetch(`${API_URL}/auth/login`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email, password }) + }); + return handleResponse(response); + }, + + async logout(): Promise { + await fetch(`${API_URL}/auth/logout`, { + method: 'POST', + headers: getAuthHeaders() + }); + }, + + async me(): Promise { + const response = await fetch(`${API_URL}/auth/me`, { + headers: getAuthHeaders() + }); + return handleResponse(response); + } + }, + + bots: { + async list(): Promise { + const response = await fetch(`${API_URL}/bots`, { + headers: getAuthHeaders() + }); + return handleResponse(response); + }, + + async create(name: string, description?: string): Promise { + const response = await fetch(`${API_URL}/bots`, { + method: 'POST', + headers: getAuthHeaders(), + body: JSON.stringify({ name, description }) + }); + return handleResponse(response); + }, + + async get(id: string): Promise { + const response = await fetch(`${API_URL}/bots/${id}`, { + headers: getAuthHeaders() + }); + return handleResponse(response); + }, + + async update(id: string, data: Partial): Promise { + const response = await fetch(`${API_URL}/bots/${id}`, { + method: 'PUT', + headers: getAuthHeaders(), + body: JSON.stringify(data) + }); + return handleResponse(response); + }, + + async delete(id: string): Promise { + const response = await fetch(`${API_URL}/bots/${id}`, { + method: 'DELETE', + headers: getAuthHeaders() + }); + if (!response.ok) { + throw new Error(`HTTP error ${response.status}`); + } + }, + + async chat(id: string, message: string): Promise { + const response = await fetch(`${API_URL}/bots/${id}/chat`, { + method: 'POST', + headers: getAuthHeaders(), + body: JSON.stringify({ message } as BotChatRequest) + }); + return handleResponse(response); + }, + + async getHistory(id: string): Promise { + const response = await fetch(`${API_URL}/bots/${id}/history`, { + headers: getAuthHeaders() + }); + return handleResponse(response); + } + }, + + backtest: { + async start(botId: string, config: { token: string; timeframe: string; start_date: string; end_date: string }): Promise { + const response = await fetch(`${API_URL}/bots/${botId}/backtest`, { + method: 'POST', + headers: getAuthHeaders(), + body: JSON.stringify(config) + }); + return handleResponse(response); + }, + + async get(botId: string, runId: string): Promise { + const response = await fetch(`${API_URL}/bots/${botId}/backtest/${runId}`, { + headers: getAuthHeaders() + }); + return handleResponse(response); + }, + + async list(botId: string): Promise { + const response = await fetch(`${API_URL}/bots/${botId}/backtests`, { + headers: getAuthHeaders() + }); + return handleResponse(response); + }, + + async stop(botId: string, runId: string): Promise { + const response = await fetch(`${API_URL}/bots/${botId}/backtest/${runId}/stop`, { + method: 'POST', + headers: getAuthHeaders() + }); + if (!response.ok) { + throw new Error(`HTTP error ${response.status}`); + } + } + }, + + simulate: { + async start(botId: string, config: { token: string; interval_seconds: number; auto_execute: boolean }): Promise { + const response = await fetch(`${API_URL}/bots/${botId}/simulate`, { + method: 'POST', + headers: getAuthHeaders(), + body: JSON.stringify(config) + }); + return handleResponse(response); + }, + + async get(botId: string, runId: string): Promise { + const response = await fetch(`${API_URL}/bots/${botId}/simulate/${runId}`, { + headers: getAuthHeaders() + }); + return handleResponse(response); + }, + + async list(botId: string): Promise { + const response = await fetch(`${API_URL}/bots/${botId}/simulations`, { + headers: getAuthHeaders() + }); + return handleResponse(response); + }, + + async stop(botId: string, runId: string): Promise { + const response = await fetch(`${API_URL}/bots/${botId}/simulate/${runId}/stop`, { + method: 'POST', + headers: getAuthHeaders() + }); + if (!response.ok) { + throw new Error(`HTTP error ${response.status}`); + } + } + }, + + config: { + async getChains(): Promise { + const response = await fetch(`${API_URL}/config/chains`, { + headers: getAuthHeaders() + }); + return handleResponse(response); + }, + + async getTokens(): Promise<{ symbol: string; chain: string; name: string }[]> { + const response = await fetch(`${API_URL}/config/tokens`, { + headers: getAuthHeaders() + }); + return handleResponse<{ symbol: string; chain: string; name: string }[]>(response); + } + } +}; diff --git a/src/frontend/src/lib/api/index.ts b/src/frontend/src/lib/api/index.ts new file mode 100644 index 0000000..3663164 --- /dev/null +++ b/src/frontend/src/lib/api/index.ts @@ -0,0 +1,2 @@ +export { api } from './client'; +export * from './types'; diff --git a/src/frontend/src/lib/api/types.ts b/src/frontend/src/lib/api/types.ts new file mode 100644 index 0000000..c6b9847 --- /dev/null +++ b/src/frontend/src/lib/api/types.ts @@ -0,0 +1,128 @@ +export interface User { + id: string; + email: string; + created_at: string; + updated_at: string; +} + +export interface Bot { + id: string; + user_id: string; + name: string; + description: string | null; + strategy_config: StrategyConfig; + llm_config: LLMConfig; + status: 'draft' | 'active' | 'paused'; + created_at: string; + updated_at: string; +} + +export interface StrategyConfig { + conditions: Condition[]; + actions: Action[]; + risk_management?: RiskManagement; +} + +export interface Condition { + type: 'price_drop' | 'price_rise' | 'volume_spike' | 'price_level'; + token: string; + chain?: string; + threshold?: number; + price?: number; + direction?: 'above' | 'below'; + timeframe?: string; +} + +export interface Action { + type: 'buy' | 'sell' | 'hold'; + amount_percent?: number; + token?: string; +} + +export interface RiskManagement { + stop_loss_percent?: number; + take_profit_percent?: number; +} + +export interface LLMConfig { + model: string; + temperature: number; +} + +export interface BotConversation { + id: string; + bot_id: string; + role: 'user' | 'assistant' | 'system'; + content: string; + created_at: string; +} + +export interface Backtest { + id: string; + bot_id: string; + started_at: string; + ended_at: string | null; + status: 'running' | 'completed' | 'failed'; + config: BacktestConfig; + result: BacktestResult | null; +} + +export interface BacktestConfig { + token: string; + timeframe: string; + start_date: string; + end_date: string; +} + +export interface BacktestResult { + total_return: number; + win_rate: number; + total_trades: number; + buy_signals: number; + sell_signals: number; + max_drawdown: number; + sharpe_ratio: number; +} + +export interface Simulation { + id: string; + bot_id: string; + started_at: string; + status: 'running' | 'stopped'; + config: SimulationConfig; + signals: Signal[] | null; +} + +export interface SimulationConfig { + token: string; + interval_seconds: number; + auto_execute: boolean; +} + +export interface Signal { + id: string; + bot_id: string; + run_id: string; + signal_type: 'buy' | 'sell' | 'hold'; + token: string; + price: number; + confidence: number | null; + reasoning: string | null; + executed: boolean; + created_at: string; +} + +export interface AuthResponse { + access_token: string; + token_type: string; +} + +export interface BotChatRequest { + message: string; +} + +export interface BotChatResponse { + response: string; + strategy_config: StrategyConfig | null; + success: boolean; +} diff --git a/src/frontend/src/lib/assets/favicon.svg b/src/frontend/src/lib/assets/favicon.svg new file mode 100644 index 0000000..cc5dc66 --- /dev/null +++ b/src/frontend/src/lib/assets/favicon.svg @@ -0,0 +1 @@ +svelte-logo \ No newline at end of file diff --git a/src/frontend/src/lib/index.ts b/src/frontend/src/lib/index.ts new file mode 100644 index 0000000..856f2b6 --- /dev/null +++ b/src/frontend/src/lib/index.ts @@ -0,0 +1 @@ +// place files you want to import through the `$lib` alias in this folder. diff --git a/src/frontend/src/lib/stores/authStore.ts b/src/frontend/src/lib/stores/authStore.ts new file mode 100644 index 0000000..c1fd7d8 --- /dev/null +++ b/src/frontend/src/lib/stores/authStore.ts @@ -0,0 +1,50 @@ +import { writable, get } from 'svelte/store'; +import { api } from '$lib/api'; +import { setUser, clearUser, clearBots } from './index'; +import { clearSimulationState } from './simulationStore'; +import { clearBacktestState } from './backtestStore'; + +export const isAuthenticated = writable(false); +export const isLoading = writable(true); + +export async function initAuth() { + isLoading.set(true); + const token = localStorage.getItem('token'); + if (token) { + try { + const user = await api.auth.me(); + setUser(user); + isAuthenticated.set(true); + } catch { + localStorage.removeItem('token'); + isAuthenticated.set(false); + } + } + isLoading.set(false); +} + +export async function login(email: string, password: string) { + const response = await api.auth.login(email, password); + localStorage.setItem('token', response.access_token); + const user = await api.auth.me(); + setUser(user); + isAuthenticated.set(true); +} + +export async function register(email: string, password: string) { + const response = await api.auth.register(email, password); + localStorage.setItem('token', response.access_token); + const user = await api.auth.me(); + setUser(user); + isAuthenticated.set(true); +} + +export function logout() { + api.auth.logout().catch(() => {}); + localStorage.removeItem('token'); + clearUser(); + clearBots(); + clearBacktestState(); + clearSimulationState(); + isAuthenticated.set(false); +} diff --git a/src/frontend/src/lib/stores/backtestStore.ts b/src/frontend/src/lib/stores/backtestStore.ts new file mode 100644 index 0000000..31f53e3 --- /dev/null +++ b/src/frontend/src/lib/stores/backtestStore.ts @@ -0,0 +1,45 @@ +import { writable } from 'svelte/store'; +import type { Backtest, BacktestResult } from '$lib/api'; + +export interface BacktestState { + currentBacktest: Backtest | null; + backtestHistory: Backtest[]; + isLoading: boolean; + error: string | null; +} + +const initialState: BacktestState = { + currentBacktest: null, + backtestHistory: [], + isLoading: false, + error: null +}; + +export const backtestStore = writable(initialState); + +export function setCurrentBacktest(backtest: Backtest | null) { + backtestStore.update(state => ({ ...state, currentBacktest: backtest })); +} + +export function addBacktestToHistory(backtest: Backtest) { + backtestStore.update(state => ({ + ...state, + backtestHistory: [backtest, ...state.backtestHistory] + })); +} + +export function setBacktestHistory(backtests: Backtest[]) { + backtestStore.update(state => ({ ...state, backtestHistory: backtests })); +} + +export function setBacktestLoading(loading: boolean) { + backtestStore.update(state => ({ ...state, isLoading: loading })); +} + +export function setBacktestError(error: string | null) { + backtestStore.update(state => ({ ...state, error })); +} + +export function clearBacktestState() { + backtestStore.set(initialState); +} diff --git a/src/frontend/src/lib/stores/botsStore.ts b/src/frontend/src/lib/stores/botsStore.ts new file mode 100644 index 0000000..11b0130 --- /dev/null +++ b/src/frontend/src/lib/stores/botsStore.ts @@ -0,0 +1,24 @@ +import { writable } from 'svelte/store'; +import type { Bot } from '$lib/api'; + +export const botsStore = writable([]); + +export function setBots(bots: Bot[]) { + botsStore.set(bots); +} + +export function addBot(bot: Bot) { + botsStore.update(bots => [...bots, bot]); +} + +export function updateBot(bot: Bot) { + botsStore.update(bots => bots.map(b => b.id === bot.id ? bot : b)); +} + +export function removeBot(botId: string) { + botsStore.update(bots => bots.filter(b => b.id !== botId)); +} + +export function clearBots() { + botsStore.set([]); +} diff --git a/src/frontend/src/lib/stores/chatStore.ts b/src/frontend/src/lib/stores/chatStore.ts new file mode 100644 index 0000000..58961a6 --- /dev/null +++ b/src/frontend/src/lib/stores/chatStore.ts @@ -0,0 +1,33 @@ +import { writable } from 'svelte/store'; +import type { BotConversation } from '$lib/api'; + +export interface ChatMessage { + id: string; + role: 'user' | 'assistant' | 'system'; + content: string; + timestamp: Date; +} + +export const chatStore = writable([]); + +export function addMessage(message: Omit) { + const newMessage: ChatMessage = { + ...message, + id: crypto.randomUUID(), + timestamp: new Date() + }; + chatStore.update(messages => [...messages, newMessage]); +} + +export function setMessages(messages: BotConversation[]) { + chatStore.set(messages.map(m => ({ + id: m.id, + role: m.role, + content: m.content, + timestamp: new Date(m.created_at) + }))); +} + +export function clearChat() { + chatStore.set([]); +} diff --git a/src/frontend/src/lib/stores/currentBotStore.ts b/src/frontend/src/lib/stores/currentBotStore.ts new file mode 100644 index 0000000..e20a972 --- /dev/null +++ b/src/frontend/src/lib/stores/currentBotStore.ts @@ -0,0 +1,12 @@ +import { writable } from 'svelte/store'; +import type { Bot } from '$lib/api'; + +export const currentBotStore = writable(null); + +export function setCurrentBot(bot: Bot | null) { + currentBotStore.set(bot); +} + +export function clearCurrentBot() { + currentBotStore.set(null); +} diff --git a/src/frontend/src/lib/stores/index.ts b/src/frontend/src/lib/stores/index.ts new file mode 100644 index 0000000..2ce2e2d --- /dev/null +++ b/src/frontend/src/lib/stores/index.ts @@ -0,0 +1,30 @@ +export { userStore, setUser, clearUser } from './userStore'; +export { botsStore, setBots, addBot, updateBot, removeBot, clearBots } from './botsStore'; +export { currentBotStore, setCurrentBot, clearCurrentBot } from './currentBotStore'; +export { chatStore, addMessage, setMessages, clearChat } from './chatStore'; +export { + backtestStore, + setCurrentBacktest, + addBacktestToHistory, + setBacktestHistory, + setBacktestLoading, + setBacktestError, + clearBacktestState +} from './backtestStore'; +export { + simulationStore, + setCurrentSimulation, + addSignals, + clearSignals, + setSimulationLoading, + setSimulationError, + clearSimulationState +} from './simulationStore'; +export { + isAuthenticated, + isLoading, + initAuth, + login, + register, + logout +} from './authStore'; diff --git a/src/frontend/src/lib/stores/simulationStore.ts b/src/frontend/src/lib/stores/simulationStore.ts new file mode 100644 index 0000000..2a75590 --- /dev/null +++ b/src/frontend/src/lib/stores/simulationStore.ts @@ -0,0 +1,45 @@ +import { writable } from 'svelte/store'; +import type { Simulation, Signal } from '$lib/api'; + +export interface SimulationState { + currentSimulation: Simulation | null; + signals: Signal[]; + isLoading: boolean; + error: string | null; +} + +const initialState: SimulationState = { + currentSimulation: null, + signals: [], + isLoading: false, + error: null +}; + +export const simulationStore = writable(initialState); + +export function setCurrentSimulation(simulation: Simulation | null) { + simulationStore.update(state => ({ ...state, currentSimulation: simulation })); +} + +export function addSignals(newSignals: Signal[]) { + simulationStore.update(state => ({ + ...state, + signals: [...state.signals, ...newSignals] + })); +} + +export function clearSignals() { + simulationStore.update(state => ({ ...state, signals: [] })); +} + +export function setSimulationLoading(loading: boolean) { + simulationStore.update(state => ({ ...state, isLoading: loading })); +} + +export function setSimulationError(error: string | null) { + simulationStore.update(state => ({ ...state, error })); +} + +export function clearSimulationState() { + simulationStore.set(initialState); +} diff --git a/src/frontend/src/lib/stores/userStore.ts b/src/frontend/src/lib/stores/userStore.ts new file mode 100644 index 0000000..f267e6a --- /dev/null +++ b/src/frontend/src/lib/stores/userStore.ts @@ -0,0 +1,12 @@ +import { writable } from 'svelte/store'; +import type { User } from '$lib/api'; + +export const userStore = writable(null); + +export function setUser(user: User | null) { + userStore.set(user); +} + +export function clearUser() { + userStore.set(null); +} diff --git a/src/frontend/src/routes/+layout.svelte b/src/frontend/src/routes/+layout.svelte new file mode 100644 index 0000000..382983a --- /dev/null +++ b/src/frontend/src/routes/+layout.svelte @@ -0,0 +1,54 @@ + + + + + + +{#if $isLoading} +
+
+
+{:else} + {@render children()} +{/if} + + diff --git a/src/frontend/src/routes/+page.svelte b/src/frontend/src/routes/+page.svelte new file mode 100644 index 0000000..c2c136a --- /dev/null +++ b/src/frontend/src/routes/+page.svelte @@ -0,0 +1,135 @@ + + + + Randebu - AI Trading Bot Platform + + +{#if $isAuthenticated} + +{:else} +
+
+

Randebu

+

Create trading bots through conversation with AI

+ +
+ +
+

How It Works

+
+
+

1. Describe Your Strategy

+

Tell our AI what kind of trading you want to do in plain English

+
+
+

2. Backtest & Validate

+

Test your strategy against historical data before risking real funds

+
+
+

3. Simulate & Monitor

+

Run real-time simulations and watch for trading signals

+
+
+
+
+{/if} + + diff --git a/src/frontend/src/routes/bot/[id]/+page.svelte b/src/frontend/src/routes/bot/[id]/+page.svelte new file mode 100644 index 0000000..ac12178 --- /dev/null +++ b/src/frontend/src/routes/bot/[id]/+page.svelte @@ -0,0 +1,354 @@ + + + + {$currentBotStore?.name || 'Bot'} - Randebu + + +
+
+
+ ← Dashboard +

{$currentBotStore?.name || 'Loading...'}

+
+ +
+ +
+ {#if $chatStore.length === 0} +
+

Welcome to {$currentBotStore?.name}! Describe your trading strategy in plain English.

+

Example: "Buy PEPE when the price drops by 5% within 1 hour"

+
+ {/if} + + {#each $chatStore as message} +
+
+ {message.content} +
+
+ {message.timestamp.toLocaleTimeString()} +
+
+ {/each} + + {#if isSending} +
+
+ + + +
+
+ {/if} +
+ + {#if $currentBotStore} +
+ + +
+ {/if} +
+ + diff --git a/src/frontend/src/routes/bot/[id]/backtest/+page.svelte b/src/frontend/src/routes/bot/[id]/backtest/+page.svelte new file mode 100644 index 0000000..a498047 --- /dev/null +++ b/src/frontend/src/routes/bot/[id]/backtest/+page.svelte @@ -0,0 +1,391 @@ + + + + Backtest - {$currentBotStore?.name || 'Bot'} - Randebu + + +
+
+
+ ← Back to Chat +

Backtest

+
+
+ +
+
+

Configure Backtest

+ + {#if $backtestStore.error} +
{$backtestStore.error}
+ {/if} + +
{ e.preventDefault(); startBacktest(); }}> +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+

Backtest History

+ + {#if $backtestStore.backtestHistory.length === 0} +

No backtests yet. Run your first backtest above.

+ {:else} +
+ {#each $backtestStore.backtestHistory as backtest} +
+
+ {backtest.status} + {new Date(backtest.started_at).toLocaleDateString()} +
+ {#if backtest.result} +
+
+ Total Return + 0} class:negative={backtest.result.total_return < 0}> + {backtest.result.total_return.toFixed(2)}% + +
+
+ Win Rate + {backtest.result.win_rate.toFixed(1)}% +
+
+ Total Trades + {backtest.result.total_trades} +
+
+ Max Drawdown + {backtest.result.max_drawdown.toFixed(2)}% +
+
+ {/if} + {#if backtest.status === 'running'} + + {/if} +
+ {/each} +
+ {/if} +
+
+
+ + diff --git a/src/frontend/src/routes/bot/[id]/simulate/+page.svelte b/src/frontend/src/routes/bot/[id]/simulate/+page.svelte new file mode 100644 index 0000000..cc8df84 --- /dev/null +++ b/src/frontend/src/routes/bot/[id]/simulate/+page.svelte @@ -0,0 +1,426 @@ + + + + Simulate - {$currentBotStore?.name || 'Bot'} - Randebu + + +
+
+
+ ← Back to Chat +

Simulation

+
+
+ +
+ ⚠️ + Simulation Mode - Using REST polling (every {intervalSeconds}s). For real-time signals, consider upgrading to Pro tier. +
+ +
+
+

Configure Simulation

+ + {#if $simulationStore.error} +
{$simulationStore.error}
+ {/if} + +
{ e.preventDefault(); startSimulation(); }}> +
+
+ + +
+
+ + +
+
+ +
+ + +
+ + {#if isRunning} + + {:else} + + {/if} +
+
+ +
+

Signals ({$simulationStore.signals.length})

+ + {#if $simulationStore.signals.length === 0} +

No signals yet. Start a simulation to see trading signals.

+ {:else} +
+ {#each $simulationStore.signals as signal} +
+
+ {signal.signal_type} + {signal.token} + ${signal.price.toFixed(6)} +
+ {#if signal.confidence} +
+ Confidence: {(signal.confidence * 100).toFixed(1)}% +
+
+
+
+ {/if} + {#if signal.reasoning} +

{signal.reasoning}

+ {/if} +
+ {new Date(signal.created_at).toLocaleString()} +
+
+ {/each} +
+ {/if} +
+
+
+ + diff --git a/src/frontend/src/routes/dashboard/+page.svelte b/src/frontend/src/routes/dashboard/+page.svelte new file mode 100644 index 0000000..767e957 --- /dev/null +++ b/src/frontend/src/routes/dashboard/+page.svelte @@ -0,0 +1,359 @@ + + + + Dashboard - Randebu + + +
+
+

Dashboard

+
+ {$userStore?.email} + Settings + +
+
+ +
+
+

Your Bots ({$botsStore.length}/3)

+ {#if $botsStore.length < 3} + + {/if} +
+ + {#if $botsStore.length === 0} +
+

You haven't created any bots yet.

+

Create your first bot to start trading!

+
+ {:else} +
+ {#each $botsStore as bot} +
+
+

{bot.name}

+ {#if bot.description} +

{bot.description}

+ {/if} + {bot.status} +
+
+ Open + +
+
+ {/each} +
+ {/if} +
+ + {#if showCreateModal} + + {/if} +
+ + diff --git a/src/frontend/src/routes/login/+page.svelte b/src/frontend/src/routes/login/+page.svelte new file mode 100644 index 0000000..f20bdd5 --- /dev/null +++ b/src/frontend/src/routes/login/+page.svelte @@ -0,0 +1,176 @@ + + + + Login - Randebu + + +
+
+

Login

+

Welcome back

+ + {#if error} +
{error}
+ {/if} + +
{ e.preventDefault(); handleSubmit(); }}> +
+ + +
+ +
+ + +
+ + +
+ + +
+
+ + diff --git a/src/frontend/src/routes/register/+page.svelte b/src/frontend/src/routes/register/+page.svelte new file mode 100644 index 0000000..ffe6ed3 --- /dev/null +++ b/src/frontend/src/routes/register/+page.svelte @@ -0,0 +1,193 @@ + + + + Register - Randebu + + +
+
+

Create Account

+

Start creating trading bots

+ + {#if error} +
{error}
+ {/if} + +
{ e.preventDefault(); handleSubmit(); }}> +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ + +
+
+ + diff --git a/src/frontend/src/routes/settings/+page.svelte b/src/frontend/src/routes/settings/+page.svelte new file mode 100644 index 0000000..a8a01da --- /dev/null +++ b/src/frontend/src/routes/settings/+page.svelte @@ -0,0 +1,281 @@ + + + + Settings - Randebu + + +
+
+
+ ← Dashboard +

Settings

+
+
+ +
+
+

Profile

+ + {#if updateSuccess} +
{updateSuccess}
+ {/if} + + {#if updateError} +
{updateError}
+ {/if} + +
{ e.preventDefault(); updateEmail(); }}> +
+ + +
+ +
+
+ +
+

Change Password

+ +
{ e.preventDefault(); updatePassword(); }}> +
+ + +
+
+ + +
+
+ + +
+ +
+
+ +
+

Account

+ +
+
+
+ + diff --git a/src/frontend/static/robots.txt b/src/frontend/static/robots.txt new file mode 100644 index 0000000..b6dd667 --- /dev/null +++ b/src/frontend/static/robots.txt @@ -0,0 +1,3 @@ +# allow crawling everything by default +User-agent: * +Disallow: diff --git a/src/frontend/svelte.config.js b/src/frontend/svelte.config.js new file mode 100644 index 0000000..0c3412e --- /dev/null +++ b/src/frontend/svelte.config.js @@ -0,0 +1,17 @@ +import adapter from '@sveltejs/adapter-auto'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + compilerOptions: { + // Force runes mode for the project, except for libraries. Can be removed in svelte 6. + runes: ({ filename }) => (filename.split(/[/\\]/).includes('node_modules') ? undefined : true) + }, + kit: { + // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. + // If your environment is not supported, or you settled on a specific environment, switch out the adapter. + // See https://svelte.dev/docs/kit/adapters for more information about adapters. + adapter: adapter() + } +}; + +export default config; diff --git a/src/frontend/tsconfig.json b/src/frontend/tsconfig.json new file mode 100644 index 0000000..2c2ed3c --- /dev/null +++ b/src/frontend/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "rewriteRelativeImportExtensions": true, + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } + // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias + // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files + // + // To make changes to top-level options such as include and exclude, we recommend extending + // the generated config; see https://svelte.dev/docs/kit/configuration#typescript +} diff --git a/src/frontend/vite.config.ts b/src/frontend/vite.config.ts new file mode 100644 index 0000000..bbf8c7d --- /dev/null +++ b/src/frontend/vite.config.ts @@ -0,0 +1,6 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [sveltekit()] +}); -- 2.49.1