commit bf03750e768a61e90a3f8574cb133d2917a7a948 Author: Maxim Date: Tue Dec 16 22:16:45 2025 +0300 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1511055 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# Python +__pycache__/ +*.py[cod] +.venv/ +*.sqlite3 + +# Node +node_modules/ +dist/ + +# IDE +.idea/ +.vscode/ +*.swp + +# Env +.env +.env.* + +# Logs +*.log diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..26d7a83 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +services: + backend: + build: ./dota-random-builds-back + volumes: + - backend-data:/app/data + expose: + - "8000" + + frontend: + build: ./dota-random-builds-front + ports: + - "80:80" + depends_on: + - backend + +volumes: + backend-data: diff --git a/dota-random-builds-back/.dockerignore b/dota-random-builds-back/.dockerignore new file mode 100644 index 0000000..2e74d61 --- /dev/null +++ b/dota-random-builds-back/.dockerignore @@ -0,0 +1,8 @@ +.venv +__pycache__ +*.pyc +*.pyo +.git +.gitignore +*.sqlite3 +.env diff --git a/dota-random-builds-back/API.md b/dota-random-builds-back/API.md new file mode 100644 index 0000000..b2c77fb --- /dev/null +++ b/dota-random-builds-back/API.md @@ -0,0 +1,52 @@ +# API Endpoints for Frontend Compatibility + +This document defines the minimal backend surface expected by the Vue frontend. Default base path is `/api`; responses are JSON with `Content-Type: application/json`. + +## POST /api/randomize +- **Purpose:** Produce a randomized Dota 2 build and optionally return the full data sets used for rendering. +- **Request body:** + - `includeSkills` (boolean) — include a skill build suggestion. + - `includeAspect` (boolean) — include an aspect suggestion. + - `itemsCount` (number) — how many items to return (UI offers 3–6). +- **Response 200 body:** + - `hero` — object `{ id: string; name: string; primary: string; }`. + - `items` — array of `{ id: string; name: string; }` with length `itemsCount` (also used to hydrate cached items if you return the full pool). + - `skillBuild` (optional) — `{ id: string; name: string; description?: string; }` when `includeSkills` is true. + - `aspect` (optional) — string when `includeAspect` is true. + - `heroes` (optional) — full hero list to cache client-side; same shape as `hero`. + - `skillBuilds` (optional) — full skill build list; same shape as `skillBuild`. + - `aspects` (optional) — array of strings. +- **Example request:** +```json +{ + "includeSkills": true, + "includeAspect": false, + "itemsCount": 6 +} +``` +- **Example response:** +```json +{ + "hero": { "id": "pudge", "name": "Pudge", "primary": "strength" }, + "items": [ + { "id": "blink", "name": "Blink Dagger" }, + { "id": "bkb", "name": "Black King Bar" } + ], + "skillBuild": { "id": "hookdom", "name": "Hook/Rot Max", "description": "Max hook, then rot; early Flesh Heap." }, + "heroes": [{ "id": "axe", "name": "Axe", "primary": "strength" }], + "skillBuilds": [{ "id": "support", "name": "Support" }], + "aspects": ["Heroic Might"] +} +``` + +## Error Handling +- Use standard HTTP status codes (400 invalid input, 500 server error). +- Error payload shape: +```json +{ "message": "Failed to load random build" } +``` +- The UI surfaces `message` directly; keep it user-friendly. + +## Implementation Notes +- Ensure deterministic constraints: unique `id` per entity and avoid empty arrays when `includeSkills`/`includeAspect` are true. +- If backing data is static, you may return `heroes/items/skillBuilds/aspects` once per request; the frontend caches them after the first successful call. diff --git a/dota-random-builds-back/Dockerfile b/dota-random-builds-back/Dockerfile new file mode 100644 index 0000000..6fe7f15 --- /dev/null +++ b/dota-random-builds-back/Dockerfile @@ -0,0 +1,13 @@ +FROM python:3.12-slim + +WORKDIR /app + +# Install dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy application code +COPY . . + +# Run migrations, load data and start server +CMD ["sh", "-c", "python manage.py migrate && python manage.py load_static_data && python manage.py runserver 0.0.0.0:8000"] diff --git a/dota-random-builds-back/api/__init__.py b/dota-random-builds-back/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dota-random-builds-back/api/admin.py b/dota-random-builds-back/api/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/dota-random-builds-back/api/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/dota-random-builds-back/api/apps.py b/dota-random-builds-back/api/apps.py new file mode 100644 index 0000000..d87006d --- /dev/null +++ b/dota-random-builds-back/api/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ApiConfig(AppConfig): + name = 'api' diff --git a/dota-random-builds-back/api/data.py b/dota-random-builds-back/api/data.py new file mode 100644 index 0000000..7e446a9 --- /dev/null +++ b/dota-random-builds-back/api/data.py @@ -0,0 +1,51 @@ +import random +from typing import Dict + + +def generate_skill_build() -> Dict[int, str]: + """ + Generate a random skill build for levels 1-25 following Dota 2 rules: + - Ultimate (r) can only be skilled at levels 6, 12, 18 + - Talents can only be skilled at levels 10, 15, 20, 25 + - Basic abilities (q, w, e) can be skilled at other levels + - Each basic ability maxes at 4 points + - Ultimate maxes at 3 points + - One talent per tier (left OR right) + """ + skill_build: Dict[int, str] = {} + + # Track ability points spent + ability_points = {"q": 0, "w": 0, "e": 0, "r": 0} + max_points = {"q": 4, "w": 4, "e": 4, "r": 3} + + # Talent levels and which side to pick + talent_levels = [10, 15, 20, 25] + + # Ultimate levels + ult_levels = [6, 12, 18] + + for level in range(1, 26): + if level in talent_levels: + # Pick random talent side + skill_build[level] = random.choice(["left_talent", "right_talent"]) + elif level in ult_levels: + # Must skill ultimate if not maxed + if ability_points["r"] < max_points["r"]: + skill_build[level] = "r" + ability_points["r"] += 1 + else: + # Ultimate maxed, pick a basic ability + available = [a for a in ["q", "w", "e"] if ability_points[a] < max_points[a]] + if available: + choice = random.choice(available) + skill_build[level] = choice + ability_points[choice] += 1 + else: + # Regular level - pick a basic ability + available = [a for a in ["q", "w", "e"] if ability_points[a] < max_points[a]] + if available: + choice = random.choice(available) + skill_build[level] = choice + ability_points[choice] += 1 + + return skill_build diff --git a/dota-random-builds-back/api/management/__init__.py b/dota-random-builds-back/api/management/__init__.py new file mode 100644 index 0000000..5641a8e --- /dev/null +++ b/dota-random-builds-back/api/management/__init__.py @@ -0,0 +1 @@ +"" # Package marker diff --git a/dota-random-builds-back/api/management/commands/__init__.py b/dota-random-builds-back/api/management/commands/__init__.py new file mode 100644 index 0000000..5641a8e --- /dev/null +++ b/dota-random-builds-back/api/management/commands/__init__.py @@ -0,0 +1 @@ +"" # Package marker diff --git a/dota-random-builds-back/api/management/commands/load_static_data.py b/dota-random-builds-back/api/management/commands/load_static_data.py new file mode 100644 index 0000000..fd8ff0a --- /dev/null +++ b/dota-random-builds-back/api/management/commands/load_static_data.py @@ -0,0 +1,104 @@ +import re +import sys +from pathlib import Path + +from django.conf import settings +from django.core.management.base import BaseCommand, CommandError + +from api.models import Aspect, Hero, Item + +# Add data directory to path for imports +sys.path.insert(0, str(Path(settings.BASE_DIR) / "data")) +from facets import HERO_FACETS + + +def _strip_comments(text: str) -> str: + """Remove single-line and block comments from TypeScript data.""" + text = re.sub(r"/\*.*?\*/", "", text, flags=re.S) + lines = [] + for line in text.splitlines(): + stripped = line.strip() + if stripped.startswith("//"): + continue + lines.append(line) + return "\n".join(lines) + + +def _extract_objects(text: str, require_primary: bool = False): + cleaned = _strip_comments(text) + objects = [] + + for match in re.finditer(r"\{[^}]*\}", cleaned): + chunk = match.group(0) + entry = {} + id_match = re.search(r"['\"]?id['\"]?\s*:\s*(['\"])(.*?)\1", chunk) + name_match = re.search(r"['\"]?name['\"]?\s*:\s*(['\"])(.*?)\1", chunk) + if not (id_match and name_match): + continue + entry["slug"] = id_match.group(2) + entry["name"] = name_match.group(2) + + if require_primary: + primary_match = re.search(r"['\"]?primary['\"]?\s*:\s*(['\"])(.*?)\1", chunk) + if not primary_match: + continue + entry["primary"] = primary_match.group(2) + + if len(entry) == (3 if require_primary else 2): + objects.append(entry) + + return objects + + +class Command(BaseCommand): + help = "Load heroes, items and facets from data files into the database." + + def handle(self, *args, **options): + base_dir = Path(settings.BASE_DIR) + heroes_path = base_dir / "data" / "heroes.ts" + items_path = base_dir / "data" / "items.ts" + + if not heroes_path.exists() or not items_path.exists(): + raise CommandError("Missing data files in the ./data directory.") + + heroes = _extract_objects(heroes_path.read_text(encoding="utf-8"), require_primary=True) + items = _extract_objects(items_path.read_text(encoding="utf-8"), require_primary=False) + + if not heroes: + raise CommandError("Failed to parse heroes.ts") + if not items: + raise CommandError("Failed to parse items.ts") + + hero_created = item_created = aspect_created = 0 + + for hero in heroes: + hero_obj, created = Hero.objects.update_or_create( + name=hero["name"], + defaults={ + "primary": hero["primary"], + }, + ) + hero_created += int(created) + + # Load facets for this hero + facets = HERO_FACETS.get(hero["name"], []) + for facet_name in facets: + _, facet_created = Aspect.objects.update_or_create( + hero=hero_obj, + name=facet_name, + ) + aspect_created += int(facet_created) + + for item in items: + _, created = Item.objects.update_or_create( + name=item["name"], + ) + item_created += int(created) + + self.stdout.write( + self.style.SUCCESS( + f"Loaded {len(heroes)} heroes ({hero_created} new), " + f"{len(items)} items ({item_created} new), " + f"and {aspect_created} new facets." + ) + ) diff --git a/dota-random-builds-back/api/migrations/0001_initial.py b/dota-random-builds-back/api/migrations/0001_initial.py new file mode 100644 index 0000000..7fc9b04 --- /dev/null +++ b/dota-random-builds-back/api/migrations/0001_initial.py @@ -0,0 +1,31 @@ +# Generated by Django 6.0 on 2025-12-09 03:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Hero', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('external_id', models.CharField(max_length=100, unique=True)), + ('name', models.CharField(max_length=200)), + ('primary', models.CharField(choices=[('Strength', 'Strength'), ('Agility', 'Agility'), ('Intelligence', 'Intelligence')], max_length=20)), + ], + ), + migrations.CreateModel( + name='Item', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('external_id', models.CharField(max_length=100, unique=True)), + ('name', models.CharField(max_length=200)), + ], + ), + ] diff --git a/dota-random-builds-back/api/migrations/0002_remove_hero_external_id_remove_item_external_id.py b/dota-random-builds-back/api/migrations/0002_remove_hero_external_id_remove_item_external_id.py new file mode 100644 index 0000000..954594e --- /dev/null +++ b/dota-random-builds-back/api/migrations/0002_remove_hero_external_id_remove_item_external_id.py @@ -0,0 +1,21 @@ +# Generated by Django 6.0 on 2025-12-09 03:33 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='hero', + name='external_id', + ), + migrations.RemoveField( + model_name='item', + name='external_id', + ), + ] diff --git a/dota-random-builds-back/api/migrations/0003_aspect.py b/dota-random-builds-back/api/migrations/0003_aspect.py new file mode 100644 index 0000000..02ffdeb --- /dev/null +++ b/dota-random-builds-back/api/migrations/0003_aspect.py @@ -0,0 +1,25 @@ +# Generated by Django 6.0 on 2025-12-09 18:43 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0002_remove_hero_external_id_remove_item_external_id'), + ] + + operations = [ + migrations.CreateModel( + name='Aspect', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('hero', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='aspects', to='api.hero')), + ], + options={ + 'unique_together': {('hero', 'name')}, + }, + ), + ] diff --git a/dota-random-builds-back/api/migrations/0004_buildofday.py b/dota-random-builds-back/api/migrations/0004_buildofday.py new file mode 100644 index 0000000..0c30389 --- /dev/null +++ b/dota-random-builds-back/api/migrations/0004_buildofday.py @@ -0,0 +1,25 @@ +# Generated by Django 6.0 on 2025-12-16 18:42 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0003_aspect'), + ] + + operations = [ + migrations.CreateModel( + name='BuildOfDay', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateField(unique=True)), + ('skill_build', models.JSONField(default=dict)), + ('aspect', models.CharField(blank=True, max_length=200, null=True)), + ('hero', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.hero')), + ('items', models.ManyToManyField(to='api.item')), + ], + ), + ] diff --git a/dota-random-builds-back/api/migrations/__init__.py b/dota-random-builds-back/api/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dota-random-builds-back/api/models.py b/dota-random-builds-back/api/models.py new file mode 100644 index 0000000..0087c3b --- /dev/null +++ b/dota-random-builds-back/api/models.py @@ -0,0 +1,48 @@ +from django.db import models + + +class Hero(models.Model): + PRIMARY_ATTR_STRENGTH = "Strength" + PRIMARY_ATTR_AGILITY = "Agility" + PRIMARY_ATTR_INTELLIGENCE = "Intelligence" + + PRIMARY_CHOICES = ( + (PRIMARY_ATTR_STRENGTH, "Strength"), + (PRIMARY_ATTR_AGILITY, "Agility"), + (PRIMARY_ATTR_INTELLIGENCE, "Intelligence"), + ) + + name = models.CharField(max_length=200) + primary = models.CharField(max_length=20, choices=PRIMARY_CHOICES) + + def __str__(self) -> str: # pragma: no cover - convenience only + return f"{self.name} ({self.primary})" + + +class Item(models.Model): + name = models.CharField(max_length=200) + + def __str__(self) -> str: # pragma: no cover - convenience only + return self.name + + +class Aspect(models.Model): + hero = models.ForeignKey(Hero, on_delete=models.CASCADE, related_name="aspects") + name = models.CharField(max_length=200) + + class Meta: + unique_together = ["hero", "name"] + + def __str__(self) -> str: # pragma: no cover - convenience only + return f"{self.hero.name} - {self.name}" + + +class BuildOfDay(models.Model): + date = models.DateField(unique=True) + hero = models.ForeignKey(Hero, on_delete=models.CASCADE) + items = models.ManyToManyField(Item) + skill_build = models.JSONField(default=dict) + aspect = models.CharField(max_length=200, blank=True, null=True) + + def __str__(self) -> str: # pragma: no cover - convenience only + return f"Build of {self.date} - {self.hero.name}" diff --git a/dota-random-builds-back/api/serializers.py b/dota-random-builds-back/api/serializers.py new file mode 100644 index 0000000..271ddf9 --- /dev/null +++ b/dota-random-builds-back/api/serializers.py @@ -0,0 +1,38 @@ +from rest_framework import serializers + +from api.models import Hero, Item + + +class HeroSerializer(serializers.ModelSerializer): + primary = serializers.SerializerMethodField() + + class Meta: + model = Hero + fields = ["id", "name", "primary"] + + def get_primary(self, obj): + return obj.primary.lower() + + +class ItemSerializer(serializers.ModelSerializer): + class Meta: + model = Item + fields = ["id", "name"] + + +class RandomizeBuildRequestSerializer(serializers.Serializer): + includeSkills = serializers.BooleanField() + includeAspect = serializers.BooleanField() + itemsCount = serializers.IntegerField(min_value=1) + heroId = serializers.IntegerField(required=False, allow_null=True) + + +class RandomizeBuildResponseSerializer(serializers.Serializer): + hero = HeroSerializer() + items = ItemSerializer(many=True) + skillBuild = serializers.DictField( + child=serializers.CharField(), + required=False, + help_text="Map of level (1-25) to skill (q/w/e/r/left_talent/right_talent)" + ) + aspect = serializers.CharField(required=False) diff --git a/dota-random-builds-back/api/tests.py b/dota-random-builds-back/api/tests.py new file mode 100644 index 0000000..94b2da1 --- /dev/null +++ b/dota-random-builds-back/api/tests.py @@ -0,0 +1,69 @@ +from django.core.management import call_command +from django.test import TestCase +from rest_framework import status +from rest_framework.test import APIClient + +from api.models import Hero, Item + + +class RandomizeBuildAPITest(TestCase): + def setUp(self): + self.client = APIClient() + self.hero = Hero.objects.create(name="Axe", primary="Strength") + Item.objects.bulk_create( + [ + Item(name="Blink Dagger"), + Item(name="Black King Bar"), + Item(name="Boots of Travel"), + Item(name="Force Staff"), + ] + ) + + def test_randomize_with_skills(self): + response = self.client.post( + "/api/randomize", + {"includeSkills": True, "includeAspect": False, "itemsCount": 3}, + format="json", + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + data = response.json() + + self.assertIn("hero", data) + self.assertEqual(len(data.get("items", [])), 3) + self.assertIn("skillBuild", data) + self.assertTrue(data.get("skillBuilds")) + self.assertNotIn("aspect", data) + + def test_randomize_with_aspect_only(self): + response = self.client.post( + "/api/randomize", + {"includeSkills": False, "includeAspect": True, "itemsCount": 2}, + format="json", + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + data = response.json() + + self.assertIn("aspect", data) + self.assertTrue(data.get("aspects")) + self.assertNotIn("skillBuild", data) + self.assertEqual(len(data.get("items", [])), 2) + + def test_rejects_invalid_payload(self): + response = self.client.post( + "/api/randomize", + {"includeSkills": "yes", "includeAspect": True, "itemsCount": 0}, + format="json", + ) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn("message", response.json()) + + +class LoadStaticDataCommandTest(TestCase): + def test_loads_heroes_and_items(self): + call_command("load_static_data") + + self.assertGreater(Hero.objects.count(), 0) + self.assertGreater(Item.objects.count(), 0) diff --git a/dota-random-builds-back/api/urls.py b/dota-random-builds-back/api/urls.py new file mode 100644 index 0000000..21133cc --- /dev/null +++ b/dota-random-builds-back/api/urls.py @@ -0,0 +1,9 @@ +from django.urls import path + +from .views import BuildOfDayView, HeroesListView, RandomizeBuildView + +urlpatterns = [ + path("randomize", RandomizeBuildView.as_view(), name="randomize-build"), + path("heroes", HeroesListView.as_view(), name="heroes-list"), + path("build-of-day", BuildOfDayView.as_view(), name="build-of-day"), +] diff --git a/dota-random-builds-back/api/views.py b/dota-random-builds-back/api/views.py new file mode 100644 index 0000000..b8f46e3 --- /dev/null +++ b/dota-random-builds-back/api/views.py @@ -0,0 +1,164 @@ +from datetime import date + +from rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from api.models import Aspect, BuildOfDay, Hero, Item +from api.serializers import ( + HeroSerializer, + ItemSerializer, + RandomizeBuildRequestSerializer, +) + +from .data import generate_skill_build + + +class HeroesListView(APIView): + """ + GET: Return all available heroes for selection. + """ + + def get(self, request): + heroes = Hero.objects.all().order_by("name") + return Response(HeroSerializer(heroes, many=True).data, status=status.HTTP_200_OK) + + +class RandomizeBuildView(APIView): + """ + POST: Generate a random Dota 2 build with hero, items, and optionally skills/aspects. + """ + + def get_serializer(self, *args, **kwargs): + return RandomizeBuildRequestSerializer(*args, **kwargs) + + def post(self, request): + serializer = RandomizeBuildRequestSerializer(data=request.data) + if not serializer.is_valid(): + return Response( + {"message": self._format_errors(serializer.errors)}, + status=status.HTTP_400_BAD_REQUEST, + ) + + validated_data = serializer.validated_data + include_skills = validated_data["includeSkills"] + include_aspect = validated_data["includeAspect"] + items_count = validated_data["itemsCount"] + hero_id = validated_data.get("heroId") + + hero_count = Hero.objects.count() + item_count = Item.objects.count() + + if hero_count == 0: + return Response( + {"message": "No heroes available. Load data first."}, + status=status.HTTP_400_BAD_REQUEST, + ) + + if item_count < items_count: + return Response( + {"message": f"Not enough items available. Requested {items_count}, found {item_count}."}, + status=status.HTTP_400_BAD_REQUEST, + ) + + if hero_id: + hero_obj = Hero.objects.filter(id=hero_id).first() + if not hero_obj: + return Response( + {"message": f"Hero with id {hero_id} not found."}, + status=status.HTTP_400_BAD_REQUEST, + ) + else: + hero_obj = Hero.objects.order_by("?").first() + item_objs = Item.objects.order_by("?")[:items_count] + + response_data = { + "hero": HeroSerializer(hero_obj).data, + "items": ItemSerializer(item_objs, many=True).data, + } + + if include_skills: + response_data["skillBuild"] = generate_skill_build() + + if include_aspect: + hero_aspects = Aspect.objects.filter(hero=hero_obj) + if hero_aspects.exists(): + aspect_obj = hero_aspects.order_by("?").first() + response_data["aspect"] = aspect_obj.name + + return Response(response_data, status=status.HTTP_200_OK) + + def _format_errors(self, errors: dict) -> str: + """Format serializer errors into a readable message.""" + messages = [] + for field, field_errors in errors.items(): + for error in field_errors: + if field == "itemsCount" and "greater than or equal to" in str(error): + messages.append("itemsCount must be at least 1.") + elif "valid" in str(error).lower() or "required" in str(error).lower(): + if field in ("includeSkills", "includeAspect"): + messages.append("includeSkills and includeAspect must be boolean.") + elif field == "itemsCount": + messages.append("itemsCount must be a number.") + else: + messages.append(f"{field}: {error}") + return messages[0] if messages else "Invalid request data." + + +class BuildOfDayView(APIView): + """ + GET: Return the build of the day. Generates one if it doesn't exist for today. + """ + + ITEMS_COUNT = 6 + + def get(self, request): + today = date.today() + + build = BuildOfDay.objects.filter(date=today).first() + if not build: + build = self._generate_build_of_day(today) + if build is None: + return Response( + {"message": "Unable to generate build. Load data first."}, + status=status.HTTP_400_BAD_REQUEST, + ) + + response_data = { + "date": build.date.isoformat(), + "hero": HeroSerializer(build.hero).data, + "items": ItemSerializer(build.items.all(), many=True).data, + "skillBuild": build.skill_build, + } + + if build.aspect: + response_data["aspect"] = build.aspect + + return Response(response_data, status=status.HTTP_200_OK) + + def _generate_build_of_day(self, today: date) -> BuildOfDay | None: + """Generate and save a new build of the day.""" + hero_count = Hero.objects.count() + item_count = Item.objects.count() + + if hero_count == 0 or item_count < self.ITEMS_COUNT: + return None + + hero_obj = Hero.objects.order_by("?").first() + item_objs = list(Item.objects.order_by("?")[: self.ITEMS_COUNT]) + + aspect_name = None + hero_aspects = Aspect.objects.filter(hero=hero_obj) + if hero_aspects.exists(): + aspect_obj = hero_aspects.order_by("?").first() + aspect_name = aspect_obj.name + + build = BuildOfDay.objects.create( + date=today, + hero=hero_obj, + skill_build=generate_skill_build(), + aspect=aspect_name, + ) + build.items.set(item_objs) + + return build diff --git a/dota-random-builds-back/config/__init__.py b/dota-random-builds-back/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dota-random-builds-back/config/asgi.py b/dota-random-builds-back/config/asgi.py new file mode 100644 index 0000000..ffbb5f5 --- /dev/null +++ b/dota-random-builds-back/config/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for config project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/6.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') + +application = get_asgi_application() diff --git a/dota-random-builds-back/config/settings.py b/dota-random-builds-back/config/settings.py new file mode 100644 index 0000000..eef9ade --- /dev/null +++ b/dota-random-builds-back/config/settings.py @@ -0,0 +1,129 @@ +""" +Django settings for config project. + +Generated by 'django-admin startproject' using Django 6.0. + +For more information on this file, see +https://docs.djangoproject.com/en/6.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/6.0/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-a#7v1um&b88$k)2nm7hvxe__o2c(gz=t5%)1)*oaij6u%+i((=' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = ['*'] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + "corsheaders", + "rest_framework", + "api", +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'corsheaders.middleware.CorsMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'config.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'config.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/6.0/ref/settings/#databases + +import os + +DATA_DIR = BASE_DIR / 'data' +DATA_DIR.mkdir(exist_ok=True) + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': DATA_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/6.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/6.0/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/6.0/howto/static-files/ + +STATIC_URL = 'static/' + +# CORS settings +CORS_ALLOW_ALL_ORIGINS = True diff --git a/dota-random-builds-back/config/urls.py b/dota-random-builds-back/config/urls.py new file mode 100644 index 0000000..78463b1 --- /dev/null +++ b/dota-random-builds-back/config/urls.py @@ -0,0 +1,23 @@ +""" +URL configuration for config project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/6.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import include, path + +urlpatterns = [ + path('admin/', admin.site.urls), + path("api/", include("api.urls")), +] diff --git a/dota-random-builds-back/config/wsgi.py b/dota-random-builds-back/config/wsgi.py new file mode 100644 index 0000000..4ced574 --- /dev/null +++ b/dota-random-builds-back/config/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for config project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/6.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') + +application = get_wsgi_application() diff --git a/dota-random-builds-back/data/aspects.ts b/dota-random-builds-back/data/aspects.ts new file mode 100644 index 0000000..86a000c --- /dev/null +++ b/dota-random-builds-back/data/aspects.ts @@ -0,0 +1,7 @@ +export const aspects = [ + 'Carry', + 'Support', + 'Roamer', + 'Jungler', + 'Pusher' +] diff --git a/dota-random-builds-back/data/facets.py b/dota-random-builds-back/data/facets.py new file mode 100644 index 0000000..c6d554b --- /dev/null +++ b/dota-random-builds-back/data/facets.py @@ -0,0 +1,128 @@ +# Hero Facets data from Dota 2 patch 7.36+ +# Each hero has 2 facets that modify their abilities + +HERO_FACETS = { + # Strength Heroes + "Abaddon": ["Mephitic Shroud", "The Quickening"], + "Alchemist": ["Seed Money", "Mixologist"], + "Axe": ["One Man Army", "Call Out"], + "Beastmaster": ["Wild Hunt", "Beast Mode"], + "Brewmaster": ["Roll Out the Barrel", "Drunken Master"], + "Bristleback": ["Seeing Red", "Prickly"], + "Centaur Warrunner": ["Horsepower", "Rawhide"], + "Chaos Knight": ["Irrationality", "Schism"], + "Clockwerk": ["Expanded Armature", "Overclock"], + "Dawnbreaker": ["Solar Guardian", "Gleaming Hammer"], + "Doom": ["Impending Doom", "Gluttony"], + "Dragon Knight": ["Wyrm's Wrath", "Corrosive Dragon"], + "Earth Spirit": ["Ready to Roll", "Stepping Stone"], + "Earthshaker": ["Tectonic Buildup", "Slugger"], + "Elder Titan": ["Deconstruction", "Momentum"], + "Huskar": ["Bloodbath", "Incendiary"], + "Kunkka": ["Grog Blossom", "Tideskipper"], + "Legion Commander": ["Stonehall Plate", "Spoils of War"], + "Lifestealer": ["Corpse Eater", "Rage Demon"], + "Lycan": ["Spirit Wolves", "Alpha Wolves"], + "Magnus": ["Reverse Polarity Field", "Hot-Blooded"], + "Mars": ["Shock Troops", "Arena of Blood"], + "Night Stalker": ["Blinding Void", "Heart of Darkness"], + "Ogre Magi": ["Fat Chances", "Learning Curve"], + "Omniknight": ["Degen Aura", "Ardent Revelation"], + "Phoenix": ["Dying Light", "Spread the Fire"], + "Primal Beast": ["Ferocity", "Irascible"], + "Pudge": ["Flayers Hook", "Fresh Meat"], + "Slardar": ["Brineguard", "Leg Day"], + "Snapfire": ["Ricochet II", "Full Bore"], + "Spirit Breaker": ["Imbalanced", "Bulldoze"], + "Sven": ["Heavy Plate", "Warcry Aura"], + "Tidehunter": ["Blubber", "Kraken Swell"], + "Timbersaw": ["Flamethrower", "Shredder"], + "Tiny": ["Crash Landing", "Insurmountable"], + "Treant Protector": ["Sapling", "Primeval Power"], + "Tusk": ["Drinking Buddies", "Overpowering Chill"], + "Underlord": ["Abyssal Horde", "Fiend's Gate"], + "Undying": ["Rotting Mitts", "Ripped"], + "Wraith King": ["Bone Guard", "Death's Hallow"], + + # Agility Heroes + "Anti-Mage": ["Magebane", "Mana Thirst"], + "Arc Warden": ["Order", "Disorder"], + "Bloodseeker": ["Arterial Spray", "Sanguivore"], + "Bounty Hunter": ["Through and Through", "Big Game Hunter"], + "Broodmother": ["Necrotic Webs", "Feeding Frenzy"], + "Clinkz": ["Bone and Arrow", "Engulfing Step"], + "Drow Ranger": ["Sidestep", "Vantage Point"], + "Ember Spirit": ["Double Impact", "Chain Gang"], + "Faceless Void": ["Chronosphere Teamwork", "Distortion Field"], + "Gyrocopter": ["Afterburner", "Chop Shop"], + "Hoodwink": ["Treebounce Trickshot", "Go Nuts"], + "Juggernaut": ["Bladestorm", "Agigain"], + "Lone Druid": ["Bear Necessities", "Unbearable"], + "Luna": ["Lunar Orbit", "Moonshield"], + "Medusa": ["Engorged", "Monstrous"], + "Meepo": ["More Meepo", "Pack Rat"], + "Mirana": ["Moonlight", "Shooting Star"], + "Monkey King": ["Simian Stride", "Wukong's Faithful"], + "Muerta": ["Ofrenda", "Dance of the Dead"], + "Naga Siren": ["Aquatic Dominance", "Deluge"], + "Nyx Assassin": ["Scuttle", "Spelunker"], + "Pangolier": ["Double Jump", "Thunderbolt"], + "Phantom Assassin": ["Methodical", "Immaterial"], + "Phantom Lancer": ["Convergence", "Phantom Edge"], + "Razor": ["Dynamo", "Shocking"], + "Riki": ["Exterminator", "Infiltrator"], + "Shadow Fiend": ["Lasting Presence", "Shadowmire"], + "Slark": ["Leeching Leash", "Dark Reef Renegade"], + "Sniper": ["Ghillie Suit", "Scattershot"], + "Spectre": ["Forsaken", "Twist the Knife"], + "Templar Assassin": ["Refractor", "Third Strike"], + "Terrorblade": ["Soul Fragment", "Condemned"], + "Troll Warlord": ["Bad Influence", "Insensitive"], + "Ursa": ["Bear Down", "Grudge Bearer"], + "Viper": ["Predator", "Poison Burst"], + "Weaver": ["Skitterstep", "Hivemind"], + + # Intelligence Heroes + "Ancient Apparition": ["Bone Chill", "Exposure"], + "Bane": ["Dream Haunter", "Sleepwalk"], + "Batrider": ["Arsonist", "Stoked"], + "Chen": ["Centaur Convert", "Totemic"], + "Crystal Maiden": ["Cold Comfort", "Frozen Expanse"], + "Dark Seer": ["Heart of Battle", "Quick Wit"], + "Dark Willow": ["Thorny Thicket", "Wilding"], + "Dazzle": ["Nothl Boon", "Poison Bloom"], + "Death Prophet": ["Spirit Collector", "Mourning Ritual"], + "Disruptor": ["Thunderstorm", "Kinetic Fence"], + "Enchantress": ["Overprotective Wisps", "Little Friends"], + "Enigma": ["Event Horizon", "Gravity Well"], + "Grimstroke": ["Fine Art", "Inkstigate"], + "Invoker": ["Elitist", "Magus"], + "Jakiro": ["Liquid Fire", "Liquid Frost"], + "Keeper of the Light": ["Solar Bind", "Recall"], + "Leshrac": ["Diabolic Edict Charges", "Misery"], + "Lich": ["Growing Cold", "Ice Spire"], + "Lina": ["Slow Burn", "Thermal Runaway"], + "Lion": ["Fist of Death", "Gobble Up"], + "Nature's Prophet": ["Ironwood Treant", "Curse of the Forest"], + "Necrophos": ["Rapid Decay", "Profane Potency"], + "Oracle": ["Clairvoyant Curse", "Prognosticate"], + "Outworld Destroyer": ["Obsidian Decimator", "Ominous Discernment"], + "Puck": ["Jostling Rift", "Curveball"], + "Pugna": ["Siphoning Ward", "Rewards of Ruin"], + "Queen of Pain": ["Succubus", "Selfish Scream"], + "Rubick": ["Frugal Filch", "Arcane Supremacy"], + "Shadow Demon": ["Promulgate", "Menace"], + "Shadow Shaman": ["Cluster Cluck", "Massive Serpent Wards"], + "Silencer": ["Irrepressible", "Reverberating Silence"], + "Skywrath Mage": ["Staff of the Scion", "Shield of the Scion"], + "Storm Spirit": ["Shock Collar", "Static Slide"], + "Techies": ["Squee", "Spleen"], + "Tinker": ["Repair Bots", "Translocator"], + "Visage": ["Sepulchre", "Death Toll"], + "Void Spirit": ["Sanctuary", "Aether Remnant"], + "Warlock": ["Black Grimoire", "Champion of Gorroth"], + "Windranger": ["Journeyman", "Whirlwind"], + "Winter Wyvern": ["Heal Wyvern", "Essence Devourer"], + "Witch Doctor": ["Cleft Death", "Voodoo Festeration"], + "Zeus": ["Livewire", "Lightning Hands"], +} diff --git a/dota-random-builds-back/data/heroes.ts b/dota-random-builds-back/data/heroes.ts new file mode 100644 index 0000000..ff0720d --- /dev/null +++ b/dota-random-builds-back/data/heroes.ts @@ -0,0 +1,134 @@ +export interface Hero { + id: string + name: string + primary: 'Strength' | 'Agility' | 'Intelligence' +} + +export const heroes: Hero[] = [ + { id: 'abaddon', name: 'Abaddon', primary: 'Strength' }, + { id: 'alchemist', name: 'Alchemist', primary: 'Strength' }, + { id: 'axe', name: 'Axe', primary: 'Strength' }, + { id: 'beastmaster', name: 'Beastmaster', primary: 'Strength' }, + { id: 'brewmaster', name: 'Brewmaster', primary: 'Strength' }, + { id: 'bristleback', name: 'Bristleback', primary: 'Strength' }, + { id: 'centaur', name: 'Centaur Warrunner', primary: 'Strength' }, + { id: 'chaos_knight', name: 'Chaos Knight', primary: 'Strength' }, + { id: 'clockwerk', name: 'Clockwerk', primary: 'Strength' }, + { id: 'dawnbreaker', name: 'Dawnbreaker', primary: 'Strength' }, + { id: 'doom', name: 'Doom', primary: 'Strength' }, + { id: 'dragon_knight', name: 'Dragon Knight', primary: 'Strength' }, + { id: 'earth_spirit', name: 'Earth Spirit', primary: 'Strength' }, + { id: 'earthshaker', name: 'Earthshaker', primary: 'Strength' }, + { id: 'elder_titan', name: 'Elder Titan', primary: 'Strength' }, + { id: 'huskar', name: 'Huskar', primary: 'Strength' }, + { id: 'kunkka', name: 'Kunkka', primary: 'Strength' }, + { id: 'legion_commander', name: 'Legion Commander', primary: 'Strength' }, + { id: 'lifestealer', name: 'Lifestealer', primary: 'Strength' }, + { id: 'lycan', name: 'Lycan', primary: 'Strength' }, + { id: 'magnus', name: 'Magnus', primary: 'Strength' }, + { id: 'mars', name: 'Mars', primary: 'Strength' }, + { id: 'night_stalker', name: 'Night Stalker', primary: 'Strength' }, + { id: 'ogre_magi', name: 'Ogre Magi', primary: 'Strength' }, + { id: 'omniknight', name: 'Omniknight', primary: 'Strength' }, + { id: 'phoenix', name: 'Phoenix', primary: 'Strength' }, + { id: 'primal_beast', name: 'Primal Beast', primary: 'Strength' }, + { id: 'pudge', name: 'Pudge', primary: 'Strength' }, + { id: 'slardar', name: 'Slardar', primary: 'Strength' }, + { id: 'snapfire', name: 'Snapfire', primary: 'Strength' }, + { id: 'spirit_breaker', name: 'Spirit Breaker', primary: 'Strength' }, + { id: 'sven', name: 'Sven', primary: 'Strength' }, + { id: 'tidehunter', name: 'Tidehunter', primary: 'Strength' }, + { id: 'timbersaw', name: 'Timbersaw', primary: 'Strength' }, + { id: 'tiny', name: 'Tiny', primary: 'Strength' }, + { id: 'treant', name: 'Treant Protector', primary: 'Strength' }, + { id: 'tusk', name: 'Tusk', primary: 'Strength' }, + { id: 'underlord', name: 'Underlord', primary: 'Strength' }, + { id: 'undying', name: 'Undying', primary: 'Strength' }, + { id: 'wraith_king', name: 'Wraith King', primary: 'Strength' }, + + /* -------------------- Agility -------------------- */ + + { id: 'anti_mage', name: 'Anti-Mage', primary: 'Agility' }, + { id: 'arc_warden', name: 'Arc Warden', primary: 'Agility' }, + { id: 'bloodseeker', name: 'Bloodseeker', primary: 'Agility' }, + { id: 'bounty_hunter', name: 'Bounty Hunter', primary: 'Agility' }, + { id: 'broodmother', name: 'Broodmother', primary: 'Agility' }, + { id: 'clinkz', name: 'Clinkz', primary: 'Agility' }, + { id: 'drow_ranger', name: 'Drow Ranger', primary: 'Agility' }, + { id: 'ember_spirit', name: 'Ember Spirit', primary: 'Agility' }, + { id: 'faceless_void', name: 'Faceless Void', primary: 'Agility' }, + { id: 'gyrocopter', name: 'Gyrocopter', primary: 'Agility' }, + { id: 'hoodwink', name: 'Hoodwink', primary: 'Agility' }, + { id: 'juggernaut', name: 'Juggernaut', primary: 'Agility' }, + { id: 'lone_druid', name: 'Lone Druid', primary: 'Agility' }, + { id: 'luna', name: 'Luna', primary: 'Agility' }, + { id: 'medusa', name: 'Medusa', primary: 'Agility' }, + { id: 'meepo', name: 'Meepo', primary: 'Agility' }, + { id: 'mirana', name: 'Mirana', primary: 'Agility' }, + { id: 'monkey_king', name: 'Monkey King', primary: 'Agility' }, + { id: 'muerta', name: 'Muerta', primary: 'Agility' }, + { id: 'naga_siren', name: 'Naga Siren', primary: 'Agility' }, + { id: 'nyx_assassin', name: 'Nyx Assassin', primary: 'Agility' }, + { id: 'pangolier', name: 'Pangolier', primary: 'Agility' }, + { id: 'phantom_assassin', name: 'Phantom Assassin', primary: 'Agility' }, + { id: 'phantom_lancer', name: 'Phantom Lancer', primary: 'Agility' }, + { id: 'razor', name: 'Razor', primary: 'Agility' }, + { id: 'riki', name: 'Riki', primary: 'Agility' }, + { id: 'shadow_fiend', name: 'Shadow Fiend', primary: 'Agility' }, + { id: 'slark', name: 'Slark', primary: 'Agility' }, + { id: 'sniper', name: 'Sniper', primary: 'Agility' }, + { id: 'spectre', name: 'Spectre', primary: 'Agility' }, + { id: 'templar_assassin', name: 'Templar Assassin', primary: 'Agility' }, + { id: 'terrorblade', name: 'Terrorblade', primary: 'Agility' }, + { id: 'troll_warlord', name: 'Troll Warlord', primary: 'Agility' }, + { id: 'ursa', name: 'Ursa', primary: 'Agility' }, + { id: 'viper', name: 'Viper', primary: 'Agility' }, + { id: 'weaver', name: 'Weaver', primary: 'Agility' }, + + /* -------------------- Intelligence -------------------- */ + + { id: 'ancient_apparition', name: 'Ancient Apparition', primary: 'Intelligence' }, + { id: 'bane', name: 'Bane', primary: 'Intelligence' }, + { id: 'batrider', name: 'Batrider', primary: 'Intelligence' }, + { id: 'chen', name: 'Chen', primary: 'Intelligence' }, + { id: 'crystal_maiden', name: 'Crystal Maiden', primary: 'Intelligence' }, + { id: 'dark_seer', name: 'Dark Seer', primary: 'Intelligence' }, + { id: 'dark_willow', name: 'Dark Willow', primary: 'Intelligence' }, + { id: 'dazzle', name: 'Dazzle', primary: 'Intelligence' }, + { id: 'death_prophet', name: 'Death Prophet', primary: 'Intelligence' }, + { id: 'disruptor', name: 'Disruptor', primary: 'Intelligence' }, + { id: 'enchantress', name: 'Enchantress', primary: 'Intelligence' }, + { id: 'enigma', name: 'Enigma', primary: 'Intelligence' }, + { id: 'grimstroke', name: 'Grimstroke', primary: 'Intelligence' }, + { id: 'invoker', name: 'Invoker', primary: 'Intelligence' }, + { id: 'jakiro', name: 'Jakiro', primary: 'Intelligence' }, + { id: 'keeper_of_the_light', name: 'Keeper of the Light', primary: 'Intelligence' }, + { id: 'leshrac', name: 'Leshrac', primary: 'Intelligence' }, + { id: 'lich', name: 'Lich', primary: 'Intelligence' }, + { id: 'lina', name: 'Lina', primary: 'Intelligence' }, + { id: 'lion', name: 'Lion', primary: 'Intelligence' }, + { id: 'muerta', name: 'Muerta', primary: 'Intelligence' }, + { id: 'nature_prophet', name: "Nature's Prophet", primary: 'Intelligence' }, + { id: 'necrophos', name: 'Necrophos', primary: 'Intelligence' }, + { id: 'ogre_magi_int', name: 'Ogre Magi (Int)', primary: 'Intelligence' }, + { id: 'oracle', name: 'Oracle', primary: 'Intelligence' }, + { id: 'outworld_destroyer', name: 'Outworld Destroyer', primary: 'Intelligence' }, + { id: 'puck', name: 'Puck', primary: 'Intelligence' }, + { id: 'pugna', name: 'Pugna', primary: 'Intelligence' }, + { id: 'queen_of_pain', name: 'Queen of Pain', primary: 'Intelligence' }, + { id: 'rubick', name: 'Rubick', primary: 'Intelligence' }, + { id: 'shadow_demon', name: 'Shadow Demon', primary: 'Intelligence' }, + { id: 'shadow_shaman', name: 'Shadow Shaman', primary: 'Intelligence' }, + { id: 'silencer', name: 'Silencer', primary: 'Intelligence' }, + { id: 'skywrath_mage', name: 'Skywrath Mage', primary: 'Intelligence' }, + { id: 'storm_spirit', name: 'Storm Spirit', primary: 'Intelligence' }, + { id: 'techies', name: 'Techies', primary: 'Intelligence' }, + { id: 'tinker', name: 'Tinker', primary: 'Intelligence' }, + { id: 'visage', name: 'Visage', primary: 'Intelligence' }, + { id: 'void_spirit', name: 'Void Spirit', primary: 'Intelligence' }, + { id: 'warlock', name: 'Warlock', primary: 'Intelligence' }, + { id: 'windranger', name: 'Windranger', primary: 'Intelligence' }, + { id: 'winter_wyvern', name: 'Winter Wyvern', primary: 'Intelligence' }, + { id: 'witch_doctor', name: 'Witch Doctor', primary: 'Intelligence' }, + { id: 'zeus', name: 'Zeus', primary: 'Intelligence' } +]; diff --git a/dota-random-builds-back/data/items.ts b/dota-random-builds-back/data/items.ts new file mode 100644 index 0000000..a9ac623 --- /dev/null +++ b/dota-random-builds-back/data/items.ts @@ -0,0 +1,887 @@ +export interface Item { + id: string + name: string +} + + +export const items: Item[] = [ + { + "id": "item_town_portal_scroll", + "name": "Town Portal Scroll" + }, + { + "id": "item_clarity", + "name": "Clarity" + }, + { + "id": "item_faerie_fire", + "name": "Faerie Fire" + }, + { + "id": "item_smoke_of_deceit", + "name": "Smoke of Deceit" + }, + { + "id": "item_observer_ward", + "name": "Observer Ward" + }, + { + "id": "item_sentry_ward", + "name": "Sentry Ward" + }, + { + "id": "item_enchanted_mango", + "name": "Enchanted Mango" + }, + { + "id": "item_healing_salve", + "name": "Healing Salve" + }, + { + "id": "item_tango", + "name": "Tango" + }, + { + "id": "item_blood_grenade", + "name": "Blood Grenade" + }, + { + "id": "item_dust_of_appearance", + "name": "Dust of Appearance" + }, + { + "id": "item_bottle", + "name": "Bottle" + }, + { + "id": "item_aghanim_s_shard", + "name": "Aghanim's Shard" + }, + { + "id": "item_aghanim_s_blessing", + "name": "Aghanim's Blessing" + }, + { + "id": "item_observer_and_sentry_wards", + "name": "Observer and Sentry Wards" + }, + { + "id": "item_iron_branch", + "name": "Iron Branch" + }, + { + "id": "item_gauntlets_of_strength", + "name": "Gauntlets of Strength" + }, + { + "id": "item_slippers_of_agility", + "name": "Slippers of Agility" + }, + { + "id": "item_mantle_of_intelligence", + "name": "Mantle of Intelligence" + }, + { + "id": "item_circlet", + "name": "Circlet" + }, + { + "id": "item_belt_of_strength", + "name": "Belt of Strength" + }, + { + "id": "item_band_of_elvenskin", + "name": "Band of Elvenskin" + }, + { + "id": "item_robe_of_the_magi", + "name": "Robe of the Magi" + }, + { + "id": "item_crown", + "name": "Crown" + }, + { + "id": "item_ogre_axe", + "name": "Ogre Axe" + }, + { + "id": "item_blade_of_alacrity", + "name": "Blade of Alacrity" + }, + { + "id": "item_staff_of_wizardry", + "name": "Staff of Wizardry" + }, + { + "id": "item_diadem", + "name": "Diadem" + }, + { + "id": "item_quelling_blade", + "name": "Quelling Blade" + }, + { + "id": "item_ring_of_protection", + "name": "Ring of Protection" + }, + { + "id": "item_infused_raindrops", + "name": "Infused Raindrops" + }, + { + "id": "item_orb_of_venom", + "name": "Orb of Venom" + }, + { + "id": "item_orb_of_blight", + "name": "Orb of Blight" + }, + { + "id": "item_blades_of_attack", + "name": "Blades of Attack" + }, + { + "id": "item_orb_of_frost", + "name": "Orb of Frost" + }, + { + "id": "item_gloves_of_haste", + "name": "Gloves of Haste" + }, + { + "id": "item_chainmail", + "name": "Chainmail" + }, + { + "id": "item_helm_of_iron_will", + "name": "Helm of Iron Will" + }, + { + "id": "item_broadsword", + "name": "Broadsword" + }, + { + "id": "item_blitz_knuckles", + "name": "Blitz Knuckles" + }, + { + "id": "item_javelin", + "name": "Javelin" + }, + { + "id": "item_claymore", + "name": "Claymore" + }, + { + "id": "item_mithril_hammer", + "name": "Mithril Hammer" + }, + { + "id": "item_ring_of_regen", + "name": "Ring of Regen" + }, + { + "id": "item_sage_s_mask", + "name": "Sage's Mask" + }, + { + "id": "item_magic_stick", + "name": "Magic Stick" + }, + { + "id": "item_fluffy_hat", + "name": "Fluffy Hat" + }, + { + "id": "item_wind_lace", + "name": "Wind Lace" + }, + { + "id": "item_cloak", + "name": "Cloak" + }, + { + "id": "item_boots_of_speed", + "name": "Boots of Speed" + }, + { + "id": "item_gem_of_true_sight", + "name": "Gem of True Sight" + }, + { + "id": "item_morbid_mask", + "name": "Morbid Mask" + }, + { + "id": "item_voodoo_mask", + "name": "Voodoo Mask" + }, + { + "id": "item_shadow_amulet", + "name": "Shadow Amulet" + }, + { + "id": "item_ghost_scepter", + "name": "Ghost Scepter" + }, + { + "id": "item_blink_dagger", + "name": "Blink Dagger" + }, + { + "id": "item_ring_of_health", + "name": "Ring of Health" + }, + { + "id": "item_void_stone", + "name": "Void Stone" + }, + { + "id": "item_magic_wand", + "name": "Magic Wand" + }, + { + "id": "item_null_talisman", + "name": "Null Talisman" + }, + { + "id": "item_wraith_band", + "name": "Wraith Band" + }, + { + "id": "item_bracer", + "name": "Bracer" + }, + { + "id": "item_soul_ring", + "name": "Soul Ring" + }, + { + "id": "item_orb_of_corrosion", + "name": "Orb of Corrosion" + }, + { + "id": "item_falcon_blade", + "name": "Falcon Blade" + }, + { + "id": "item_power_treads", + "name": "Power Treads" + }, + { + "id": "item_phase_boots", + "name": "Phase Boots" + }, + { + "id": "item_oblivion_staff", + "name": "Oblivion Staff" + }, + { + "id": "item_perseverance", + "name": "Perseverance" + }, + { + "id": "item_mask_of_madness", + "name": "Mask of Madness" + }, + { + "id": "item_hand_of_midas", + "name": "Hand of Midas" + }, + { + "id": "item_helm_of_the_dominator", + "name": "Helm of the Dominator" + }, + { + "id": "item_boots_of_travel", + "name": "Boots of Travel" + }, + { + "id": "item_moon_shard", + "name": "Moon Shard" + }, + { + "id": "item_boots_of_travel_2", + "name": "Boots of Travel 2" + }, + { + "id": "item_buckler", + "name": "Buckler" + }, + { + "id": "item_ring_of_basilius", + "name": "Ring of Basilius" + }, + { + "id": "item_headdress", + "name": "Headdress" + }, + { + "id": "item_urn_of_shadows", + "name": "Urn of Shadows" + }, + { + "id": "item_tranquil_boots", + "name": "Tranquil Boots" + }, + { + "id": "item_pavise", + "name": "Pavise" + }, + { + "id": "item_arcane_boots", + "name": "Arcane Boots" + }, + { + "id": "item_drum_of_endurance", + "name": "Drum of Endurance" + }, + { + "id": "item_mekansm", + "name": "Mekansm" + }, + { + "id": "item_holy_locket", + "name": "Holy Locket" + }, + { + "id": "item_vladmir_s_offering", + "name": "Vladmir's Offering" + }, + { + "id": "item_spirit_vessel", + "name": "Spirit Vessel" + }, + { + "id": "item_pipe_of_insight", + "name": "Pipe of Insight" + }, + { + "id": "item_guardian_greaves", + "name": "Guardian Greaves" + }, + { + "id": "item_boots_of_bearing", + "name": "Boots of Bearing" + }, + { + "id": "item_parasma", + "name": "Parasma" + }, + { + "id": "item_veil_of_discord", + "name": "Veil of Discord" + }, + { + "id": "item_glimmer_cape", + "name": "Glimmer Cape" + }, + { + "id": "item_force_staff", + "name": "Force Staff" + }, + { + "id": "item_aether_lens", + "name": "Aether Lens" + }, + { + "id": "item_witch_blade", + "name": "Witch Blade" + }, + { + "id": "item_eul_s_scepter_of_divinity", + "name": "Eul's Scepter of Divinity" + }, + { + "id": "item_rod_of_atos", + "name": "Rod of Atos" + }, + { + "id": "item_dagon", + "name": "Dagon" + }, + { + "id": "item_orchid_malevolence", + "name": "Orchid Malevolence" + }, + { + "id": "item_solar_crest", + "name": "Solar Crest" + }, + { + "id": "item_aghanim_s_scepter", + "name": "Aghanim's Scepter" + }, + { + "id": "item_refresher_orb", + "name": "Refresher Orb" + }, + { + "id": "item_octarine_core", + "name": "Octarine Core" + }, + { + "id": "item_scythe_of_vyse", + "name": "Scythe of Vyse" + }, + { + "id": "item_gleipnir", + "name": "Gleipnir" + }, + { + "id": "item_wind_waker", + "name": "Wind Waker" + }, + { + "id": "item_crystalys", + "name": "Crystalys" + }, + { + "id": "item_meteor_hammer", + "name": "Meteor Hammer" + }, + { + "id": "item_armlet_of_mordiggian", + "name": "Armlet of Mordiggian" + }, + { + "id": "item_skull_basher", + "name": "Skull Basher" + }, + { + "id": "item_shadow_blade", + "name": "Shadow Blade" + }, + { + "id": "item_desolator", + "name": "Desolator" + }, + { + "id": "item_battle_fury", + "name": "Battle Fury" + }, + { + "id": "item_ethereal_blade", + "name": "Ethereal Blade" + }, + { + "id": "item_nullifier", + "name": "Nullifier" + }, + { + "id": "item_monkey_king_bar", + "name": "Monkey King Bar" + }, + { + "id": "item_butterfly", + "name": "Butterfly" + }, + { + "id": "item_radiance", + "name": "Radiance" + }, + { + "id": "item_daedalus", + "name": "Daedalus" + }, + { + "id": "item_silver_edge", + "name": "Silver Edge" + }, + { + "id": "item_divine_rapier", + "name": "Divine Rapier" + }, + { + "id": "item_bloodthorn", + "name": "Bloodthorn" + }, + { + "id": "item_abyssal_blade", + "name": "Abyssal Blade" + }, + { + "id": "item_revenant_s_brooch", + "name": "Revenant's Brooch" + }, + { + "id": "item_disperser", + "name": "Disperser" + }, + { + "id": "item_khanda", + "name": "Khanda" + }, + { + "id": "item_vanguard", + "name": "Vanguard" + }, + { + "id": "item_blade_mail", + "name": "Blade Mail" + }, + { + "id": "item_aeon_disk", + "name": "Aeon Disk" + }, + { + "id": "item_soul_booster", + "name": "Soul Booster" + }, + { + "id": "item_crimson_guard", + "name": "Crimson Guard" + }, + { + "id": "item_lotus_orb", + "name": "Lotus Orb" + }, + { + "id": "item_black_king_bar", + "name": "Black King Bar" + }, + { + "id": "item_hurricane_pike", + "name": "Hurricane Pike" + }, + { + "id": "item_manta_style", + "name": "Manta Style" + }, + { + "id": "item_linken_s_sphere", + "name": "Linken's Sphere" + }, + { + "id": "item_shiva_s_guard", + "name": "Shiva's Guard" + }, + { + "id": "item_heart_of_tarrasque", + "name": "Heart of Tarrasque" + }, + { + "id": "item_assault_cuirass", + "name": "Assault Cuirass" + }, + { + "id": "item_bloodstone", + "name": "Bloodstone" + }, + { + "id": "item_helm_of_the_overlord", + "name": "Helm of the Overlord" + }, + { + "id": "item_eternal_shroud", + "name": "Eternal Shroud" + }, + { + "id": "item_dragon_lance", + "name": "Dragon Lance" + }, + { + "id": "item_sange", + "name": "Sange" + }, + { + "id": "item_yasha", + "name": "Yasha" + }, + { + "id": "item_kaya", + "name": "Kaya" + }, + { + "id": "item_echo_sabre", + "name": "Echo Sabre" + }, + { + "id": "item_maelstrom", + "name": "Maelstrom" + }, + { + "id": "item_diffusal_blade", + "name": "Diffusal Blade" + }, + { + "id": "item_mage_slayer", + "name": "Mage Slayer" + }, + { + "id": "item_phylactery", + "name": "Phylactery" + }, + { + "id": "item_heaven_s_halberd", + "name": "Heaven's Halberd" + }, + { + "id": "item_kaya_and_sange", + "name": "Kaya and Sange" + }, + { + "id": "item_sange_and_yasha", + "name": "Sange and Yasha" + }, + { + "id": "item_yasha_and_kaya", + "name": "Yasha and Kaya" + }, + { + "id": "item_satanic", + "name": "Satanic" + }, + { + "id": "item_eye_of_skadi", + "name": "Eye of Skadi" + }, + { + "id": "item_mjollnir", + "name": "Mjollnir" + }, + { + "id": "item_overwhelming_blink", + "name": "Overwhelming Blink" + }, + { + "id": "item_swift_blink", + "name": "Swift Blink" + }, + { + "id": "item_arcane_blink", + "name": "Arcane Blink" + }, + { + "id": "item_harpoon", + "name": "Harpoon" + }, + { + "id": "item_ring_of_tarrasque", + "name": "Ring of Tarrasque" + }, + { + "id": "item_tiara_of_selemene", + "name": "Tiara of Selemene" + }, + { + "id": "item_cornucopia", + "name": "Cornucopia" + }, + { + "id": "item_energy_booster", + "name": "Energy Booster" + }, + { + "id": "item_vitality_booster", + "name": "Vitality Booster" + }, + { + "id": "item_point_booster", + "name": "Point Booster" + }, + { + "id": "item_talisman_of_evasion", + "name": "Talisman of Evasion" + }, + { + "id": "item_platemail", + "name": "Platemail" + }, + { + "id": "item_hyperstone", + "name": "Hyperstone" + }, + { + "id": "item_ultimate_orb", + "name": "Ultimate Orb" + }, + { + "id": "item_demon_edge", + "name": "Demon Edge" + }, + { + "id": "item_mystic_staff", + "name": "Mystic Staff" + }, + { + "id": "item_reaver", + "name": "Reaver" + }, + { + "id": "item_eaglesong", + "name": "Eaglesong" + }, + { + "id": "item_sacred_relic", + "name": "Sacred Relic" + } +] + +// export const items: Item[] = [ +// { id: 'item_magic_wand', name: 'Magic Wand' }, +// { id: 'item_bracer', name: 'Bracer' }, +// { id: 'item_wraith_band', name: 'Wraith Band' }, +// { id: 'item_null_talisman', name: 'Null Talisman' }, +// { id: 'item_soul_ring', name: 'Soul Ring' }, +// { id: 'item_falcon_blade', name: 'Falcon Blade' }, + +// // Boots +// { id: 'item_phase_boots', name: 'Phase Boots' }, +// { id: 'item_power_treads', name: 'Power Treads' }, +// { id: 'item_arcane_boots', name: 'Arcane Boots' }, +// { id: 'item_tranquil_boots', name: 'Tranquil Boots' }, +// { id: 'item_boots_of_bearing', name: 'Boots of Bearing' }, +// { id: 'item_guardian_greaves', name: 'Guardian Greaves' }, + +// // Mobility +// { id: 'item_blink', name: 'Blink Dagger' }, +// { id: 'item_force_staff', name: 'Force Staff' }, +// { id: 'item_hurricane_pike', name: 'Hurricane Pike' }, +// { id: 'item_overwhelming_blink', name: 'Overwhelming Blink' }, +// { id: 'item_swift_blink', name: 'Swift Blink' }, +// { id: 'item_arcane_blink', name: 'Arcane Blink' }, + +// // Armor / regen aura +// { id: 'item_mekansm', name: 'Mekansm' }, +// { id: 'item_pipe', name: 'Pipe of Insight' }, +// { id: 'item_vladmir', name: 'Vladmir’s Offering' }, +// { id: 'item_solar_crest', name: 'Solar Crest' }, +// { id: 'item_spirit_vessel', name: 'Spirit Vessel' }, + +// // Simple DPS items +// { id: 'item_mask_of_madness', name: 'Mask of Madness' }, +// { id: 'item_armlet', name: 'Armlet of Mordiggian' }, +// { id: 'item_echo_sabre', name: 'Echo Sabre' }, +// { id: 'item_skull_basher', name: 'Skull Basher' }, +// { id: 'item_desolator', name: 'Desolator' }, +// { id: 'item_silver_edge', name: 'Silver Edge' }, + +// // Magic items +// { id: 'item_veil_of_discord', name: 'Veil of Discord' }, +// { id: 'item_aether_lens', name: 'Aether Lens' }, +// { id: 'item_kaya', name: 'Kaya' }, +// { id: 'item_rod_of_atos', name: 'Rod of Atos' }, +// { id: 'item_dagon', name: 'Dagon' }, + +// // HP items +// { id: 'item_hood_of_defiance', name: 'Hood of Defiance' }, +// { id: 'item_heart', name: 'Heart of Tarrasque' }, +// // Strong DPS items +// { id: 'item_monkey_king_bar', name: 'Monkey King Bar' }, +// { id: 'item_butterfly', name: 'Butterfly' }, +// { id: 'item_daedalus', name: 'Daedalus' }, +// { id: 'item_greater_crit', name: 'Greater Crit' }, +// { id: 'item_bfury', name: 'Battle Fury' }, +// { id: 'item_satanic', name: 'Satanic' }, +// { id: 'item_mjollnir', name: 'Mjollnir' }, +// { id: 'item_radiance', name: 'Radiance' }, +// { id: 'item_diffusal_blade', name: 'Diffusal Blade' }, +// { id: 'item_abissal_blade', name: 'Abyssal Blade' }, +// { id: 'item_maelstrom', name: 'Maelstrom' }, +// { id: 'item_mage_slayer', name: 'Mage Slayer' }, + +// // Tank / defensive items +// { id: 'item_black_king_bar', name: 'Black King Bar' }, +// { id: 'item_shivas_guard', name: 'Shiva’s Guard' }, +// { id: 'item_blade_mail', name: 'Blade Mail' }, +// { id: 'item_lotus_orb', name: 'Lotus Orb' }, +// { id: 'item_vanguard', name: 'Vanguard' }, +// { id: 'item_pipe', name: 'Pipe of Insight' }, +// { id: 'item_eternal_shroud', name: 'Eternal Shroud' }, +// { id: 'item_crimson_guard', name: 'Crimson Guard' }, +// { id: 'item_heavens_halberd', name: 'Heaven’s Halberd' }, + +// // Mobility / utility +// { id: 'item_cyclone', name: 'Eul’s Scepter of Divinity' }, +// { id: 'item_glimmer_cape', name: 'Glimmer Cape' }, +// { id: 'item_ghost', name: 'Ghost Scepter' }, +// { id: 'item_ethereal_blade', name: 'Ethereal Blade' }, +// { id: 'item_aeon_disk', name: 'Aeon Disk' }, +// { id: 'item_boots_of_bearing', name: 'Boots of Bearing' }, +// { id: 'item_rod_of_atos', name: 'Rod of Atos' }, +// { id: 'item_solar_crest', name: 'Solar Crest' }, + +// // Healing / support items +// { id: 'item_guardian_greaves', name: 'Guardian Greaves' }, +// { id: 'item_holy_locket', name: 'Holy Locket' }, +// { id: 'item_arcane_boots', name: 'Arcane Boots' }, +// { id: 'item_spirit_vessel', name: 'Spirit Vessel' }, +// { id: 'item_medallion_of_courage', name: 'Medallion of Courage' }, + +// // Magical burst / caster items +// { id: 'item_kaya', name: 'Kaya' }, +// { id: 'item_kaya_and_sange', name: 'Kaya and Sange' }, +// { id: 'item_yasha_and_kaya', name: 'Yasha and Kaya' }, +// { id: 'item_sange_and_yasha', name: 'Sange and Yasha' }, +// { id: 'item_octarine_core', name: 'Octarine Core' }, +// { id: 'item_scythe_of_vyse', name: 'Scythe of Vyse' }, +// { id: 'item_dagon', name: 'Dagon' }, + +// { id: 'item_rapier', name: 'Divine Rapier' }, +// { id: 'item_moon_shard', name: 'Moon Shard' }, +// { id: 'item_silver_edge', name: 'Silver Edge' }, +// { id: 'item_bloodthorn', name: 'Bloodthorn' }, +// { id: 'item_nullifier', name: 'Nullifier' }, + +// // Intelligence & spellpower +// { id: 'item_refresher', name: 'Refresher Orb' }, +// { id: 'item_aghanims_scepter', name: 'Aghanim’s Scepter' }, +// { id: 'item_aghanims_shard', name: 'Aghanim’s Shard' }, +// { id: 'item_witch_blade', name: 'Witch Blade' }, +// { id: 'item_gungir', name: 'Gleipnir' }, + +// // Tank / sustain late game +// { id: 'item_heart', name: 'Heart of Tarrasque' }, +// { id: 'item_assault', name: 'Assault Cuirass' }, +// { id: 'item_satanic', name: 'Satanic' }, +// { id: 'item_harpoon', name: 'Harpoon' }, + +// // Universal top-tier +// { id: 'item_sphere', name: 'Linken’s Sphere' }, +// { id: 'item_skadi', name: 'Eye of Skadi' }, +// { id: 'item_manta', name: 'Manta Style' }, +// { id: 'item_overwhelming_blink', name: 'Overwhelming Blink' }, +// { id: 'item_swift_blink', name: 'Swift Blink' }, + +// // Summon & utility +// { id: 'item_necronomicon', name: 'Necronomicon' }, +// { id: 'item_drum', name: 'Drum of Endurance' }, +// { id: 'item_helm_of_the_overlord', name: 'Helm of the Overlord' }, + +// // Roshan rewards +// { id: 'item_aegis', name: 'Aegis of the Immortal' }, +// { id: 'item_cheese', name: 'Cheese' }, +// { id: 'item_refresher_shard', name: 'Refresher Shard' }, +// { id: 'item_aghanims_blessing', name: 'Aghanim’s Blessing' }, + +// // Neutral items — Tier 1 +// { id: 'item_arcane_ring', name: 'Arcane Ring' }, +// { id: 'item_faded_broach', name: 'Faded Broach' }, +// { id: 'item_keen_optic', name: 'Keen Optic' }, + +// // Tier 2 +// { id: 'item_grove_bow', name: 'Grove Bow' }, +// { id: 'item_pupils_gift', name: 'Pupil’s Gift' }, +// { id: 'item_philosophers_stone', name: 'Philosopher’s Stone' }, + +// // Tier 3 +// { id: 'item_paladin_sword', name: 'Paladin Sword' }, +// { id: 'item_quickening_charm', name: 'Quickening Charm' }, +// { id: 'item_spider_legs', name: 'Spider Legs' }, + +// // Tier 4 +// { id: 'item_spell_prism', name: 'Spell Prism' }, +// { id: 'item_timeless_relic', name: 'Timeless Relic' }, +// { id: 'item_mind_breaker', name: 'Mind Breaker' }, + +// // Tier 5 +// { id: 'item_apex', name: 'Apex' }, +// { id: 'item_ex_machina', name: 'Ex Machina' }, +// { id: 'item_mirror_shield', name: 'Mirror Shield' }, +// { id: 'item_book_of_shadows', name: 'Book of Shadows' }, +// { id: 'item_seer_stone', name: 'Seer Stone' } +// ]; + + diff --git a/dota-random-builds-back/data/skills.ts b/dota-random-builds-back/data/skills.ts new file mode 100644 index 0000000..b3dcfc8 --- /dev/null +++ b/dota-random-builds-back/data/skills.ts @@ -0,0 +1,12 @@ +export interface SkillBuildOption { + id: string + name: string + description?: string +} + +export const skillBuilds: SkillBuildOption[] = [ + { id: 'q_first', name: 'Max Q first', description: 'Prioritize first ability' }, + { id: 'w_first', name: 'Max W first', description: 'Prioritize second ability' }, + { id: 'e_first', name: 'Max E first', description: 'Prioritize third ability' }, + { id: 'balanced', name: 'Balanced', description: 'Evenly distribute points' } +] diff --git a/dota-random-builds-back/manage.py b/dota-random-builds-back/manage.py new file mode 100755 index 0000000..8e7ac79 --- /dev/null +++ b/dota-random-builds-back/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/dota-random-builds-back/prompt.txt b/dota-random-builds-back/prompt.txt new file mode 100644 index 0000000..a57dd62 --- /dev/null +++ b/dota-random-builds-back/prompt.txt @@ -0,0 +1 @@ +создай django + drf приложение щас в среде используется python 3.12.3 \ No newline at end of file diff --git a/dota-random-builds-back/requirements.txt b/dota-random-builds-back/requirements.txt new file mode 100644 index 0000000..b9d02c2 --- /dev/null +++ b/dota-random-builds-back/requirements.txt @@ -0,0 +1,3 @@ +django>=6.0,<7.0 +djangorestframework>=3.16,<4.0 +django-cors-headers>=4.9,<5.0 diff --git a/dota-random-builds-front/.dockerignore b/dota-random-builds-front/.dockerignore new file mode 100644 index 0000000..e7b910d --- /dev/null +++ b/dota-random-builds-front/.dockerignore @@ -0,0 +1,6 @@ +node_modules +dist +.git +.gitignore +*.log +.env* diff --git a/dota-random-builds-front/.gitignore b/dota-random-builds-front/.gitignore new file mode 100644 index 0000000..a3f7a51 --- /dev/null +++ b/dota-random-builds-front/.gitignore @@ -0,0 +1,36 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.DS_Store +dist +dist-ssr +coverage +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*.tsbuildinfo + +.eslintcache + +# Cypress +/cypress/videos/ +/cypress/screenshots/ + +# Vitest +__screenshots__/ diff --git a/dota-random-builds-front/AGENTS.md b/dota-random-builds-front/AGENTS.md new file mode 100644 index 0000000..7b467cd --- /dev/null +++ b/dota-random-builds-front/AGENTS.md @@ -0,0 +1,37 @@ +# Repository Guidelines + +## Project Structure & Module Organization +- `src/main.ts` bootstraps the Vue app, mounts `App.vue`, and registers Pinia + router. +- `src/App.vue` hosts the layout shell; global theme styles live in `src/assets/main.css`. +- `src/pages` holds route-level views (e.g., `HomePage.vue`), wired in `src/router/index.ts`. +- `src/components` contains shareable UI (`Randomizer.vue`); aim to keep these presentational. +- `src/stores` stores state and side effects (e.g., `randomData.ts` for `/api/randomize` and localStorage prefs); static datasets sit in `src/data`. +- `public` serves static assets as-is; `vite.config.ts` configures aliases (`@` → `src`) and plugins. + +## Build, Test, and Development Commands +- `npm run dev` — start Vite dev server with hot reload. +- `npm run build` — type-check via `vue-tsc`, then produce the production bundle. +- `npm run build-only` — production bundle without type-check (useful for quick iteration). +- `npm run preview` — serve the built assets locally to verify production output. +- `npm run type-check` — standalone TS/SFC type validation. + +## Coding Style & Naming Conventions +- Vue 3 + TypeScript with ` + + diff --git a/dota-random-builds-front/nginx.conf b/dota-random-builds-front/nginx.conf new file mode 100644 index 0000000..63656bf --- /dev/null +++ b/dota-random-builds-front/nginx.conf @@ -0,0 +1,23 @@ +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } + + location /api/ { + proxy_pass http://backend:8000/api/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } +} diff --git a/dota-random-builds-front/package-lock.json b/dota-random-builds-front/package-lock.json new file mode 100644 index 0000000..8474b50 --- /dev/null +++ b/dota-random-builds-front/package-lock.json @@ -0,0 +1,3088 @@ +{ + "name": "dota-random-builds-front", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "dota-random-builds-front", + "version": "0.0.0", + "dependencies": { + "axios": "^1.13.2", + "pinia": "^3.0.4", + "vue": "^3.5.25", + "vue-router": "^4.6.3" + }, + "devDependencies": { + "@tsconfig/node24": "^24.0.3", + "@types/node": "^24.10.1", + "@vitejs/plugin-vue": "^6.0.2", + "@vue/tsconfig": "^0.8.1", + "npm-run-all2": "^8.0.4", + "typescript": "~5.9.0", + "vite": "^7.2.4", + "vite-plugin-vue-devtools": "^8.0.5", + "vue-tsc": "^3.1.5" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz", + "integrity": "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.5", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.28.0.tgz", + "integrity": "sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-decorators": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz", + "integrity": "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.5.tgz", + "integrity": "sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "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, + "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, + "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, + "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==" + }, + "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, + "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 + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.50", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.50.tgz", + "integrity": "sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==", + "dev": true + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tsconfig/node24": { + "version": "24.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node24/-/node24-24.0.3.tgz", + "integrity": "sha512-vcERKtKQKHgzt/vfS3Gjasd8SUI2a0WZXpgJURdJsMySpS5+ctgbPfuLj2z/W+w4lAfTWxoN4upKfu2WzIRYnw==", + "dev": true + }, + "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 + }, + "node_modules/@types/node": { + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "dev": true, + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.2.tgz", + "integrity": "sha512-iHmwV3QcVGGvSC1BG5bZ4z6iwa1SOpAPWmnjOErd4Ske+lZua5K9TtAVdx0gMBClJ28DViCbSmZitjWZsWO3LA==", + "dev": true, + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.50" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.23.tgz", + "integrity": "sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==", + "dev": true, + "dependencies": { + "@volar/source-map": "2.4.23" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.23.tgz", + "integrity": "sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==", + "dev": true + }, + "node_modules/@volar/typescript": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.23.tgz", + "integrity": "sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==", + "dev": true, + "dependencies": { + "@volar/language-core": "2.4.23", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/babel-helper-vue-transform-on": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.5.0.tgz", + "integrity": "sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==", + "dev": true + }, + "node_modules/@vue/babel-plugin-jsx": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.5.0.tgz", + "integrity": "sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.2", + "@vue/babel-helper-vue-transform-on": "1.5.0", + "@vue/babel-plugin-resolve-type": "1.5.0", + "@vue/shared": "^3.5.18" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + } + } + }, + "node_modules/@vue/babel-plugin-resolve-type": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.5.0.tgz", + "integrity": "sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/parser": "^7.28.0", + "@vue/compiler-sfc": "^3.5.18" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.25.tgz", + "integrity": "sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/shared": "3.5.25", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.25.tgz", + "integrity": "sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==", + "dependencies": { + "@vue/compiler-core": "3.5.25", + "@vue/shared": "3.5.25" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.25.tgz", + "integrity": "sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/compiler-core": "3.5.25", + "@vue/compiler-dom": "3.5.25", + "@vue/compiler-ssr": "3.5.25", + "@vue/shared": "3.5.25", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.25.tgz", + "integrity": "sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==", + "dependencies": { + "@vue/compiler-dom": "3.5.25", + "@vue/shared": "3.5.25" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz", + "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==", + "dependencies": { + "@vue/devtools-kit": "^7.7.9" + } + }, + "node_modules/@vue/devtools-core": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-8.0.5.tgz", + "integrity": "sha512-dpCw8nl0GDBuiL9SaY0mtDxoGIEmU38w+TQiYEPOLhW03VDC0lfNMYXS/qhl4I0YlysGp04NLY4UNn6xgD0VIQ==", + "dev": true, + "dependencies": { + "@vue/devtools-kit": "^8.0.5", + "@vue/devtools-shared": "^8.0.5", + "mitt": "^3.0.1", + "nanoid": "^5.1.5", + "pathe": "^2.0.3", + "vite-hot-client": "^2.1.0" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/@vue/devtools-core/node_modules/@vue/devtools-kit": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.0.5.tgz", + "integrity": "sha512-q2VV6x1U3KJMTQPUlRMyWEKVbcHuxhqJdSr6Jtjz5uAThAIrfJ6WVZdGZm5cuO63ZnSUz0RCsVwiUUb0mDV0Yg==", + "dev": true, + "dependencies": { + "@vue/devtools-shared": "^8.0.5", + "birpc": "^2.6.1", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^2.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-core/node_modules/@vue/devtools-shared": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.0.5.tgz", + "integrity": "sha512-bRLn6/spxpmgLk+iwOrR29KrYnJjG9DGpHGkDFG82UM21ZpJ39ztUT9OXX3g+usW7/b2z+h46I9ZiYyB07XMXg==", + "dev": true, + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/devtools-core/node_modules/nanoid": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz", + "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/@vue/devtools-core/node_modules/perfect-debounce": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.0.0.tgz", + "integrity": "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==", + "dev": true + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz", + "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==", + "dependencies": { + "@vue/devtools-shared": "^7.7.9", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz", + "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/language-core": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.1.5.tgz", + "integrity": "sha512-FMcqyzWN+sYBeqRMWPGT2QY0mUasZMVIuHvmb5NT3eeqPrbHBYtCP8JWEUCDCgM+Zr62uuWY/qoeBrPrzfa78w==", + "dev": true, + "dependencies": { + "@volar/language-core": "2.4.23", + "@vue/compiler-dom": "^3.5.0", + "@vue/shared": "^3.5.0", + "alien-signals": "^3.0.0", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.2" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.25.tgz", + "integrity": "sha512-5xfAypCQepv4Jog1U4zn8cZIcbKKFka3AgWHEFQeK65OW+Ys4XybP6z2kKgws4YB43KGpqp5D/K3go2UPPunLA==", + "dependencies": { + "@vue/shared": "3.5.25" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.25.tgz", + "integrity": "sha512-Z751v203YWwYzy460bzsYQISDfPjHTl+6Zzwo/a3CsAf+0ccEjQ8c+0CdX1WsumRTHeywvyUFtW6KvNukT/smA==", + "dependencies": { + "@vue/reactivity": "3.5.25", + "@vue/shared": "3.5.25" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.25.tgz", + "integrity": "sha512-a4WrkYFbb19i9pjkz38zJBg8wa/rboNERq3+hRRb0dHiJh13c+6kAbgqCPfMaJ2gg4weWD3APZswASOfmKwamA==", + "dependencies": { + "@vue/reactivity": "3.5.25", + "@vue/runtime-core": "3.5.25", + "@vue/shared": "3.5.25", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.25.tgz", + "integrity": "sha512-UJaXR54vMG61i8XNIzTSf2Q7MOqZHpp8+x3XLGtE3+fL+nQd+k7O5+X3D/uWrnQXOdMw5VPih+Uremcw+u1woQ==", + "dependencies": { + "@vue/compiler-ssr": "3.5.25", + "@vue/shared": "3.5.25" + }, + "peerDependencies": { + "vue": "3.5.25" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.25.tgz", + "integrity": "sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==" + }, + "node_modules/@vue/tsconfig": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.8.1.tgz", + "integrity": "sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g==", + "dev": true, + "peerDependencies": { + "typescript": "5.x", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/alien-signals": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.1.1.tgz", + "integrity": "sha512-ogkIWbVrLwKtHY6oOAXaYkAxP+cTH7V5FZ5+Tm4NZFd8VDZ6uNMDrfzqctTZ42eTMCSR3ne3otpcxmqSnFfPYA==", + "dev": true + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz", + "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.32.tgz", + "integrity": "sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==", + "dev": true, + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/birpc": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.8.0.tgz", + "integrity": "sha512-Bz2a4qD/5GRhiHSwj30c/8kC8QGj12nNDwz3D4ErQ4Xhy35dsSDvF+RA/tWpjyU0pdGtSDiEk6B5fBGE1qNVhw==", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/browserslist": { + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz", + "integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "baseline-browser-mapping": "^2.8.25", + "caniuse-lite": "^1.0.30001754", + "electron-to-chromium": "^1.5.249", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.1.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001757", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz", + "integrity": "sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/copy-anything": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz", + "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", + "dependencies": { + "is-what": "^5.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/default-browser": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.4.0.tgz", + "integrity": "sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==", + "dev": true, + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.262", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.262.tgz", + "integrity": "sha512-NlAsMteRHek05jRUxUR0a5jpjYq9ykk6+kO0yRaMi5moe7u0fVIOeQ3Y30A8dIiWFBNUoQGi1ljb1i5VtS9WQQ==", + "dev": true + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-stack-parser-es": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", + "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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==" + }, + "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, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "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, + "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==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==" + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-what": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", + "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", + "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", + "dev": true, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "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==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "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 + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true + }, + "node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", + "dev": true, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-all2": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-8.0.4.tgz", + "integrity": "sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "cross-spawn": "^7.0.6", + "memorystream": "^0.3.1", + "picomatch": "^4.0.2", + "pidtree": "^0.6.0", + "read-package-json-fast": "^4.0.0", + "shell-quote": "^1.7.3", + "which": "^5.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "npm-run-all2": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": "^20.5.0 || >=22.0.0", + "npm": ">= 10" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "dev": true + }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pinia": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz", + "integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==", + "dependencies": { + "@vue/devtools-api": "^7.7.7" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.5.0", + "vue": "^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "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" + } + ], + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/read-package-json-fast": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz", + "integrity": "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==", + "dev": true, + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" + }, + "node_modules/rollup": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/superjson": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", + "dependencies": { + "copy-anything": "^4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "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, + "engines": { + "node": ">=6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true + }, + "node_modules/unplugin-utils": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.1.tgz", + "integrity": "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==", + "dev": true, + "dependencies": { + "pathe": "^2.0.3", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.4.tgz", + "integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==", + "dev": true, + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "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", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-dev-rpc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vite-dev-rpc/-/vite-dev-rpc-1.1.0.tgz", + "integrity": "sha512-pKXZlgoXGoE8sEKiKJSng4hI1sQ4wi5YT24FCrwrLt6opmkjlqPPVmiPWWJn8M8byMxRGzp1CrFuqQs4M/Z39A==", + "dev": true, + "dependencies": { + "birpc": "^2.4.0", + "vite-hot-client": "^2.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0" + } + }, + "node_modules/vite-hot-client": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vite-hot-client/-/vite-hot-client-2.1.0.tgz", + "integrity": "sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0" + } + }, + "node_modules/vite-plugin-inspect": { + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/vite-plugin-inspect/-/vite-plugin-inspect-11.3.3.tgz", + "integrity": "sha512-u2eV5La99oHoYPHE6UvbwgEqKKOQGz86wMg40CCosP6q8BkB6e5xPneZfYagK4ojPJSj5anHCrnvC20DpwVdRA==", + "dev": true, + "dependencies": { + "ansis": "^4.1.0", + "debug": "^4.4.1", + "error-stack-parser-es": "^1.0.5", + "ohash": "^2.0.11", + "open": "^10.2.0", + "perfect-debounce": "^2.0.0", + "sirv": "^3.0.1", + "unplugin-utils": "^0.3.0", + "vite-dev-rpc": "^1.1.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + } + } + }, + "node_modules/vite-plugin-inspect/node_modules/perfect-debounce": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.0.0.tgz", + "integrity": "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==", + "dev": true + }, + "node_modules/vite-plugin-vue-devtools": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-8.0.5.tgz", + "integrity": "sha512-p619BlKFOqQXJ6uDWS1vUPQzuJOD6xJTfftj57JXBGoBD/yeQCowR7pnWcr/FEX4/HVkFbreI6w2uuGBmQOh6A==", + "dev": true, + "dependencies": { + "@vue/devtools-core": "^8.0.5", + "@vue/devtools-kit": "^8.0.5", + "@vue/devtools-shared": "^8.0.5", + "sirv": "^3.0.2", + "vite-plugin-inspect": "^11.3.3", + "vite-plugin-vue-inspector": "^5.3.2" + }, + "engines": { + "node": ">=v14.21.3" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0-0" + } + }, + "node_modules/vite-plugin-vue-devtools/node_modules/@vue/devtools-kit": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.0.5.tgz", + "integrity": "sha512-q2VV6x1U3KJMTQPUlRMyWEKVbcHuxhqJdSr6Jtjz5uAThAIrfJ6WVZdGZm5cuO63ZnSUz0RCsVwiUUb0mDV0Yg==", + "dev": true, + "dependencies": { + "@vue/devtools-shared": "^8.0.5", + "birpc": "^2.6.1", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^2.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/vite-plugin-vue-devtools/node_modules/@vue/devtools-shared": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.0.5.tgz", + "integrity": "sha512-bRLn6/spxpmgLk+iwOrR29KrYnJjG9DGpHGkDFG82UM21ZpJ39ztUT9OXX3g+usW7/b2z+h46I9ZiYyB07XMXg==", + "dev": true, + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/vite-plugin-vue-devtools/node_modules/perfect-debounce": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.0.0.tgz", + "integrity": "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==", + "dev": true + }, + "node_modules/vite-plugin-vue-inspector": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/vite-plugin-vue-inspector/-/vite-plugin-vue-inspector-5.3.2.tgz", + "integrity": "sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.0", + "@babel/plugin-proposal-decorators": "^7.23.0", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-transform-typescript": "^7.22.15", + "@vue/babel-plugin-jsx": "^1.1.5", + "@vue/compiler-dom": "^3.3.4", + "kolorist": "^1.8.0", + "magic-string": "^0.30.4" + }, + "peerDependencies": { + "vite": "^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0" + } + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true + }, + "node_modules/vue": { + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.25.tgz", + "integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==", + "dependencies": { + "@vue/compiler-dom": "3.5.25", + "@vue/compiler-sfc": "3.5.25", + "@vue/runtime-dom": "3.5.25", + "@vue/server-renderer": "3.5.25", + "@vue/shared": "3.5.25" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.3.tgz", + "integrity": "sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/vue-router/node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==" + }, + "node_modules/vue-tsc": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.1.5.tgz", + "integrity": "sha512-L/G9IUjOWhBU0yun89rv8fKqmKC+T0HfhrFjlIml71WpfBv9eb4E9Bev8FMbyueBIU9vxQqbd+oOsVcDa5amGw==", + "dev": true, + "dependencies": { + "@volar/typescript": "2.4.23", + "@vue/language-core": "3.1.5" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } +} diff --git a/dota-random-builds-front/package.json b/dota-random-builds-front/package.json new file mode 100644 index 0000000..a1575c5 --- /dev/null +++ b/dota-random-builds-front/package.json @@ -0,0 +1,33 @@ +{ + "name": "dota-random-builds-front", + "version": "0.0.0", + "private": true, + "type": "module", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "scripts": { + "dev": "vite", + "build": "run-p type-check \"build-only {@}\" --", + "preview": "vite preview", + "build-only": "vite build", + "type-check": "vue-tsc --build" + }, + "dependencies": { + "axios": "^1.13.2", + "pinia": "^3.0.4", + "vue": "^3.5.25", + "vue-router": "^4.6.3" + }, + "devDependencies": { + "@tsconfig/node24": "^24.0.3", + "@types/node": "^24.10.1", + "@vitejs/plugin-vue": "^6.0.2", + "@vue/tsconfig": "^0.8.1", + "npm-run-all2": "^8.0.4", + "typescript": "~5.9.0", + "vite": "^7.2.4", + "vite-plugin-vue-devtools": "^8.0.5", + "vue-tsc": "^3.1.5" + } +} diff --git a/dota-random-builds-front/public/favicon.ico b/dota-random-builds-front/public/favicon.ico new file mode 100644 index 0000000..df36fcf Binary files /dev/null and b/dota-random-builds-front/public/favicon.ico differ diff --git a/dota-random-builds-front/src/App.vue b/dota-random-builds-front/src/App.vue new file mode 100644 index 0000000..837b0f8 --- /dev/null +++ b/dota-random-builds-front/src/App.vue @@ -0,0 +1,127 @@ + + + + + diff --git a/dota-random-builds-front/src/assets/main.css b/dota-random-builds-front/src/assets/main.css new file mode 100644 index 0000000..06b19ac --- /dev/null +++ b/dota-random-builds-front/src/assets/main.css @@ -0,0 +1,31 @@ +:root{ + --bg-1: #050a19; + --bg-2: #0b1f3a; + + /* Бирюзово-синие glows */ + --glow-1: rgba(79, 209, 197, 0.16); + --glow-2: rgba(56, 189, 248, 0.12); + --glow-3: rgba(96, 165, 250, 0.10); + + /* Акценты и текст */ + --accent: #4fd1c5; + --muted: #9fb3cc; + + --noise-opacity: 0.04; + + /* Dota-стильные подсветки */ + --gold-glow: rgba(200, 168, 106, 0.12); + --blue-glow: rgba(79,195,247,0.12); +} + +*{ + box-sizing: border-box; +} + +html, body { + /* width: 100%; + height: 100%; */ + margin: 0; + padding: 0; +} + diff --git a/dota-random-builds-front/src/components/Randomizer.vue b/dota-random-builds-front/src/components/Randomizer.vue new file mode 100644 index 0000000..f133251 --- /dev/null +++ b/dota-random-builds-front/src/components/Randomizer.vue @@ -0,0 +1,410 @@ + + + + + diff --git a/dota-random-builds-front/src/data/aspects.ts b/dota-random-builds-front/src/data/aspects.ts new file mode 100644 index 0000000..86a000c --- /dev/null +++ b/dota-random-builds-front/src/data/aspects.ts @@ -0,0 +1,7 @@ +export const aspects = [ + 'Carry', + 'Support', + 'Roamer', + 'Jungler', + 'Pusher' +] diff --git a/dota-random-builds-front/src/data/heroes.ts b/dota-random-builds-front/src/data/heroes.ts new file mode 100644 index 0000000..ff0720d --- /dev/null +++ b/dota-random-builds-front/src/data/heroes.ts @@ -0,0 +1,134 @@ +export interface Hero { + id: string + name: string + primary: 'Strength' | 'Agility' | 'Intelligence' +} + +export const heroes: Hero[] = [ + { id: 'abaddon', name: 'Abaddon', primary: 'Strength' }, + { id: 'alchemist', name: 'Alchemist', primary: 'Strength' }, + { id: 'axe', name: 'Axe', primary: 'Strength' }, + { id: 'beastmaster', name: 'Beastmaster', primary: 'Strength' }, + { id: 'brewmaster', name: 'Brewmaster', primary: 'Strength' }, + { id: 'bristleback', name: 'Bristleback', primary: 'Strength' }, + { id: 'centaur', name: 'Centaur Warrunner', primary: 'Strength' }, + { id: 'chaos_knight', name: 'Chaos Knight', primary: 'Strength' }, + { id: 'clockwerk', name: 'Clockwerk', primary: 'Strength' }, + { id: 'dawnbreaker', name: 'Dawnbreaker', primary: 'Strength' }, + { id: 'doom', name: 'Doom', primary: 'Strength' }, + { id: 'dragon_knight', name: 'Dragon Knight', primary: 'Strength' }, + { id: 'earth_spirit', name: 'Earth Spirit', primary: 'Strength' }, + { id: 'earthshaker', name: 'Earthshaker', primary: 'Strength' }, + { id: 'elder_titan', name: 'Elder Titan', primary: 'Strength' }, + { id: 'huskar', name: 'Huskar', primary: 'Strength' }, + { id: 'kunkka', name: 'Kunkka', primary: 'Strength' }, + { id: 'legion_commander', name: 'Legion Commander', primary: 'Strength' }, + { id: 'lifestealer', name: 'Lifestealer', primary: 'Strength' }, + { id: 'lycan', name: 'Lycan', primary: 'Strength' }, + { id: 'magnus', name: 'Magnus', primary: 'Strength' }, + { id: 'mars', name: 'Mars', primary: 'Strength' }, + { id: 'night_stalker', name: 'Night Stalker', primary: 'Strength' }, + { id: 'ogre_magi', name: 'Ogre Magi', primary: 'Strength' }, + { id: 'omniknight', name: 'Omniknight', primary: 'Strength' }, + { id: 'phoenix', name: 'Phoenix', primary: 'Strength' }, + { id: 'primal_beast', name: 'Primal Beast', primary: 'Strength' }, + { id: 'pudge', name: 'Pudge', primary: 'Strength' }, + { id: 'slardar', name: 'Slardar', primary: 'Strength' }, + { id: 'snapfire', name: 'Snapfire', primary: 'Strength' }, + { id: 'spirit_breaker', name: 'Spirit Breaker', primary: 'Strength' }, + { id: 'sven', name: 'Sven', primary: 'Strength' }, + { id: 'tidehunter', name: 'Tidehunter', primary: 'Strength' }, + { id: 'timbersaw', name: 'Timbersaw', primary: 'Strength' }, + { id: 'tiny', name: 'Tiny', primary: 'Strength' }, + { id: 'treant', name: 'Treant Protector', primary: 'Strength' }, + { id: 'tusk', name: 'Tusk', primary: 'Strength' }, + { id: 'underlord', name: 'Underlord', primary: 'Strength' }, + { id: 'undying', name: 'Undying', primary: 'Strength' }, + { id: 'wraith_king', name: 'Wraith King', primary: 'Strength' }, + + /* -------------------- Agility -------------------- */ + + { id: 'anti_mage', name: 'Anti-Mage', primary: 'Agility' }, + { id: 'arc_warden', name: 'Arc Warden', primary: 'Agility' }, + { id: 'bloodseeker', name: 'Bloodseeker', primary: 'Agility' }, + { id: 'bounty_hunter', name: 'Bounty Hunter', primary: 'Agility' }, + { id: 'broodmother', name: 'Broodmother', primary: 'Agility' }, + { id: 'clinkz', name: 'Clinkz', primary: 'Agility' }, + { id: 'drow_ranger', name: 'Drow Ranger', primary: 'Agility' }, + { id: 'ember_spirit', name: 'Ember Spirit', primary: 'Agility' }, + { id: 'faceless_void', name: 'Faceless Void', primary: 'Agility' }, + { id: 'gyrocopter', name: 'Gyrocopter', primary: 'Agility' }, + { id: 'hoodwink', name: 'Hoodwink', primary: 'Agility' }, + { id: 'juggernaut', name: 'Juggernaut', primary: 'Agility' }, + { id: 'lone_druid', name: 'Lone Druid', primary: 'Agility' }, + { id: 'luna', name: 'Luna', primary: 'Agility' }, + { id: 'medusa', name: 'Medusa', primary: 'Agility' }, + { id: 'meepo', name: 'Meepo', primary: 'Agility' }, + { id: 'mirana', name: 'Mirana', primary: 'Agility' }, + { id: 'monkey_king', name: 'Monkey King', primary: 'Agility' }, + { id: 'muerta', name: 'Muerta', primary: 'Agility' }, + { id: 'naga_siren', name: 'Naga Siren', primary: 'Agility' }, + { id: 'nyx_assassin', name: 'Nyx Assassin', primary: 'Agility' }, + { id: 'pangolier', name: 'Pangolier', primary: 'Agility' }, + { id: 'phantom_assassin', name: 'Phantom Assassin', primary: 'Agility' }, + { id: 'phantom_lancer', name: 'Phantom Lancer', primary: 'Agility' }, + { id: 'razor', name: 'Razor', primary: 'Agility' }, + { id: 'riki', name: 'Riki', primary: 'Agility' }, + { id: 'shadow_fiend', name: 'Shadow Fiend', primary: 'Agility' }, + { id: 'slark', name: 'Slark', primary: 'Agility' }, + { id: 'sniper', name: 'Sniper', primary: 'Agility' }, + { id: 'spectre', name: 'Spectre', primary: 'Agility' }, + { id: 'templar_assassin', name: 'Templar Assassin', primary: 'Agility' }, + { id: 'terrorblade', name: 'Terrorblade', primary: 'Agility' }, + { id: 'troll_warlord', name: 'Troll Warlord', primary: 'Agility' }, + { id: 'ursa', name: 'Ursa', primary: 'Agility' }, + { id: 'viper', name: 'Viper', primary: 'Agility' }, + { id: 'weaver', name: 'Weaver', primary: 'Agility' }, + + /* -------------------- Intelligence -------------------- */ + + { id: 'ancient_apparition', name: 'Ancient Apparition', primary: 'Intelligence' }, + { id: 'bane', name: 'Bane', primary: 'Intelligence' }, + { id: 'batrider', name: 'Batrider', primary: 'Intelligence' }, + { id: 'chen', name: 'Chen', primary: 'Intelligence' }, + { id: 'crystal_maiden', name: 'Crystal Maiden', primary: 'Intelligence' }, + { id: 'dark_seer', name: 'Dark Seer', primary: 'Intelligence' }, + { id: 'dark_willow', name: 'Dark Willow', primary: 'Intelligence' }, + { id: 'dazzle', name: 'Dazzle', primary: 'Intelligence' }, + { id: 'death_prophet', name: 'Death Prophet', primary: 'Intelligence' }, + { id: 'disruptor', name: 'Disruptor', primary: 'Intelligence' }, + { id: 'enchantress', name: 'Enchantress', primary: 'Intelligence' }, + { id: 'enigma', name: 'Enigma', primary: 'Intelligence' }, + { id: 'grimstroke', name: 'Grimstroke', primary: 'Intelligence' }, + { id: 'invoker', name: 'Invoker', primary: 'Intelligence' }, + { id: 'jakiro', name: 'Jakiro', primary: 'Intelligence' }, + { id: 'keeper_of_the_light', name: 'Keeper of the Light', primary: 'Intelligence' }, + { id: 'leshrac', name: 'Leshrac', primary: 'Intelligence' }, + { id: 'lich', name: 'Lich', primary: 'Intelligence' }, + { id: 'lina', name: 'Lina', primary: 'Intelligence' }, + { id: 'lion', name: 'Lion', primary: 'Intelligence' }, + { id: 'muerta', name: 'Muerta', primary: 'Intelligence' }, + { id: 'nature_prophet', name: "Nature's Prophet", primary: 'Intelligence' }, + { id: 'necrophos', name: 'Necrophos', primary: 'Intelligence' }, + { id: 'ogre_magi_int', name: 'Ogre Magi (Int)', primary: 'Intelligence' }, + { id: 'oracle', name: 'Oracle', primary: 'Intelligence' }, + { id: 'outworld_destroyer', name: 'Outworld Destroyer', primary: 'Intelligence' }, + { id: 'puck', name: 'Puck', primary: 'Intelligence' }, + { id: 'pugna', name: 'Pugna', primary: 'Intelligence' }, + { id: 'queen_of_pain', name: 'Queen of Pain', primary: 'Intelligence' }, + { id: 'rubick', name: 'Rubick', primary: 'Intelligence' }, + { id: 'shadow_demon', name: 'Shadow Demon', primary: 'Intelligence' }, + { id: 'shadow_shaman', name: 'Shadow Shaman', primary: 'Intelligence' }, + { id: 'silencer', name: 'Silencer', primary: 'Intelligence' }, + { id: 'skywrath_mage', name: 'Skywrath Mage', primary: 'Intelligence' }, + { id: 'storm_spirit', name: 'Storm Spirit', primary: 'Intelligence' }, + { id: 'techies', name: 'Techies', primary: 'Intelligence' }, + { id: 'tinker', name: 'Tinker', primary: 'Intelligence' }, + { id: 'visage', name: 'Visage', primary: 'Intelligence' }, + { id: 'void_spirit', name: 'Void Spirit', primary: 'Intelligence' }, + { id: 'warlock', name: 'Warlock', primary: 'Intelligence' }, + { id: 'windranger', name: 'Windranger', primary: 'Intelligence' }, + { id: 'winter_wyvern', name: 'Winter Wyvern', primary: 'Intelligence' }, + { id: 'witch_doctor', name: 'Witch Doctor', primary: 'Intelligence' }, + { id: 'zeus', name: 'Zeus', primary: 'Intelligence' } +]; diff --git a/dota-random-builds-front/src/data/items.ts b/dota-random-builds-front/src/data/items.ts new file mode 100644 index 0000000..a9ac623 --- /dev/null +++ b/dota-random-builds-front/src/data/items.ts @@ -0,0 +1,887 @@ +export interface Item { + id: string + name: string +} + + +export const items: Item[] = [ + { + "id": "item_town_portal_scroll", + "name": "Town Portal Scroll" + }, + { + "id": "item_clarity", + "name": "Clarity" + }, + { + "id": "item_faerie_fire", + "name": "Faerie Fire" + }, + { + "id": "item_smoke_of_deceit", + "name": "Smoke of Deceit" + }, + { + "id": "item_observer_ward", + "name": "Observer Ward" + }, + { + "id": "item_sentry_ward", + "name": "Sentry Ward" + }, + { + "id": "item_enchanted_mango", + "name": "Enchanted Mango" + }, + { + "id": "item_healing_salve", + "name": "Healing Salve" + }, + { + "id": "item_tango", + "name": "Tango" + }, + { + "id": "item_blood_grenade", + "name": "Blood Grenade" + }, + { + "id": "item_dust_of_appearance", + "name": "Dust of Appearance" + }, + { + "id": "item_bottle", + "name": "Bottle" + }, + { + "id": "item_aghanim_s_shard", + "name": "Aghanim's Shard" + }, + { + "id": "item_aghanim_s_blessing", + "name": "Aghanim's Blessing" + }, + { + "id": "item_observer_and_sentry_wards", + "name": "Observer and Sentry Wards" + }, + { + "id": "item_iron_branch", + "name": "Iron Branch" + }, + { + "id": "item_gauntlets_of_strength", + "name": "Gauntlets of Strength" + }, + { + "id": "item_slippers_of_agility", + "name": "Slippers of Agility" + }, + { + "id": "item_mantle_of_intelligence", + "name": "Mantle of Intelligence" + }, + { + "id": "item_circlet", + "name": "Circlet" + }, + { + "id": "item_belt_of_strength", + "name": "Belt of Strength" + }, + { + "id": "item_band_of_elvenskin", + "name": "Band of Elvenskin" + }, + { + "id": "item_robe_of_the_magi", + "name": "Robe of the Magi" + }, + { + "id": "item_crown", + "name": "Crown" + }, + { + "id": "item_ogre_axe", + "name": "Ogre Axe" + }, + { + "id": "item_blade_of_alacrity", + "name": "Blade of Alacrity" + }, + { + "id": "item_staff_of_wizardry", + "name": "Staff of Wizardry" + }, + { + "id": "item_diadem", + "name": "Diadem" + }, + { + "id": "item_quelling_blade", + "name": "Quelling Blade" + }, + { + "id": "item_ring_of_protection", + "name": "Ring of Protection" + }, + { + "id": "item_infused_raindrops", + "name": "Infused Raindrops" + }, + { + "id": "item_orb_of_venom", + "name": "Orb of Venom" + }, + { + "id": "item_orb_of_blight", + "name": "Orb of Blight" + }, + { + "id": "item_blades_of_attack", + "name": "Blades of Attack" + }, + { + "id": "item_orb_of_frost", + "name": "Orb of Frost" + }, + { + "id": "item_gloves_of_haste", + "name": "Gloves of Haste" + }, + { + "id": "item_chainmail", + "name": "Chainmail" + }, + { + "id": "item_helm_of_iron_will", + "name": "Helm of Iron Will" + }, + { + "id": "item_broadsword", + "name": "Broadsword" + }, + { + "id": "item_blitz_knuckles", + "name": "Blitz Knuckles" + }, + { + "id": "item_javelin", + "name": "Javelin" + }, + { + "id": "item_claymore", + "name": "Claymore" + }, + { + "id": "item_mithril_hammer", + "name": "Mithril Hammer" + }, + { + "id": "item_ring_of_regen", + "name": "Ring of Regen" + }, + { + "id": "item_sage_s_mask", + "name": "Sage's Mask" + }, + { + "id": "item_magic_stick", + "name": "Magic Stick" + }, + { + "id": "item_fluffy_hat", + "name": "Fluffy Hat" + }, + { + "id": "item_wind_lace", + "name": "Wind Lace" + }, + { + "id": "item_cloak", + "name": "Cloak" + }, + { + "id": "item_boots_of_speed", + "name": "Boots of Speed" + }, + { + "id": "item_gem_of_true_sight", + "name": "Gem of True Sight" + }, + { + "id": "item_morbid_mask", + "name": "Morbid Mask" + }, + { + "id": "item_voodoo_mask", + "name": "Voodoo Mask" + }, + { + "id": "item_shadow_amulet", + "name": "Shadow Amulet" + }, + { + "id": "item_ghost_scepter", + "name": "Ghost Scepter" + }, + { + "id": "item_blink_dagger", + "name": "Blink Dagger" + }, + { + "id": "item_ring_of_health", + "name": "Ring of Health" + }, + { + "id": "item_void_stone", + "name": "Void Stone" + }, + { + "id": "item_magic_wand", + "name": "Magic Wand" + }, + { + "id": "item_null_talisman", + "name": "Null Talisman" + }, + { + "id": "item_wraith_band", + "name": "Wraith Band" + }, + { + "id": "item_bracer", + "name": "Bracer" + }, + { + "id": "item_soul_ring", + "name": "Soul Ring" + }, + { + "id": "item_orb_of_corrosion", + "name": "Orb of Corrosion" + }, + { + "id": "item_falcon_blade", + "name": "Falcon Blade" + }, + { + "id": "item_power_treads", + "name": "Power Treads" + }, + { + "id": "item_phase_boots", + "name": "Phase Boots" + }, + { + "id": "item_oblivion_staff", + "name": "Oblivion Staff" + }, + { + "id": "item_perseverance", + "name": "Perseverance" + }, + { + "id": "item_mask_of_madness", + "name": "Mask of Madness" + }, + { + "id": "item_hand_of_midas", + "name": "Hand of Midas" + }, + { + "id": "item_helm_of_the_dominator", + "name": "Helm of the Dominator" + }, + { + "id": "item_boots_of_travel", + "name": "Boots of Travel" + }, + { + "id": "item_moon_shard", + "name": "Moon Shard" + }, + { + "id": "item_boots_of_travel_2", + "name": "Boots of Travel 2" + }, + { + "id": "item_buckler", + "name": "Buckler" + }, + { + "id": "item_ring_of_basilius", + "name": "Ring of Basilius" + }, + { + "id": "item_headdress", + "name": "Headdress" + }, + { + "id": "item_urn_of_shadows", + "name": "Urn of Shadows" + }, + { + "id": "item_tranquil_boots", + "name": "Tranquil Boots" + }, + { + "id": "item_pavise", + "name": "Pavise" + }, + { + "id": "item_arcane_boots", + "name": "Arcane Boots" + }, + { + "id": "item_drum_of_endurance", + "name": "Drum of Endurance" + }, + { + "id": "item_mekansm", + "name": "Mekansm" + }, + { + "id": "item_holy_locket", + "name": "Holy Locket" + }, + { + "id": "item_vladmir_s_offering", + "name": "Vladmir's Offering" + }, + { + "id": "item_spirit_vessel", + "name": "Spirit Vessel" + }, + { + "id": "item_pipe_of_insight", + "name": "Pipe of Insight" + }, + { + "id": "item_guardian_greaves", + "name": "Guardian Greaves" + }, + { + "id": "item_boots_of_bearing", + "name": "Boots of Bearing" + }, + { + "id": "item_parasma", + "name": "Parasma" + }, + { + "id": "item_veil_of_discord", + "name": "Veil of Discord" + }, + { + "id": "item_glimmer_cape", + "name": "Glimmer Cape" + }, + { + "id": "item_force_staff", + "name": "Force Staff" + }, + { + "id": "item_aether_lens", + "name": "Aether Lens" + }, + { + "id": "item_witch_blade", + "name": "Witch Blade" + }, + { + "id": "item_eul_s_scepter_of_divinity", + "name": "Eul's Scepter of Divinity" + }, + { + "id": "item_rod_of_atos", + "name": "Rod of Atos" + }, + { + "id": "item_dagon", + "name": "Dagon" + }, + { + "id": "item_orchid_malevolence", + "name": "Orchid Malevolence" + }, + { + "id": "item_solar_crest", + "name": "Solar Crest" + }, + { + "id": "item_aghanim_s_scepter", + "name": "Aghanim's Scepter" + }, + { + "id": "item_refresher_orb", + "name": "Refresher Orb" + }, + { + "id": "item_octarine_core", + "name": "Octarine Core" + }, + { + "id": "item_scythe_of_vyse", + "name": "Scythe of Vyse" + }, + { + "id": "item_gleipnir", + "name": "Gleipnir" + }, + { + "id": "item_wind_waker", + "name": "Wind Waker" + }, + { + "id": "item_crystalys", + "name": "Crystalys" + }, + { + "id": "item_meteor_hammer", + "name": "Meteor Hammer" + }, + { + "id": "item_armlet_of_mordiggian", + "name": "Armlet of Mordiggian" + }, + { + "id": "item_skull_basher", + "name": "Skull Basher" + }, + { + "id": "item_shadow_blade", + "name": "Shadow Blade" + }, + { + "id": "item_desolator", + "name": "Desolator" + }, + { + "id": "item_battle_fury", + "name": "Battle Fury" + }, + { + "id": "item_ethereal_blade", + "name": "Ethereal Blade" + }, + { + "id": "item_nullifier", + "name": "Nullifier" + }, + { + "id": "item_monkey_king_bar", + "name": "Monkey King Bar" + }, + { + "id": "item_butterfly", + "name": "Butterfly" + }, + { + "id": "item_radiance", + "name": "Radiance" + }, + { + "id": "item_daedalus", + "name": "Daedalus" + }, + { + "id": "item_silver_edge", + "name": "Silver Edge" + }, + { + "id": "item_divine_rapier", + "name": "Divine Rapier" + }, + { + "id": "item_bloodthorn", + "name": "Bloodthorn" + }, + { + "id": "item_abyssal_blade", + "name": "Abyssal Blade" + }, + { + "id": "item_revenant_s_brooch", + "name": "Revenant's Brooch" + }, + { + "id": "item_disperser", + "name": "Disperser" + }, + { + "id": "item_khanda", + "name": "Khanda" + }, + { + "id": "item_vanguard", + "name": "Vanguard" + }, + { + "id": "item_blade_mail", + "name": "Blade Mail" + }, + { + "id": "item_aeon_disk", + "name": "Aeon Disk" + }, + { + "id": "item_soul_booster", + "name": "Soul Booster" + }, + { + "id": "item_crimson_guard", + "name": "Crimson Guard" + }, + { + "id": "item_lotus_orb", + "name": "Lotus Orb" + }, + { + "id": "item_black_king_bar", + "name": "Black King Bar" + }, + { + "id": "item_hurricane_pike", + "name": "Hurricane Pike" + }, + { + "id": "item_manta_style", + "name": "Manta Style" + }, + { + "id": "item_linken_s_sphere", + "name": "Linken's Sphere" + }, + { + "id": "item_shiva_s_guard", + "name": "Shiva's Guard" + }, + { + "id": "item_heart_of_tarrasque", + "name": "Heart of Tarrasque" + }, + { + "id": "item_assault_cuirass", + "name": "Assault Cuirass" + }, + { + "id": "item_bloodstone", + "name": "Bloodstone" + }, + { + "id": "item_helm_of_the_overlord", + "name": "Helm of the Overlord" + }, + { + "id": "item_eternal_shroud", + "name": "Eternal Shroud" + }, + { + "id": "item_dragon_lance", + "name": "Dragon Lance" + }, + { + "id": "item_sange", + "name": "Sange" + }, + { + "id": "item_yasha", + "name": "Yasha" + }, + { + "id": "item_kaya", + "name": "Kaya" + }, + { + "id": "item_echo_sabre", + "name": "Echo Sabre" + }, + { + "id": "item_maelstrom", + "name": "Maelstrom" + }, + { + "id": "item_diffusal_blade", + "name": "Diffusal Blade" + }, + { + "id": "item_mage_slayer", + "name": "Mage Slayer" + }, + { + "id": "item_phylactery", + "name": "Phylactery" + }, + { + "id": "item_heaven_s_halberd", + "name": "Heaven's Halberd" + }, + { + "id": "item_kaya_and_sange", + "name": "Kaya and Sange" + }, + { + "id": "item_sange_and_yasha", + "name": "Sange and Yasha" + }, + { + "id": "item_yasha_and_kaya", + "name": "Yasha and Kaya" + }, + { + "id": "item_satanic", + "name": "Satanic" + }, + { + "id": "item_eye_of_skadi", + "name": "Eye of Skadi" + }, + { + "id": "item_mjollnir", + "name": "Mjollnir" + }, + { + "id": "item_overwhelming_blink", + "name": "Overwhelming Blink" + }, + { + "id": "item_swift_blink", + "name": "Swift Blink" + }, + { + "id": "item_arcane_blink", + "name": "Arcane Blink" + }, + { + "id": "item_harpoon", + "name": "Harpoon" + }, + { + "id": "item_ring_of_tarrasque", + "name": "Ring of Tarrasque" + }, + { + "id": "item_tiara_of_selemene", + "name": "Tiara of Selemene" + }, + { + "id": "item_cornucopia", + "name": "Cornucopia" + }, + { + "id": "item_energy_booster", + "name": "Energy Booster" + }, + { + "id": "item_vitality_booster", + "name": "Vitality Booster" + }, + { + "id": "item_point_booster", + "name": "Point Booster" + }, + { + "id": "item_talisman_of_evasion", + "name": "Talisman of Evasion" + }, + { + "id": "item_platemail", + "name": "Platemail" + }, + { + "id": "item_hyperstone", + "name": "Hyperstone" + }, + { + "id": "item_ultimate_orb", + "name": "Ultimate Orb" + }, + { + "id": "item_demon_edge", + "name": "Demon Edge" + }, + { + "id": "item_mystic_staff", + "name": "Mystic Staff" + }, + { + "id": "item_reaver", + "name": "Reaver" + }, + { + "id": "item_eaglesong", + "name": "Eaglesong" + }, + { + "id": "item_sacred_relic", + "name": "Sacred Relic" + } +] + +// export const items: Item[] = [ +// { id: 'item_magic_wand', name: 'Magic Wand' }, +// { id: 'item_bracer', name: 'Bracer' }, +// { id: 'item_wraith_band', name: 'Wraith Band' }, +// { id: 'item_null_talisman', name: 'Null Talisman' }, +// { id: 'item_soul_ring', name: 'Soul Ring' }, +// { id: 'item_falcon_blade', name: 'Falcon Blade' }, + +// // Boots +// { id: 'item_phase_boots', name: 'Phase Boots' }, +// { id: 'item_power_treads', name: 'Power Treads' }, +// { id: 'item_arcane_boots', name: 'Arcane Boots' }, +// { id: 'item_tranquil_boots', name: 'Tranquil Boots' }, +// { id: 'item_boots_of_bearing', name: 'Boots of Bearing' }, +// { id: 'item_guardian_greaves', name: 'Guardian Greaves' }, + +// // Mobility +// { id: 'item_blink', name: 'Blink Dagger' }, +// { id: 'item_force_staff', name: 'Force Staff' }, +// { id: 'item_hurricane_pike', name: 'Hurricane Pike' }, +// { id: 'item_overwhelming_blink', name: 'Overwhelming Blink' }, +// { id: 'item_swift_blink', name: 'Swift Blink' }, +// { id: 'item_arcane_blink', name: 'Arcane Blink' }, + +// // Armor / regen aura +// { id: 'item_mekansm', name: 'Mekansm' }, +// { id: 'item_pipe', name: 'Pipe of Insight' }, +// { id: 'item_vladmir', name: 'Vladmir’s Offering' }, +// { id: 'item_solar_crest', name: 'Solar Crest' }, +// { id: 'item_spirit_vessel', name: 'Spirit Vessel' }, + +// // Simple DPS items +// { id: 'item_mask_of_madness', name: 'Mask of Madness' }, +// { id: 'item_armlet', name: 'Armlet of Mordiggian' }, +// { id: 'item_echo_sabre', name: 'Echo Sabre' }, +// { id: 'item_skull_basher', name: 'Skull Basher' }, +// { id: 'item_desolator', name: 'Desolator' }, +// { id: 'item_silver_edge', name: 'Silver Edge' }, + +// // Magic items +// { id: 'item_veil_of_discord', name: 'Veil of Discord' }, +// { id: 'item_aether_lens', name: 'Aether Lens' }, +// { id: 'item_kaya', name: 'Kaya' }, +// { id: 'item_rod_of_atos', name: 'Rod of Atos' }, +// { id: 'item_dagon', name: 'Dagon' }, + +// // HP items +// { id: 'item_hood_of_defiance', name: 'Hood of Defiance' }, +// { id: 'item_heart', name: 'Heart of Tarrasque' }, +// // Strong DPS items +// { id: 'item_monkey_king_bar', name: 'Monkey King Bar' }, +// { id: 'item_butterfly', name: 'Butterfly' }, +// { id: 'item_daedalus', name: 'Daedalus' }, +// { id: 'item_greater_crit', name: 'Greater Crit' }, +// { id: 'item_bfury', name: 'Battle Fury' }, +// { id: 'item_satanic', name: 'Satanic' }, +// { id: 'item_mjollnir', name: 'Mjollnir' }, +// { id: 'item_radiance', name: 'Radiance' }, +// { id: 'item_diffusal_blade', name: 'Diffusal Blade' }, +// { id: 'item_abissal_blade', name: 'Abyssal Blade' }, +// { id: 'item_maelstrom', name: 'Maelstrom' }, +// { id: 'item_mage_slayer', name: 'Mage Slayer' }, + +// // Tank / defensive items +// { id: 'item_black_king_bar', name: 'Black King Bar' }, +// { id: 'item_shivas_guard', name: 'Shiva’s Guard' }, +// { id: 'item_blade_mail', name: 'Blade Mail' }, +// { id: 'item_lotus_orb', name: 'Lotus Orb' }, +// { id: 'item_vanguard', name: 'Vanguard' }, +// { id: 'item_pipe', name: 'Pipe of Insight' }, +// { id: 'item_eternal_shroud', name: 'Eternal Shroud' }, +// { id: 'item_crimson_guard', name: 'Crimson Guard' }, +// { id: 'item_heavens_halberd', name: 'Heaven’s Halberd' }, + +// // Mobility / utility +// { id: 'item_cyclone', name: 'Eul’s Scepter of Divinity' }, +// { id: 'item_glimmer_cape', name: 'Glimmer Cape' }, +// { id: 'item_ghost', name: 'Ghost Scepter' }, +// { id: 'item_ethereal_blade', name: 'Ethereal Blade' }, +// { id: 'item_aeon_disk', name: 'Aeon Disk' }, +// { id: 'item_boots_of_bearing', name: 'Boots of Bearing' }, +// { id: 'item_rod_of_atos', name: 'Rod of Atos' }, +// { id: 'item_solar_crest', name: 'Solar Crest' }, + +// // Healing / support items +// { id: 'item_guardian_greaves', name: 'Guardian Greaves' }, +// { id: 'item_holy_locket', name: 'Holy Locket' }, +// { id: 'item_arcane_boots', name: 'Arcane Boots' }, +// { id: 'item_spirit_vessel', name: 'Spirit Vessel' }, +// { id: 'item_medallion_of_courage', name: 'Medallion of Courage' }, + +// // Magical burst / caster items +// { id: 'item_kaya', name: 'Kaya' }, +// { id: 'item_kaya_and_sange', name: 'Kaya and Sange' }, +// { id: 'item_yasha_and_kaya', name: 'Yasha and Kaya' }, +// { id: 'item_sange_and_yasha', name: 'Sange and Yasha' }, +// { id: 'item_octarine_core', name: 'Octarine Core' }, +// { id: 'item_scythe_of_vyse', name: 'Scythe of Vyse' }, +// { id: 'item_dagon', name: 'Dagon' }, + +// { id: 'item_rapier', name: 'Divine Rapier' }, +// { id: 'item_moon_shard', name: 'Moon Shard' }, +// { id: 'item_silver_edge', name: 'Silver Edge' }, +// { id: 'item_bloodthorn', name: 'Bloodthorn' }, +// { id: 'item_nullifier', name: 'Nullifier' }, + +// // Intelligence & spellpower +// { id: 'item_refresher', name: 'Refresher Orb' }, +// { id: 'item_aghanims_scepter', name: 'Aghanim’s Scepter' }, +// { id: 'item_aghanims_shard', name: 'Aghanim’s Shard' }, +// { id: 'item_witch_blade', name: 'Witch Blade' }, +// { id: 'item_gungir', name: 'Gleipnir' }, + +// // Tank / sustain late game +// { id: 'item_heart', name: 'Heart of Tarrasque' }, +// { id: 'item_assault', name: 'Assault Cuirass' }, +// { id: 'item_satanic', name: 'Satanic' }, +// { id: 'item_harpoon', name: 'Harpoon' }, + +// // Universal top-tier +// { id: 'item_sphere', name: 'Linken’s Sphere' }, +// { id: 'item_skadi', name: 'Eye of Skadi' }, +// { id: 'item_manta', name: 'Manta Style' }, +// { id: 'item_overwhelming_blink', name: 'Overwhelming Blink' }, +// { id: 'item_swift_blink', name: 'Swift Blink' }, + +// // Summon & utility +// { id: 'item_necronomicon', name: 'Necronomicon' }, +// { id: 'item_drum', name: 'Drum of Endurance' }, +// { id: 'item_helm_of_the_overlord', name: 'Helm of the Overlord' }, + +// // Roshan rewards +// { id: 'item_aegis', name: 'Aegis of the Immortal' }, +// { id: 'item_cheese', name: 'Cheese' }, +// { id: 'item_refresher_shard', name: 'Refresher Shard' }, +// { id: 'item_aghanims_blessing', name: 'Aghanim’s Blessing' }, + +// // Neutral items — Tier 1 +// { id: 'item_arcane_ring', name: 'Arcane Ring' }, +// { id: 'item_faded_broach', name: 'Faded Broach' }, +// { id: 'item_keen_optic', name: 'Keen Optic' }, + +// // Tier 2 +// { id: 'item_grove_bow', name: 'Grove Bow' }, +// { id: 'item_pupils_gift', name: 'Pupil’s Gift' }, +// { id: 'item_philosophers_stone', name: 'Philosopher’s Stone' }, + +// // Tier 3 +// { id: 'item_paladin_sword', name: 'Paladin Sword' }, +// { id: 'item_quickening_charm', name: 'Quickening Charm' }, +// { id: 'item_spider_legs', name: 'Spider Legs' }, + +// // Tier 4 +// { id: 'item_spell_prism', name: 'Spell Prism' }, +// { id: 'item_timeless_relic', name: 'Timeless Relic' }, +// { id: 'item_mind_breaker', name: 'Mind Breaker' }, + +// // Tier 5 +// { id: 'item_apex', name: 'Apex' }, +// { id: 'item_ex_machina', name: 'Ex Machina' }, +// { id: 'item_mirror_shield', name: 'Mirror Shield' }, +// { id: 'item_book_of_shadows', name: 'Book of Shadows' }, +// { id: 'item_seer_stone', name: 'Seer Stone' } +// ]; + + diff --git a/dota-random-builds-front/src/data/skills.ts b/dota-random-builds-front/src/data/skills.ts new file mode 100644 index 0000000..b3dcfc8 --- /dev/null +++ b/dota-random-builds-front/src/data/skills.ts @@ -0,0 +1,12 @@ +export interface SkillBuildOption { + id: string + name: string + description?: string +} + +export const skillBuilds: SkillBuildOption[] = [ + { id: 'q_first', name: 'Max Q first', description: 'Prioritize first ability' }, + { id: 'w_first', name: 'Max W first', description: 'Prioritize second ability' }, + { id: 'e_first', name: 'Max E first', description: 'Prioritize third ability' }, + { id: 'balanced', name: 'Balanced', description: 'Evenly distribute points' } +] diff --git a/dota-random-builds-front/src/main.ts b/dota-random-builds-front/src/main.ts new file mode 100644 index 0000000..7398316 --- /dev/null +++ b/dota-random-builds-front/src/main.ts @@ -0,0 +1,13 @@ +import "@/assets/main.css" +import { createApp } from 'vue' +import { createPinia } from 'pinia' + +import App from './App.vue' +import router from './router' + +const app = createApp(App) + +app.use(createPinia()) +app.use(router) + +app.mount('#app') diff --git a/dota-random-builds-front/src/pages/BuildOfTheDayPage.vue b/dota-random-builds-front/src/pages/BuildOfTheDayPage.vue new file mode 100644 index 0000000..e3042ae --- /dev/null +++ b/dota-random-builds-front/src/pages/BuildOfTheDayPage.vue @@ -0,0 +1,306 @@ + + + + + diff --git a/dota-random-builds-front/src/pages/HomePage.vue b/dota-random-builds-front/src/pages/HomePage.vue new file mode 100644 index 0000000..b63b6e1 --- /dev/null +++ b/dota-random-builds-front/src/pages/HomePage.vue @@ -0,0 +1,24 @@ + + + + + diff --git a/dota-random-builds-front/src/router/index.ts b/dota-random-builds-front/src/router/index.ts new file mode 100644 index 0000000..4ceb4b6 --- /dev/null +++ b/dota-random-builds-front/src/router/index.ts @@ -0,0 +1,21 @@ +import HomePage from '@/pages/HomePage.vue' +import BuildOfTheDayPage from '@/pages/BuildOfTheDayPage.vue' +import { createRouter, createWebHistory } from 'vue-router' + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes: [ + { + path: '/', + name: 'HomePage', + component: HomePage + }, + { + path: '/build-of-day', + name: 'BuildOfTheDayPage', + component: BuildOfTheDayPage + } + ] +}); + +export default router diff --git a/dota-random-builds-front/src/stores/counter.ts b/dota-random-builds-front/src/stores/counter.ts new file mode 100644 index 0000000..b6757ba --- /dev/null +++ b/dota-random-builds-front/src/stores/counter.ts @@ -0,0 +1,12 @@ +import { ref, computed } from 'vue' +import { defineStore } from 'pinia' + +export const useCounterStore = defineStore('counter', () => { + const count = ref(0) + const doubleCount = computed(() => count.value * 2) + function increment() { + count.value++ + } + + return { count, doubleCount, increment } +}) diff --git a/dota-random-builds-front/src/stores/randomData.ts b/dota-random-builds-front/src/stores/randomData.ts new file mode 100644 index 0000000..54d0438 --- /dev/null +++ b/dota-random-builds-front/src/stores/randomData.ts @@ -0,0 +1,139 @@ +import axios from 'axios' +import { defineStore } from 'pinia' + +export interface Hero { + id: number + name: string + primary: string +} + +export interface Item { + id: number + name: string +} + +export type SkillBuild = Record + +export interface RandomizeResult { + hero: Hero + items: Item[] + skillBuild?: SkillBuild + aspect?: string +} + +export interface BuildOfDayResult { + date: string + hero: Hero + items: Item[] + skillBuild: SkillBuild + aspect?: string +} + +type RandomizePayload = { + includeSkills: boolean + includeAspect: boolean + itemsCount: number + heroId?: number | null +} + +const PREFS_KEY = 'randomizer:prefs' +const BASE_URL = import.meta.env.VITE_API_URL || '' + +export const useRandomDataStore = defineStore('randomData', { + state: () => ({ + heroes: [] as Hero[], + result: null as RandomizeResult | null, + loading: false, + error: null as string | null, + prefs: { + includeSkills: false, + includeAspect: false, + itemsCount: 6, + heroId: null as number | null + } as RandomizePayload, + buildOfDay: null as BuildOfDayResult | null, + buildOfDayLoading: false, + buildOfDayError: null as string | null + }), + + actions: { + loadPrefs() { + try { + const raw = localStorage.getItem(PREFS_KEY) + if (!raw) return + const parsed = JSON.parse(raw) + this.prefs = { + includeSkills: Boolean(parsed.includeSkills), + includeAspect: Boolean(parsed.includeAspect), + itemsCount: Number(parsed.itemsCount) || 6, + heroId: parsed.heroId ?? null + } + } catch (err) { + console.warn('Failed to load prefs', err) + } + }, + + async loadHeroes() { + try { + const { data } = await axios.get(`${BASE_URL}/api/heroes`) + this.heroes = data + } catch (err) { + console.warn('Failed to load heroes', err) + } + }, + + savePrefs() { + try { + localStorage.setItem(PREFS_KEY, JSON.stringify(this.prefs)) + } catch (err) { + console.warn('Failed to save prefs', err) + } + }, + + setPrefs(patch: Partial) { + this.prefs = { ...this.prefs, ...patch } + this.savePrefs() + }, + + async randomize(overrides?: Partial) { + if (this.loading) return + this.loading = true + this.error = null + + const payload: RandomizePayload = { + ...this.prefs, + ...overrides + } + + // sync prefs with overrides so UI reflects latest choice + this.prefs = payload + this.savePrefs() + + try { + const { data } = await axios.post(`${BASE_URL}/api/randomize`, { ...payload }) + this.result = data + } catch (err) { + const message = err instanceof Error ? err.message : 'Failed to load random build' + this.error = message + } finally { + this.loading = false + } + }, + + async fetchBuildOfDay() { + if (this.buildOfDayLoading) return + this.buildOfDayLoading = true + this.buildOfDayError = null + + try { + const { data } = await axios.get(`${BASE_URL}/api/build-of-day`) + this.buildOfDay = data + } catch (err) { + const message = err instanceof Error ? err.message : 'Failed to load build of the day' + this.buildOfDayError = message + } finally { + this.buildOfDayLoading = false + } + } + } +}) diff --git a/dota-random-builds-front/tsconfig.app.json b/dota-random-builds-front/tsconfig.app.json new file mode 100644 index 0000000..913b8f2 --- /dev/null +++ b/dota-random-builds-front/tsconfig.app.json @@ -0,0 +1,12 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], + "exclude": ["src/**/__tests__/*"], + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/dota-random-builds-front/tsconfig.json b/dota-random-builds-front/tsconfig.json new file mode 100644 index 0000000..66b5e57 --- /dev/null +++ b/dota-random-builds-front/tsconfig.json @@ -0,0 +1,11 @@ +{ + "files": [], + "references": [ + { + "path": "./tsconfig.node.json" + }, + { + "path": "./tsconfig.app.json" + } + ] +} diff --git a/dota-random-builds-front/tsconfig.node.json b/dota-random-builds-front/tsconfig.node.json new file mode 100644 index 0000000..822562d --- /dev/null +++ b/dota-random-builds-front/tsconfig.node.json @@ -0,0 +1,19 @@ +{ + "extends": "@tsconfig/node24/tsconfig.json", + "include": [ + "vite.config.*", + "vitest.config.*", + "cypress.config.*", + "nightwatch.conf.*", + "playwright.config.*", + "eslint.config.*" + ], + "compilerOptions": { + "noEmit": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + + "module": "ESNext", + "moduleResolution": "Bundler", + "types": ["node"] + } +} diff --git a/dota-random-builds-front/vite.config.ts b/dota-random-builds-front/vite.config.ts new file mode 100644 index 0000000..4217010 --- /dev/null +++ b/dota-random-builds-front/vite.config.ts @@ -0,0 +1,18 @@ +import { fileURLToPath, URL } from 'node:url' + +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import vueDevTools from 'vite-plugin-vue-devtools' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [ + vue(), + vueDevTools(), + ], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)) + }, + }, +})