diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index 82e944e..4f7ef7b 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -16,7 +16,15 @@
"Bash(mv core:*)",
"Bash(mv apps/backend_admin app/backend_admin)",
"Bash(mv apps/common app/common)",
- "Bash(mv apps/promociones app/promociones)"
+ "Bash(mv apps/promociones app/promociones)",
+ "WebSearch",
+ "Bash(npm install:*)",
+ "Bash(engram init:*)",
+ "Bash(dir:*)",
+ "Bash(engram --help:*)",
+ "Bash(engram remember:*)",
+ "Bash(engram stats:*)",
+ "Bash(engram recall:*)"
]
}
}
diff --git a/app/api_config/.env.example b/app/api_hub_dispatcher/.env.example
similarity index 100%
rename from app/api_config/.env.example
rename to app/api_hub_dispatcher/.env.example
diff --git a/app/api_config/__init__.py b/app/api_hub_dispatcher/__init__.py
similarity index 100%
rename from app/api_config/__init__.py
rename to app/api_hub_dispatcher/__init__.py
diff --git a/app/api_config/asgi.py b/app/api_hub_dispatcher/asgi.py
similarity index 54%
rename from app/api_config/asgi.py
rename to app/api_hub_dispatcher/asgi.py
index 9fba103..02a6181 100644
--- a/app/api_config/asgi.py
+++ b/app/api_hub_dispatcher/asgi.py
@@ -1,4 +1,4 @@
import os
from django.core.asgi import get_asgi_application
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api_config.settings')
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api_hub_dispatcher.settings')
application = get_asgi_application()
\ No newline at end of file
diff --git a/app/api_config/settings.py b/app/api_hub_dispatcher/settings.py
similarity index 97%
rename from app/api_config/settings.py
rename to app/api_hub_dispatcher/settings.py
index 394d9d0..8962b7c 100644
--- a/app/api_config/settings.py
+++ b/app/api_hub_dispatcher/settings.py
@@ -58,6 +58,7 @@ INSTALLED_APPS = [
# Tus Apps (Asegúrate de que el path sea correcto)
'general',
'promociones',
+ 'automatizados',
'backend_admin',
'common',
]
@@ -73,7 +74,7 @@ MIDDLEWARE = [
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
-ROOT_URLCONF = 'api_config.urls'
+ROOT_URLCONF = 'api_hub_dispatcher.urls'
TEMPLATES = [
{
@@ -91,7 +92,7 @@ TEMPLATES = [
},
]
-WSGI_APPLICATION = 'api_config.wsgi.application'
+WSGI_APPLICATION = 'api_hub_dispatcher.wsgi.application'
# 3. DATABASE
# En producción (cuando DB_HOST está definido) usa PostgreSQL.
diff --git a/app/api_config/urls.py b/app/api_hub_dispatcher/urls.py
similarity index 56%
rename from app/api_config/urls.py
rename to app/api_hub_dispatcher/urls.py
index 66fafab..ae1ed88 100644
--- a/app/api_config/urls.py
+++ b/app/api_hub_dispatcher/urls.py
@@ -2,8 +2,9 @@ from django.urls import path, include
from backend_admin import views as admin_views
urlpatterns = [
- path('general/', include('general.urls')),
path('admin/', include('backend_admin.urls')),
- path('promociones/', include('promociones.urls')),
+ path('api/general/', include('general.urls')),
+ path('api/promociones/', include('promociones.urls')),
+ path('api/automatizados/', include('automatizados.urls')),
path('api/token/', admin_views.api_token, name='token_obtain_pair'),
]
\ No newline at end of file
diff --git a/app/api_config/wsgi.py b/app/api_hub_dispatcher/wsgi.py
similarity index 64%
rename from app/api_config/wsgi.py
rename to app/api_hub_dispatcher/wsgi.py
index a5c9ed2..6e72092 100644
--- a/app/api_config/wsgi.py
+++ b/app/api_hub_dispatcher/wsgi.py
@@ -2,6 +2,6 @@ import os
from django.core.wsgi import get_wsgi_application
# Este es el enlace con tus configuraciones
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api_config.settings')
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api_hub_dispatcher.settings')
application = get_wsgi_application()
\ No newline at end of file
diff --git a/app/automatizados/__init__.py b/app/automatizados/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/app/automatizados/acciones.py b/app/automatizados/acciones.py
new file mode 100644
index 0000000..6ce5e4e
--- /dev/null
+++ b/app/automatizados/acciones.py
@@ -0,0 +1,220 @@
+import json
+import urllib.request
+import urllib.error
+from datetime import datetime
+
+from django.db import connection
+from django.utils import timezone
+from common.utils import clean_sql_string, clean_sql_int
+
+
+# =========================================================
+# Helper HTTP (stdlib) — evita dependencia extra en requirements
+# =========================================================
+def _http_get_json(url, timeout=8):
+ """GET a un servicio externo que responde JSON. Usa stdlib para no añadir deps."""
+ req = urllib.request.Request(url, headers={'User-Agent': 'django-core-base/1.0'})
+ with urllib.request.urlopen(req, timeout=timeout) as response:
+ raw = response.read().decode('utf-8')
+ try:
+ return json.loads(raw)
+ except json.JSONDecodeError:
+ return {'raw': raw}
+
+
+def _http_get_text(url, timeout=8):
+ """GET a un servicio externo que responde texto plano."""
+ req = urllib.request.Request(url, headers={'User-Agent': 'django-core-base/1.0'})
+ with urllib.request.urlopen(req, timeout=timeout) as response:
+ return response.read().decode('utf-8').strip()
+
+
+# =========================================================
+# ACCIÓN 1 — Base de datos (INSERT histórico de ejecución)
+# set_parameterized
+# =========================================================
+def setEjecucion(params):
+ """
+ Inserta una fila en automatizacion_ejecuciones registrando una ejecución.
+ params esperados: nombre, descripcion, estado, origen, resultado, error
+ """
+ query = """
+ INSERT INTO automatizacion_ejecuciones
+ (nombre, descripcion, estado, origen, resultado, error, fecha_inicio, fecha_fin, activo)
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
+ RETURNING id
+ """
+
+ ahora = timezone.now()
+ resultado_json = json.dumps(params.get('resultado')) if params.get('resultado') is not None else None
+
+ parameter_dict = [
+ clean_sql_string(params.get('nombre')),
+ clean_sql_string(params.get('descripcion')),
+ clean_sql_string(params.get('estado') or 'ok'),
+ clean_sql_string(params.get('origen') or 'manual'),
+ resultado_json,
+ clean_sql_string(params.get('error')) if params.get('error') else None,
+ ahora,
+ ahora,
+ bool(params.get('activo', True)),
+ ]
+
+ with connection.cursor() as cursor:
+ cursor.execute(query, parameter_dict)
+ row = cursor.fetchone()
+ nuevo_id = row[0] if row else None
+
+ return {'id': nuevo_id, 'fecha': ahora.isoformat()}
+
+
+# =========================================================
+# ACCIÓN 2 — Servicio externo: Cat Fact API
+# https://catfact.ninja/fact (gratis, sin auth)
+# =========================================================
+def getCatFact():
+ url = 'https://catfact.ninja/fact'
+ data = _http_get_json(url)
+ return {
+ 'servicio': 'catfact.ninja',
+ 'url': url,
+ 'fact': data.get('fact'),
+ 'length': data.get('length'),
+ }
+
+
+# =========================================================
+# ACCIÓN 3 — Servicio externo: GitHub Zen
+# https://api.github.com/zen (gratis, sin auth)
+# =========================================================
+def getGithubZen():
+ url = 'https://api.github.com/zen'
+ texto = _http_get_text(url)
+ return {
+ 'servicio': 'api.github.com/zen',
+ 'url': url,
+ 'zen': texto,
+ }
+
+
+# =========================================================
+# Orquestador — ejecuta las 3 acciones en secuencia
+# =========================================================
+def ejecutarAutomatizaciones(params):
+ """
+ Ejecuta las 3 acciones automatizadas:
+ 1) Llamada a servicio externo: catfact.ninja
+ 2) Llamada a servicio externo: api.github.com/zen
+ 3) Persistencia en BD: inserta la ejecución en automatizacion_ejecuciones
+ """
+ resultados = {'acciones': []}
+ errores = []
+
+ # Acción 2 — catfact
+ try:
+ resultados['acciones'].append({'step': 1, 'ok': True, 'data': getCatFact()})
+ except Exception as err:
+ errores.append(f'catfact: {err}')
+ resultados['acciones'].append({'step': 1, 'ok': False, 'error': str(err)})
+
+ # Acción 3 — github zen
+ try:
+ resultados['acciones'].append({'step': 2, 'ok': True, 'data': getGithubZen()})
+ except Exception as err:
+ errores.append(f'github_zen: {err}')
+ resultados['acciones'].append({'step': 2, 'ok': False, 'error': str(err)})
+
+ # Acción 1 — persistir en BD
+ estado_final = 'ok' if not errores else 'error'
+ nombre = clean_sql_string(params.get('nombre')) or f'Ejecucion {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}'
+ origen = clean_sql_string(params.get('origen')) or 'manual'
+
+ registro = setEjecucion({
+ 'nombre': nombre,
+ 'descripcion': params.get('descripcion') or 'Ejecución orquestada por /api/automatizados/ejecutar/',
+ 'estado': estado_final,
+ 'origen': origen,
+ 'resultado': resultados,
+ 'error': '; '.join(errores) if errores else None,
+ 'activo': True,
+ })
+
+ resultados['acciones'].append({'step': 3, 'ok': True, 'data': registro})
+
+ return {
+ 'ejecucion_id': registro.get('id'),
+ 'estado': estado_final,
+ 'total_acciones': len(resultados['acciones']),
+ 'resultados': resultados,
+ 'errores': errores,
+ }
+
+
+# =========================================================
+# Lectura — historial (get_parameterized)
+# =========================================================
+def getHistorial(params):
+ """
+ Devuelve el histórico de ejecuciones. Permite filtrar por estado y limit.
+ """
+ query = """
+ SELECT id, nombre, descripcion, estado, origen, resultado, error,
+ fecha_inicio, fecha_fin, activo
+ FROM automatizacion_ejecuciones
+ WHERE (%s = '' OR estado = %s)
+ ORDER BY fecha_inicio DESC
+ LIMIT %s
+ """
+
+ estado = clean_sql_string(params.get('estado')) if params.get('estado') else ''
+ limite = clean_sql_int(params.get('limit')) or 20
+
+ parameter_dict = [estado, estado, limite]
+
+ with connection.cursor() as cursor:
+ cursor.execute(query, parameter_dict)
+ columns = [col[0] for col in cursor.description]
+ rows = [dict(zip(columns, row)) for row in cursor.fetchall()]
+
+ # Normalizamos el campo resultado (JSON serializado en SQLite)
+ for row in rows:
+ if isinstance(row.get('resultado'), str):
+ try:
+ row['resultado'] = json.loads(row['resultado'])
+ except (ValueError, TypeError):
+ pass
+
+ return {'total': len(rows), 'data': rows}
+
+
+# =========================================================
+# Estado del módulo
+# =========================================================
+def getEstado():
+ """
+ Devuelve el estado general del módulo de automatizaciones:
+ cuántas ejecuciones hay, última ejecución, endpoints disponibles.
+ """
+ query = """
+ SELECT COUNT(*) AS total,
+ SUM(CASE WHEN estado = 'ok' THEN 1 ELSE 0 END) AS ok,
+ SUM(CASE WHEN estado = 'error' THEN 1 ELSE 0 END) AS errores,
+ MAX(fecha_inicio) AS ultima_ejecucion
+ FROM automatizacion_ejecuciones
+ """
+ with connection.cursor() as cursor:
+ cursor.execute(query)
+ columns = [col[0] for col in cursor.description]
+ row = cursor.fetchone()
+ resumen = dict(zip(columns, row)) if row else {}
+
+ return {
+ 'status': 'ok',
+ 'modulo': 'automatizados',
+ 'endpoints': [
+ 'POST /api/automatizados/ejecutar/',
+ 'POST /api/automatizados/historial/',
+ 'POST /api/automatizados/estado/',
+ ],
+ 'resumen': resumen,
+ }
diff --git a/app/automatizados/apps.py b/app/automatizados/apps.py
new file mode 100644
index 0000000..05cb17a
--- /dev/null
+++ b/app/automatizados/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class AutomatizadosConfig(AppConfig):
+ default_auto_field = 'django.db.models.BigAutoField'
+ name = 'automatizados'
diff --git a/app/automatizados/fixtures/semillas.json b/app/automatizados/fixtures/semillas.json
new file mode 100644
index 0000000..ae5fac8
--- /dev/null
+++ b/app/automatizados/fixtures/semillas.json
@@ -0,0 +1,47 @@
+[
+ {
+ "model": "automatizados.automatizacionejecucion",
+ "pk": 1,
+ "fields": {
+ "nombre": "Ejecución inicial de prueba",
+ "descripcion": "Seed de ejemplo para poblar la tabla de ejecuciones automatizadas",
+ "estado": "ok",
+ "origen": "seed",
+ "resultado": {"acciones": 0, "detalle": "fixture de arranque"},
+ "error": null,
+ "fecha_inicio": "2026-04-16T10:00:00Z",
+ "fecha_fin": "2026-04-16T10:00:05Z",
+ "activo": true
+ }
+ },
+ {
+ "model": "automatizados.automatizacionejecucion",
+ "pk": 2,
+ "fields": {
+ "nombre": "Chequeo nocturno",
+ "descripcion": "Ejemplo de ejecución recurrente programada",
+ "estado": "ok",
+ "origen": "jenkins",
+ "resultado": {"acciones": 3, "detalle": "todas las llamadas OK"},
+ "error": null,
+ "fecha_inicio": "2026-04-16T03:00:00Z",
+ "fecha_fin": "2026-04-16T03:00:12Z",
+ "activo": true
+ }
+ },
+ {
+ "model": "automatizados.automatizacionejecucion",
+ "pk": 3,
+ "fields": {
+ "nombre": "Ejecución con error simulado",
+ "descripcion": "Registro de referencia para estado=error",
+ "estado": "error",
+ "origen": "manual",
+ "resultado": null,
+ "error": "timeout al llamar servicio externo",
+ "fecha_inicio": "2026-04-15T18:30:00Z",
+ "fecha_fin": "2026-04-15T18:30:08Z",
+ "activo": false
+ }
+ }
+]
diff --git a/app/automatizados/migrations/__init__.py b/app/automatizados/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/app/automatizados/models.py b/app/automatizados/models.py
new file mode 100644
index 0000000..c20498e
--- /dev/null
+++ b/app/automatizados/models.py
@@ -0,0 +1,25 @@
+from django.db import models
+
+
+class AutomatizacionEjecucion(models.Model):
+ """
+ Registro histórico de ejecuciones automatizadas.
+ Cada vez que el endpoint `ejecutar/` corre, se guarda una fila con el
+ resultado consolidado de las acciones ejecutadas.
+ """
+ nombre = models.CharField(max_length=255)
+ descripcion = models.TextField(null=True, blank=True)
+ estado = models.CharField(max_length=50, default='pendiente') # pendiente | ok | error
+ origen = models.CharField(max_length=100, default='manual') # manual | jenkins | cron
+ resultado = models.JSONField(null=True, blank=True)
+ error = models.TextField(null=True, blank=True)
+ fecha_inicio = models.DateTimeField(auto_now_add=True)
+ fecha_fin = models.DateTimeField(null=True, blank=True)
+ activo = models.BooleanField(default=True)
+
+ class Meta:
+ db_table = 'automatizacion_ejecuciones'
+ ordering = ['-fecha_inicio']
+
+ def __str__(self):
+ return f'{self.nombre} [{self.estado}]'
diff --git a/app/automatizados/urls.py b/app/automatizados/urls.py
new file mode 100644
index 0000000..038cc57
--- /dev/null
+++ b/app/automatizados/urls.py
@@ -0,0 +1,8 @@
+from django.urls import path
+from .views import AutomatizadosEjecutar, AutomatizadosHistorial, AutomatizadosEstado
+
+urlpatterns = [
+ path('ejecutar/', AutomatizadosEjecutar.as_view(), name='automatizados_ejecutar'),
+ path('historial/', AutomatizadosHistorial.as_view(), name='automatizados_historial'),
+ path('estado/', AutomatizadosEstado.as_view(), name='automatizados_estado'),
+]
diff --git a/app/automatizados/views.py b/app/automatizados/views.py
new file mode 100644
index 0000000..ee97ba4
--- /dev/null
+++ b/app/automatizados/views.py
@@ -0,0 +1,132 @@
+from rest_framework.views import APIView
+from rest_framework.permissions import IsAuthenticated
+from rest_framework_simplejwt.authentication import JWTAuthentication
+from django.http import JsonResponse
+
+from general.utilidades.acciones import LogService
+from .acciones import ejecutarAutomatizaciones, getHistorial, getEstado
+
+
+class AutomatizadosEjecutar(APIView):
+ authentication_classes = [JWTAuthentication]
+ permission_classes = [IsAuthenticated]
+
+ def post(self, request):
+ path = '/automatizados/ejecutar/'
+
+ # --- BLOQUE 1: Inicio Log ---
+ log_id = LogService.gestionar_log(self, request, path=path)
+
+ try:
+ # --- BLOQUE 2: Data Cleaning ---
+ data = request.data
+ status = 100
+ LogService.gestionar_log(self, request, log_id=log_id, path=path, body_request=data, status_code=status)
+
+ params = {
+ 'nombre': data.get('nombre'),
+ 'descripcion': data.get('descripcion'),
+ 'origen': data.get('origen') or 'manual',
+ }
+
+ except Exception as error:
+ response = {'body': {'data': [], 'error': str(error)}, 'mensaje': str(error)}
+ status = 400
+ LogService.gestionar_log(self, request, log_id=log_id, path=path, body_response=response, status_code=status)
+ return JsonResponse(response, status=status, safe=False)
+
+ try:
+ # --- BLOQUE 3: Action Call ---
+ resultado = ejecutarAutomatizaciones(params)
+ response = resultado
+ status = 200
+ # --- BLOQUE 4: Cierre Log ---
+ LogService.gestionar_log(self, request, log_id=log_id, path=path, body_response=response, status_code=status)
+ return JsonResponse(response, safe=False, status=status)
+
+ except Exception as error:
+ response = {'body': {'data': [], 'error': str(error)}, 'error': str(error)}
+ status = 500
+ LogService.gestionar_log(self, request, log_id=log_id, path=path, body_response=response, status_code=status)
+ return JsonResponse(response, status=status, safe=False)
+
+
+class AutomatizadosHistorial(APIView):
+ authentication_classes = [JWTAuthentication]
+ permission_classes = [IsAuthenticated]
+
+ def post(self, request):
+ path = '/automatizados/historial/'
+
+ # --- BLOQUE 1: Inicio Log ---
+ log_id = LogService.gestionar_log(self, request, path=path)
+
+ try:
+ # --- BLOQUE 2: Data Cleaning ---
+ data = request.data
+ status = 100
+ LogService.gestionar_log(self, request, log_id=log_id, path=path, body_request=data, status_code=status)
+
+ params = {
+ 'estado': data.get('estado'),
+ 'limit': data.get('limit') or 20,
+ }
+
+ except Exception as error:
+ response = {'body': {'data': [], 'error': str(error)}, 'mensaje': str(error)}
+ status = 400
+ LogService.gestionar_log(self, request, log_id=log_id, path=path, body_response=response, status_code=status)
+ return JsonResponse(response, status=status, safe=False)
+
+ try:
+ # --- BLOQUE 3: Action Call ---
+ resultado = getHistorial(params)
+ response = resultado
+ status = 200
+ # --- BLOQUE 4: Cierre Log ---
+ LogService.gestionar_log(self, request, log_id=log_id, path=path, body_response=response, status_code=status)
+ return JsonResponse(response, safe=False, status=status)
+
+ except Exception as error:
+ response = {'body': {'data': [], 'error': str(error)}, 'error': str(error)}
+ status = 500
+ LogService.gestionar_log(self, request, log_id=log_id, path=path, body_response=response, status_code=status)
+ return JsonResponse(response, status=status, safe=False)
+
+
+class AutomatizadosEstado(APIView):
+ authentication_classes = [JWTAuthentication]
+ permission_classes = [IsAuthenticated]
+
+ def post(self, request):
+ path = '/automatizados/estado/'
+
+ # --- BLOQUE 1: Inicio Log ---
+ log_id = LogService.gestionar_log(self, request, path=path)
+
+ try:
+ # --- BLOQUE 2: Data Cleaning ---
+ data = request.data
+ status = 100
+ LogService.gestionar_log(self, request, log_id=log_id, path=path, body_request=data, status_code=status)
+
+ except Exception as error:
+ response = {'body': {'data': [], 'error': str(error)}, 'mensaje': str(error)}
+ status = 400
+ LogService.gestionar_log(self, request, log_id=log_id, path=path, body_response=response, status_code=status)
+ return JsonResponse(response, status=status, safe=False)
+
+ try:
+ # --- BLOQUE 3: Action Call ---
+ resultado = getEstado()
+ response = resultado
+ status = 200
+ # --- BLOQUE 4: Cierre Log ---
+ LogService.gestionar_log(self, request, log_id=log_id, path=path, body_response=response, status_code=status)
+ return JsonResponse(response, safe=False, status=status)
+
+ except Exception as error:
+ response = {'body': {'data': [], 'error': str(error)}, 'error': str(error)}
+ status = 500
+ LogService.gestionar_log(self, request, log_id=log_id, path=path, body_response=response, status_code=status)
+ return JsonResponse(response, status=status, safe=False)
diff --git a/app/general/utilidades/acciones.py b/app/general/utilidades/acciones.py
index 15578df..b413d35 100644
--- a/app/general/utilidades/acciones.py
+++ b/app/general/utilidades/acciones.py
@@ -90,6 +90,7 @@ class LogService:
createdAt=timezone.now(),
)
return nuevo_log.pk
+
else:
# --- ACTUALIZACIÓN: enriquece el log con datos del procesamiento ---
diff --git a/app/manage.py b/app/manage.py
index da16c72..b0e4a60 100644
--- a/app/manage.py
+++ b/app/manage.py
@@ -6,7 +6,7 @@ import sys
def main():
"""Run administrative tasks."""
# Apuntamos a la configuración dentro de la carpeta 'core'
- os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api_config.settings')
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api_hub_dispatcher.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
diff --git a/frontend/index.html b/frontend/index.html
new file mode 100644
index 0000000..b1bf49f
--- /dev/null
+++ b/frontend/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Django Core Base
+
+
+
+
+
+
diff --git a/frontend/node_modules/.bin/nanoid b/frontend/node_modules/.bin/nanoid
new file mode 100644
index 0000000..46220bd
--- /dev/null
+++ b/frontend/node_modules/.bin/nanoid
@@ -0,0 +1,16 @@
+#!/bin/sh
+basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
+
+case `uname` in
+ *CYGWIN*|*MINGW*|*MSYS*)
+ if command -v cygpath > /dev/null 2>&1; then
+ basedir=`cygpath -w "$basedir"`
+ fi
+ ;;
+esac
+
+if [ -x "$basedir/node" ]; then
+ exec "$basedir/node" "$basedir/../nanoid/bin/nanoid.cjs" "$@"
+else
+ exec node "$basedir/../nanoid/bin/nanoid.cjs" "$@"
+fi
diff --git a/frontend/node_modules/.bin/nanoid.cmd b/frontend/node_modules/.bin/nanoid.cmd
new file mode 100644
index 0000000..9c40107
--- /dev/null
+++ b/frontend/node_modules/.bin/nanoid.cmd
@@ -0,0 +1,17 @@
+@ECHO off
+GOTO start
+:find_dp0
+SET dp0=%~dp0
+EXIT /b
+:start
+SETLOCAL
+CALL :find_dp0
+
+IF EXIST "%dp0%\node.exe" (
+ SET "_prog=%dp0%\node.exe"
+) ELSE (
+ SET "_prog=node"
+ SET PATHEXT=%PATHEXT:;.JS;=;%
+)
+
+endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\nanoid\bin\nanoid.cjs" %*
diff --git a/frontend/node_modules/.bin/nanoid.ps1 b/frontend/node_modules/.bin/nanoid.ps1
new file mode 100644
index 0000000..d8a4d7a
--- /dev/null
+++ b/frontend/node_modules/.bin/nanoid.ps1
@@ -0,0 +1,28 @@
+#!/usr/bin/env pwsh
+$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
+
+$exe=""
+if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
+ # Fix case when both the Windows and Linux builds of Node
+ # are installed in the same directory
+ $exe=".exe"
+}
+$ret=0
+if (Test-Path "$basedir/node$exe") {
+ # Support pipeline input
+ if ($MyInvocation.ExpectingInput) {
+ $input | & "$basedir/node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
+ } else {
+ & "$basedir/node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
+ }
+ $ret=$LASTEXITCODE
+} else {
+ # Support pipeline input
+ if ($MyInvocation.ExpectingInput) {
+ $input | & "node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
+ } else {
+ & "node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
+ }
+ $ret=$LASTEXITCODE
+}
+exit $ret
diff --git a/frontend/node_modules/.bin/rolldown b/frontend/node_modules/.bin/rolldown
new file mode 100644
index 0000000..e7853ee
--- /dev/null
+++ b/frontend/node_modules/.bin/rolldown
@@ -0,0 +1,16 @@
+#!/bin/sh
+basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
+
+case `uname` in
+ *CYGWIN*|*MINGW*|*MSYS*)
+ if command -v cygpath > /dev/null 2>&1; then
+ basedir=`cygpath -w "$basedir"`
+ fi
+ ;;
+esac
+
+if [ -x "$basedir/node" ]; then
+ exec "$basedir/node" "$basedir/../rolldown/bin/cli.mjs" "$@"
+else
+ exec node "$basedir/../rolldown/bin/cli.mjs" "$@"
+fi
diff --git a/frontend/node_modules/.bin/rolldown.cmd b/frontend/node_modules/.bin/rolldown.cmd
new file mode 100644
index 0000000..1882f3e
--- /dev/null
+++ b/frontend/node_modules/.bin/rolldown.cmd
@@ -0,0 +1,17 @@
+@ECHO off
+GOTO start
+:find_dp0
+SET dp0=%~dp0
+EXIT /b
+:start
+SETLOCAL
+CALL :find_dp0
+
+IF EXIST "%dp0%\node.exe" (
+ SET "_prog=%dp0%\node.exe"
+) ELSE (
+ SET "_prog=node"
+ SET PATHEXT=%PATHEXT:;.JS;=;%
+)
+
+endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\rolldown\bin\cli.mjs" %*
diff --git a/frontend/node_modules/.bin/rolldown.ps1 b/frontend/node_modules/.bin/rolldown.ps1
new file mode 100644
index 0000000..145fb8e
--- /dev/null
+++ b/frontend/node_modules/.bin/rolldown.ps1
@@ -0,0 +1,28 @@
+#!/usr/bin/env pwsh
+$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
+
+$exe=""
+if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
+ # Fix case when both the Windows and Linux builds of Node
+ # are installed in the same directory
+ $exe=".exe"
+}
+$ret=0
+if (Test-Path "$basedir/node$exe") {
+ # Support pipeline input
+ if ($MyInvocation.ExpectingInput) {
+ $input | & "$basedir/node$exe" "$basedir/../rolldown/bin/cli.mjs" $args
+ } else {
+ & "$basedir/node$exe" "$basedir/../rolldown/bin/cli.mjs" $args
+ }
+ $ret=$LASTEXITCODE
+} else {
+ # Support pipeline input
+ if ($MyInvocation.ExpectingInput) {
+ $input | & "node$exe" "$basedir/../rolldown/bin/cli.mjs" $args
+ } else {
+ & "node$exe" "$basedir/../rolldown/bin/cli.mjs" $args
+ }
+ $ret=$LASTEXITCODE
+}
+exit $ret
diff --git a/frontend/node_modules/.bin/tsc b/frontend/node_modules/.bin/tsc
new file mode 100644
index 0000000..c4864b9
--- /dev/null
+++ b/frontend/node_modules/.bin/tsc
@@ -0,0 +1,16 @@
+#!/bin/sh
+basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
+
+case `uname` in
+ *CYGWIN*|*MINGW*|*MSYS*)
+ if command -v cygpath > /dev/null 2>&1; then
+ basedir=`cygpath -w "$basedir"`
+ fi
+ ;;
+esac
+
+if [ -x "$basedir/node" ]; then
+ exec "$basedir/node" "$basedir/../typescript/bin/tsc" "$@"
+else
+ exec node "$basedir/../typescript/bin/tsc" "$@"
+fi
diff --git a/frontend/node_modules/.bin/tsc.cmd b/frontend/node_modules/.bin/tsc.cmd
new file mode 100644
index 0000000..40bf128
--- /dev/null
+++ b/frontend/node_modules/.bin/tsc.cmd
@@ -0,0 +1,17 @@
+@ECHO off
+GOTO start
+:find_dp0
+SET dp0=%~dp0
+EXIT /b
+:start
+SETLOCAL
+CALL :find_dp0
+
+IF EXIST "%dp0%\node.exe" (
+ SET "_prog=%dp0%\node.exe"
+) ELSE (
+ SET "_prog=node"
+ SET PATHEXT=%PATHEXT:;.JS;=;%
+)
+
+endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\typescript\bin\tsc" %*
diff --git a/frontend/node_modules/.bin/tsc.ps1 b/frontend/node_modules/.bin/tsc.ps1
new file mode 100644
index 0000000..112413b
--- /dev/null
+++ b/frontend/node_modules/.bin/tsc.ps1
@@ -0,0 +1,28 @@
+#!/usr/bin/env pwsh
+$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
+
+$exe=""
+if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
+ # Fix case when both the Windows and Linux builds of Node
+ # are installed in the same directory
+ $exe=".exe"
+}
+$ret=0
+if (Test-Path "$basedir/node$exe") {
+ # Support pipeline input
+ if ($MyInvocation.ExpectingInput) {
+ $input | & "$basedir/node$exe" "$basedir/../typescript/bin/tsc" $args
+ } else {
+ & "$basedir/node$exe" "$basedir/../typescript/bin/tsc" $args
+ }
+ $ret=$LASTEXITCODE
+} else {
+ # Support pipeline input
+ if ($MyInvocation.ExpectingInput) {
+ $input | & "node$exe" "$basedir/../typescript/bin/tsc" $args
+ } else {
+ & "node$exe" "$basedir/../typescript/bin/tsc" $args
+ }
+ $ret=$LASTEXITCODE
+}
+exit $ret
diff --git a/frontend/node_modules/.bin/tsserver b/frontend/node_modules/.bin/tsserver
new file mode 100644
index 0000000..6c19ce3
--- /dev/null
+++ b/frontend/node_modules/.bin/tsserver
@@ -0,0 +1,16 @@
+#!/bin/sh
+basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
+
+case `uname` in
+ *CYGWIN*|*MINGW*|*MSYS*)
+ if command -v cygpath > /dev/null 2>&1; then
+ basedir=`cygpath -w "$basedir"`
+ fi
+ ;;
+esac
+
+if [ -x "$basedir/node" ]; then
+ exec "$basedir/node" "$basedir/../typescript/bin/tsserver" "$@"
+else
+ exec node "$basedir/../typescript/bin/tsserver" "$@"
+fi
diff --git a/frontend/node_modules/.bin/tsserver.cmd b/frontend/node_modules/.bin/tsserver.cmd
new file mode 100644
index 0000000..57f851f
--- /dev/null
+++ b/frontend/node_modules/.bin/tsserver.cmd
@@ -0,0 +1,17 @@
+@ECHO off
+GOTO start
+:find_dp0
+SET dp0=%~dp0
+EXIT /b
+:start
+SETLOCAL
+CALL :find_dp0
+
+IF EXIST "%dp0%\node.exe" (
+ SET "_prog=%dp0%\node.exe"
+) ELSE (
+ SET "_prog=node"
+ SET PATHEXT=%PATHEXT:;.JS;=;%
+)
+
+endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\typescript\bin\tsserver" %*
diff --git a/frontend/node_modules/.bin/tsserver.ps1 b/frontend/node_modules/.bin/tsserver.ps1
new file mode 100644
index 0000000..249f417
--- /dev/null
+++ b/frontend/node_modules/.bin/tsserver.ps1
@@ -0,0 +1,28 @@
+#!/usr/bin/env pwsh
+$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
+
+$exe=""
+if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
+ # Fix case when both the Windows and Linux builds of Node
+ # are installed in the same directory
+ $exe=".exe"
+}
+$ret=0
+if (Test-Path "$basedir/node$exe") {
+ # Support pipeline input
+ if ($MyInvocation.ExpectingInput) {
+ $input | & "$basedir/node$exe" "$basedir/../typescript/bin/tsserver" $args
+ } else {
+ & "$basedir/node$exe" "$basedir/../typescript/bin/tsserver" $args
+ }
+ $ret=$LASTEXITCODE
+} else {
+ # Support pipeline input
+ if ($MyInvocation.ExpectingInput) {
+ $input | & "node$exe" "$basedir/../typescript/bin/tsserver" $args
+ } else {
+ & "node$exe" "$basedir/../typescript/bin/tsserver" $args
+ }
+ $ret=$LASTEXITCODE
+}
+exit $ret
diff --git a/frontend/node_modules/.bin/vite b/frontend/node_modules/.bin/vite
new file mode 100644
index 0000000..014463f
--- /dev/null
+++ b/frontend/node_modules/.bin/vite
@@ -0,0 +1,16 @@
+#!/bin/sh
+basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
+
+case `uname` in
+ *CYGWIN*|*MINGW*|*MSYS*)
+ if command -v cygpath > /dev/null 2>&1; then
+ basedir=`cygpath -w "$basedir"`
+ fi
+ ;;
+esac
+
+if [ -x "$basedir/node" ]; then
+ exec "$basedir/node" "$basedir/../vite/bin/vite.js" "$@"
+else
+ exec node "$basedir/../vite/bin/vite.js" "$@"
+fi
diff --git a/frontend/node_modules/.bin/vite.cmd b/frontend/node_modules/.bin/vite.cmd
new file mode 100644
index 0000000..f62e966
--- /dev/null
+++ b/frontend/node_modules/.bin/vite.cmd
@@ -0,0 +1,17 @@
+@ECHO off
+GOTO start
+:find_dp0
+SET dp0=%~dp0
+EXIT /b
+:start
+SETLOCAL
+CALL :find_dp0
+
+IF EXIST "%dp0%\node.exe" (
+ SET "_prog=%dp0%\node.exe"
+) ELSE (
+ SET "_prog=node"
+ SET PATHEXT=%PATHEXT:;.JS;=;%
+)
+
+endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\vite\bin\vite.js" %*
diff --git a/frontend/node_modules/.bin/vite.ps1 b/frontend/node_modules/.bin/vite.ps1
new file mode 100644
index 0000000..a7759bc
--- /dev/null
+++ b/frontend/node_modules/.bin/vite.ps1
@@ -0,0 +1,28 @@
+#!/usr/bin/env pwsh
+$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
+
+$exe=""
+if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
+ # Fix case when both the Windows and Linux builds of Node
+ # are installed in the same directory
+ $exe=".exe"
+}
+$ret=0
+if (Test-Path "$basedir/node$exe") {
+ # Support pipeline input
+ if ($MyInvocation.ExpectingInput) {
+ $input | & "$basedir/node$exe" "$basedir/../vite/bin/vite.js" $args
+ } else {
+ & "$basedir/node$exe" "$basedir/../vite/bin/vite.js" $args
+ }
+ $ret=$LASTEXITCODE
+} else {
+ # Support pipeline input
+ if ($MyInvocation.ExpectingInput) {
+ $input | & "node$exe" "$basedir/../vite/bin/vite.js" $args
+ } else {
+ & "node$exe" "$basedir/../vite/bin/vite.js" $args
+ }
+ $ret=$LASTEXITCODE
+}
+exit $ret
diff --git a/frontend/node_modules/.package-lock.json b/frontend/node_modules/.package-lock.json
new file mode 100644
index 0000000..4fd324b
--- /dev/null
+++ b/frontend/node_modules/.package-lock.json
@@ -0,0 +1,536 @@
+{
+ "name": "frontend",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "node_modules/@oxc-project/types": {
+ "version": "0.124.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz",
+ "integrity": "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ }
+ },
+ "node_modules/@reduxjs/toolkit": {
+ "version": "2.11.2",
+ "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz",
+ "integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@standard-schema/spec": "^1.0.0",
+ "@standard-schema/utils": "^0.3.0",
+ "immer": "^11.0.0",
+ "redux": "^5.0.1",
+ "redux-thunk": "^3.1.0",
+ "reselect": "^5.1.0"
+ },
+ "peerDependencies": {
+ "react": "^16.9.0 || ^17.0.0 || ^18 || ^19",
+ "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ },
+ "react-redux": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.0.0-rc.15",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz",
+ "integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-rc.7",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz",
+ "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@standard-schema/spec": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
+ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
+ "license": "MIT"
+ },
+ "node_modules/@standard-schema/utils": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
+ "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==",
+ "license": "MIT"
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.14",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
+ "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/@types/use-sync-external-store": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
+ "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
+ "license": "MIT"
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz",
+ "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rolldown/pluginutils": "1.0.0-rc.7"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0",
+ "babel-plugin-react-compiler": "^1.0.0",
+ "vite": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@rolldown/plugin-babel": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ }
+ }
+ },
+ "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==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/immer": {
+ "version": "11.1.4",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.4.tgz",
+ "integrity": "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/immer"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
+ "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.32.0",
+ "lightningcss-darwin-arm64": "1.32.0",
+ "lightningcss-darwin-x64": "1.32.0",
+ "lightningcss-freebsd-x64": "1.32.0",
+ "lightningcss-linux-arm-gnueabihf": "1.32.0",
+ "lightningcss-linux-arm64-gnu": "1.32.0",
+ "lightningcss-linux-arm64-musl": "1.32.0",
+ "lightningcss-linux-x64-gnu": "1.32.0",
+ "lightningcss-linux-x64-musl": "1.32.0",
+ "lightningcss-win32-arm64-msvc": "1.32.0",
+ "lightningcss-win32-x64-msvc": "1.32.0"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
+ "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.10",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz",
+ "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.5",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz",
+ "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.5",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz",
+ "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.5"
+ }
+ },
+ "node_modules/react-redux": {
+ "version": "9.2.0",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
+ "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/use-sync-external-store": "^0.0.6",
+ "use-sync-external-store": "^1.4.0"
+ },
+ "peerDependencies": {
+ "@types/react": "^18.2.25 || ^19",
+ "react": "^18.0 || ^19",
+ "redux": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "redux": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/redux": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
+ "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
+ "license": "MIT"
+ },
+ "node_modules/redux-thunk": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
+ "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "redux": "^5.0.0"
+ }
+ },
+ "node_modules/reselect": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
+ "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
+ "license": "MIT"
+ },
+ "node_modules/rolldown": {
+ "version": "1.0.0-rc.15",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz",
+ "integrity": "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@oxc-project/types": "=0.124.0",
+ "@rolldown/pluginutils": "1.0.0-rc.15"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm64": "1.0.0-rc.15",
+ "@rolldown/binding-darwin-arm64": "1.0.0-rc.15",
+ "@rolldown/binding-darwin-x64": "1.0.0-rc.15",
+ "@rolldown/binding-freebsd-x64": "1.0.0-rc.15",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15",
+ "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15",
+ "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15",
+ "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15",
+ "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15",
+ "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15",
+ "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15",
+ "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15",
+ "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15",
+ "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15",
+ "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15"
+ }
+ },
+ "node_modules/rolldown/node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-rc.15",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz",
+ "integrity": "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.16",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
+ "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz",
+ "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/use-sync-external-store": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
+ "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "8.0.8",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz",
+ "integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lightningcss": "^1.32.0",
+ "picomatch": "^4.0.4",
+ "postcss": "^8.5.8",
+ "rolldown": "1.0.0-rc.15",
+ "tinyglobby": "^0.2.15"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "@vitejs/devtools": "^0.1.0",
+ "esbuild": "^0.27.0 || ^0.28.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "@vitejs/devtools": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ }
+ }
+}
diff --git a/frontend/node_modules/@oxc-project/types/LICENSE b/frontend/node_modules/@oxc-project/types/LICENSE
new file mode 100644
index 0000000..444bffc
--- /dev/null
+++ b/frontend/node_modules/@oxc-project/types/LICENSE
@@ -0,0 +1,22 @@
+MIT License
+
+Copyright (c) 2024-present VoidZero Inc. & Contributors
+Copyright (c) 2023 Boshen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/frontend/node_modules/@oxc-project/types/README.md b/frontend/node_modules/@oxc-project/types/README.md
new file mode 100644
index 0000000..d5b248a
--- /dev/null
+++ b/frontend/node_modules/@oxc-project/types/README.md
@@ -0,0 +1,3 @@
+# Oxc Types
+
+Typescript definitions for Oxc AST nodes.
diff --git a/frontend/node_modules/@oxc-project/types/package.json b/frontend/node_modules/@oxc-project/types/package.json
new file mode 100644
index 0000000..99dd182
--- /dev/null
+++ b/frontend/node_modules/@oxc-project/types/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "@oxc-project/types",
+ "version": "0.124.0",
+ "description": "Types for Oxc AST nodes",
+ "keywords": [
+ "AST",
+ "Parser"
+ ],
+ "homepage": "https://oxc.rs",
+ "bugs": "https://github.com/oxc-project/oxc/issues",
+ "license": "MIT",
+ "author": "Boshen and oxc contributors",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/oxc-project/oxc.git",
+ "directory": "npm/oxc-types"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ },
+ "files": [
+ "types.d.ts"
+ ],
+ "type": "module",
+ "types": "types.d.ts"
+}
\ No newline at end of file
diff --git a/frontend/node_modules/@oxc-project/types/types.d.ts b/frontend/node_modules/@oxc-project/types/types.d.ts
new file mode 100644
index 0000000..c94c115
--- /dev/null
+++ b/frontend/node_modules/@oxc-project/types/types.d.ts
@@ -0,0 +1,1912 @@
+// Auto-generated code, DO NOT EDIT DIRECTLY!
+// To edit this generated file you have to edit `tasks/ast_tools/src/generators/typescript.rs`.
+
+export interface Program extends Span {
+ type: "Program";
+ body: Array;
+ sourceType: ModuleKind;
+ hashbang: Hashbang | null;
+ parent?: null;
+}
+
+export type Expression =
+ | BooleanLiteral
+ | NullLiteral
+ | NumericLiteral
+ | BigIntLiteral
+ | RegExpLiteral
+ | StringLiteral
+ | TemplateLiteral
+ | IdentifierReference
+ | MetaProperty
+ | Super
+ | ArrayExpression
+ | ArrowFunctionExpression
+ | AssignmentExpression
+ | AwaitExpression
+ | BinaryExpression
+ | CallExpression
+ | ChainExpression
+ | Class
+ | ConditionalExpression
+ | Function
+ | ImportExpression
+ | LogicalExpression
+ | NewExpression
+ | ObjectExpression
+ | ParenthesizedExpression
+ | SequenceExpression
+ | TaggedTemplateExpression
+ | ThisExpression
+ | UnaryExpression
+ | UpdateExpression
+ | YieldExpression
+ | PrivateInExpression
+ | JSXElement
+ | JSXFragment
+ | TSAsExpression
+ | TSSatisfiesExpression
+ | TSTypeAssertion
+ | TSNonNullExpression
+ | TSInstantiationExpression
+ | V8IntrinsicExpression
+ | MemberExpression;
+
+export interface IdentifierName extends Span {
+ type: "Identifier";
+ decorators?: [];
+ name: string;
+ optional?: false;
+ typeAnnotation?: null;
+ parent?: Node;
+}
+
+export interface IdentifierReference extends Span {
+ type: "Identifier";
+ decorators?: [];
+ name: string;
+ optional?: false;
+ typeAnnotation?: null;
+ parent?: Node;
+}
+
+export interface BindingIdentifier extends Span {
+ type: "Identifier";
+ decorators?: [];
+ name: string;
+ optional?: false;
+ typeAnnotation?: null;
+ parent?: Node;
+}
+
+export interface LabelIdentifier extends Span {
+ type: "Identifier";
+ decorators?: [];
+ name: string;
+ optional?: false;
+ typeAnnotation?: null;
+ parent?: Node;
+}
+
+export interface ThisExpression extends Span {
+ type: "ThisExpression";
+ parent?: Node;
+}
+
+export interface ArrayExpression extends Span {
+ type: "ArrayExpression";
+ elements: Array;
+ parent?: Node;
+}
+
+export type ArrayExpressionElement = SpreadElement | null | Expression;
+
+export interface ObjectExpression extends Span {
+ type: "ObjectExpression";
+ properties: Array;
+ parent?: Node;
+}
+
+export type ObjectPropertyKind = ObjectProperty | SpreadElement;
+
+export interface ObjectProperty extends Span {
+ type: "Property";
+ kind: PropertyKind;
+ key: PropertyKey;
+ value: Expression;
+ method: boolean;
+ shorthand: boolean;
+ computed: boolean;
+ optional?: false;
+ parent?: Node;
+}
+
+export type PropertyKey = IdentifierName | PrivateIdentifier | Expression;
+
+export type PropertyKind = "init" | "get" | "set";
+
+export interface TemplateLiteral extends Span {
+ type: "TemplateLiteral";
+ quasis: Array;
+ expressions: Array;
+ parent?: Node;
+}
+
+export interface TaggedTemplateExpression extends Span {
+ type: "TaggedTemplateExpression";
+ tag: Expression;
+ typeArguments?: TSTypeParameterInstantiation | null;
+ quasi: TemplateLiteral;
+ parent?: Node;
+}
+
+export interface TemplateElement extends Span {
+ type: "TemplateElement";
+ value: TemplateElementValue;
+ tail: boolean;
+ parent?: Node;
+}
+
+export interface TemplateElementValue {
+ raw: string;
+ cooked: string | null;
+}
+
+export type MemberExpression =
+ | ComputedMemberExpression
+ | StaticMemberExpression
+ | PrivateFieldExpression;
+
+export interface ComputedMemberExpression extends Span {
+ type: "MemberExpression";
+ object: Expression;
+ property: Expression;
+ optional: boolean;
+ computed: true;
+ parent?: Node;
+}
+
+export interface StaticMemberExpression extends Span {
+ type: "MemberExpression";
+ object: Expression;
+ property: IdentifierName;
+ optional: boolean;
+ computed: false;
+ parent?: Node;
+}
+
+export interface PrivateFieldExpression extends Span {
+ type: "MemberExpression";
+ object: Expression;
+ property: PrivateIdentifier;
+ optional: boolean;
+ computed: false;
+ parent?: Node;
+}
+
+export interface CallExpression extends Span {
+ type: "CallExpression";
+ callee: Expression;
+ typeArguments?: TSTypeParameterInstantiation | null;
+ arguments: Array;
+ optional: boolean;
+ parent?: Node;
+}
+
+export interface NewExpression extends Span {
+ type: "NewExpression";
+ callee: Expression;
+ typeArguments?: TSTypeParameterInstantiation | null;
+ arguments: Array;
+ parent?: Node;
+}
+
+export interface MetaProperty extends Span {
+ type: "MetaProperty";
+ meta: IdentifierName;
+ property: IdentifierName;
+ parent?: Node;
+}
+
+export interface SpreadElement extends Span {
+ type: "SpreadElement";
+ argument: Expression;
+ parent?: Node;
+}
+
+export type Argument = SpreadElement | Expression;
+
+export interface UpdateExpression extends Span {
+ type: "UpdateExpression";
+ operator: UpdateOperator;
+ prefix: boolean;
+ argument: SimpleAssignmentTarget;
+ parent?: Node;
+}
+
+export interface UnaryExpression extends Span {
+ type: "UnaryExpression";
+ operator: UnaryOperator;
+ argument: Expression;
+ prefix: true;
+ parent?: Node;
+}
+
+export interface BinaryExpression extends Span {
+ type: "BinaryExpression";
+ left: Expression;
+ operator: BinaryOperator;
+ right: Expression;
+ parent?: Node;
+}
+
+export interface PrivateInExpression extends Span {
+ type: "BinaryExpression";
+ left: PrivateIdentifier;
+ operator: "in";
+ right: Expression;
+ parent?: Node;
+}
+
+export interface LogicalExpression extends Span {
+ type: "LogicalExpression";
+ left: Expression;
+ operator: LogicalOperator;
+ right: Expression;
+ parent?: Node;
+}
+
+export interface ConditionalExpression extends Span {
+ type: "ConditionalExpression";
+ test: Expression;
+ consequent: Expression;
+ alternate: Expression;
+ parent?: Node;
+}
+
+export interface AssignmentExpression extends Span {
+ type: "AssignmentExpression";
+ operator: AssignmentOperator;
+ left: AssignmentTarget;
+ right: Expression;
+ parent?: Node;
+}
+
+export type AssignmentTarget = SimpleAssignmentTarget | AssignmentTargetPattern;
+
+export type SimpleAssignmentTarget =
+ | IdentifierReference
+ | TSAsExpression
+ | TSSatisfiesExpression
+ | TSNonNullExpression
+ | TSTypeAssertion
+ | MemberExpression;
+
+export type AssignmentTargetPattern = ArrayAssignmentTarget | ObjectAssignmentTarget;
+
+export interface ArrayAssignmentTarget extends Span {
+ type: "ArrayPattern";
+ decorators?: [];
+ elements: Array;
+ optional?: false;
+ typeAnnotation?: null;
+ parent?: Node;
+}
+
+export interface ObjectAssignmentTarget extends Span {
+ type: "ObjectPattern";
+ decorators?: [];
+ properties: Array;
+ optional?: false;
+ typeAnnotation?: null;
+ parent?: Node;
+}
+
+export interface AssignmentTargetRest extends Span {
+ type: "RestElement";
+ decorators?: [];
+ argument: AssignmentTarget;
+ optional?: false;
+ typeAnnotation?: null;
+ value?: null;
+ parent?: Node;
+}
+
+export type AssignmentTargetMaybeDefault = AssignmentTargetWithDefault | AssignmentTarget;
+
+export interface AssignmentTargetWithDefault extends Span {
+ type: "AssignmentPattern";
+ decorators?: [];
+ left: AssignmentTarget;
+ right: Expression;
+ optional?: false;
+ typeAnnotation?: null;
+ parent?: Node;
+}
+
+export type AssignmentTargetProperty =
+ | AssignmentTargetPropertyIdentifier
+ | AssignmentTargetPropertyProperty;
+
+export interface AssignmentTargetPropertyIdentifier extends Span {
+ type: "Property";
+ kind: "init";
+ key: IdentifierReference;
+ value: IdentifierReference | AssignmentTargetWithDefault;
+ method: false;
+ shorthand: true;
+ computed: false;
+ optional?: false;
+ parent?: Node;
+}
+
+export interface AssignmentTargetPropertyProperty extends Span {
+ type: "Property";
+ kind: "init";
+ key: PropertyKey;
+ value: AssignmentTargetMaybeDefault;
+ method: false;
+ shorthand: false;
+ computed: boolean;
+ optional?: false;
+ parent?: Node;
+}
+
+export interface SequenceExpression extends Span {
+ type: "SequenceExpression";
+ expressions: Array;
+ parent?: Node;
+}
+
+export interface Super extends Span {
+ type: "Super";
+ parent?: Node;
+}
+
+export interface AwaitExpression extends Span {
+ type: "AwaitExpression";
+ argument: Expression;
+ parent?: Node;
+}
+
+export interface ChainExpression extends Span {
+ type: "ChainExpression";
+ expression: ChainElement;
+ parent?: Node;
+}
+
+export type ChainElement = CallExpression | TSNonNullExpression | MemberExpression;
+
+export interface ParenthesizedExpression extends Span {
+ type: "ParenthesizedExpression";
+ expression: Expression;
+ parent?: Node;
+}
+
+export type Statement =
+ | BlockStatement
+ | BreakStatement
+ | ContinueStatement
+ | DebuggerStatement
+ | DoWhileStatement
+ | EmptyStatement
+ | ExpressionStatement
+ | ForInStatement
+ | ForOfStatement
+ | ForStatement
+ | IfStatement
+ | LabeledStatement
+ | ReturnStatement
+ | SwitchStatement
+ | ThrowStatement
+ | TryStatement
+ | WhileStatement
+ | WithStatement
+ | Declaration
+ | ModuleDeclaration;
+
+export interface Directive extends Span {
+ type: "ExpressionStatement";
+ expression: StringLiteral;
+ directive: string;
+ parent?: Node;
+}
+
+export interface Hashbang extends Span {
+ type: "Hashbang";
+ value: string;
+ parent?: Node;
+}
+
+export interface BlockStatement extends Span {
+ type: "BlockStatement";
+ body: Array;
+ parent?: Node;
+}
+
+export type Declaration =
+ | VariableDeclaration
+ | Function
+ | Class
+ | TSTypeAliasDeclaration
+ | TSInterfaceDeclaration
+ | TSEnumDeclaration
+ | TSModuleDeclaration
+ | TSGlobalDeclaration
+ | TSImportEqualsDeclaration;
+
+export interface VariableDeclaration extends Span {
+ type: "VariableDeclaration";
+ kind: VariableDeclarationKind;
+ declarations: Array;
+ declare?: boolean;
+ parent?: Node;
+}
+
+export type VariableDeclarationKind = "var" | "let" | "const" | "using" | "await using";
+
+export interface VariableDeclarator extends Span {
+ type: "VariableDeclarator";
+ id: BindingPattern;
+ init: Expression | null;
+ definite?: boolean;
+ parent?: Node;
+}
+
+export interface EmptyStatement extends Span {
+ type: "EmptyStatement";
+ parent?: Node;
+}
+
+export interface ExpressionStatement extends Span {
+ type: "ExpressionStatement";
+ expression: Expression;
+ directive?: string | null;
+ parent?: Node;
+}
+
+export interface IfStatement extends Span {
+ type: "IfStatement";
+ test: Expression;
+ consequent: Statement;
+ alternate: Statement | null;
+ parent?: Node;
+}
+
+export interface DoWhileStatement extends Span {
+ type: "DoWhileStatement";
+ body: Statement;
+ test: Expression;
+ parent?: Node;
+}
+
+export interface WhileStatement extends Span {
+ type: "WhileStatement";
+ test: Expression;
+ body: Statement;
+ parent?: Node;
+}
+
+export interface ForStatement extends Span {
+ type: "ForStatement";
+ init: ForStatementInit | null;
+ test: Expression | null;
+ update: Expression | null;
+ body: Statement;
+ parent?: Node;
+}
+
+export type ForStatementInit = VariableDeclaration | Expression;
+
+export interface ForInStatement extends Span {
+ type: "ForInStatement";
+ left: ForStatementLeft;
+ right: Expression;
+ body: Statement;
+ parent?: Node;
+}
+
+export type ForStatementLeft = VariableDeclaration | AssignmentTarget;
+
+export interface ForOfStatement extends Span {
+ type: "ForOfStatement";
+ await: boolean;
+ left: ForStatementLeft;
+ right: Expression;
+ body: Statement;
+ parent?: Node;
+}
+
+export interface ContinueStatement extends Span {
+ type: "ContinueStatement";
+ label: LabelIdentifier | null;
+ parent?: Node;
+}
+
+export interface BreakStatement extends Span {
+ type: "BreakStatement";
+ label: LabelIdentifier | null;
+ parent?: Node;
+}
+
+export interface ReturnStatement extends Span {
+ type: "ReturnStatement";
+ argument: Expression | null;
+ parent?: Node;
+}
+
+export interface WithStatement extends Span {
+ type: "WithStatement";
+ object: Expression;
+ body: Statement;
+ parent?: Node;
+}
+
+export interface SwitchStatement extends Span {
+ type: "SwitchStatement";
+ discriminant: Expression;
+ cases: Array;
+ parent?: Node;
+}
+
+export interface SwitchCase extends Span {
+ type: "SwitchCase";
+ test: Expression | null;
+ consequent: Array;
+ parent?: Node;
+}
+
+export interface LabeledStatement extends Span {
+ type: "LabeledStatement";
+ label: LabelIdentifier;
+ body: Statement;
+ parent?: Node;
+}
+
+export interface ThrowStatement extends Span {
+ type: "ThrowStatement";
+ argument: Expression;
+ parent?: Node;
+}
+
+export interface TryStatement extends Span {
+ type: "TryStatement";
+ block: BlockStatement;
+ handler: CatchClause | null;
+ finalizer: BlockStatement | null;
+ parent?: Node;
+}
+
+export interface CatchClause extends Span {
+ type: "CatchClause";
+ param: BindingPattern | null;
+ body: BlockStatement;
+ parent?: Node;
+}
+
+export interface DebuggerStatement extends Span {
+ type: "DebuggerStatement";
+ parent?: Node;
+}
+
+export type BindingPattern = BindingIdentifier | ObjectPattern | ArrayPattern | AssignmentPattern;
+
+export interface AssignmentPattern extends Span {
+ type: "AssignmentPattern";
+ decorators?: [];
+ left: BindingPattern;
+ right: Expression;
+ optional?: false;
+ typeAnnotation?: null;
+ parent?: Node;
+}
+
+export interface ObjectPattern extends Span {
+ type: "ObjectPattern";
+ decorators?: [];
+ properties: Array;
+ optional?: false;
+ typeAnnotation?: null;
+ parent?: Node;
+}
+
+export interface BindingProperty extends Span {
+ type: "Property";
+ kind: "init";
+ key: PropertyKey;
+ value: BindingPattern;
+ method: false;
+ shorthand: boolean;
+ computed: boolean;
+ optional?: false;
+ parent?: Node;
+}
+
+export interface ArrayPattern extends Span {
+ type: "ArrayPattern";
+ decorators?: [];
+ elements: Array;
+ optional?: false;
+ typeAnnotation?: null;
+ parent?: Node;
+}
+
+export interface BindingRestElement extends Span {
+ type: "RestElement";
+ decorators?: [];
+ argument: BindingPattern;
+ optional?: false;
+ typeAnnotation?: null;
+ value?: null;
+ parent?: Node;
+}
+
+export interface Function extends Span {
+ type: FunctionType;
+ id: BindingIdentifier | null;
+ generator: boolean;
+ async: boolean;
+ declare?: boolean;
+ typeParameters?: TSTypeParameterDeclaration | null;
+ params: ParamPattern[];
+ returnType?: TSTypeAnnotation | null;
+ body: FunctionBody | null;
+ expression: false;
+ parent?: Node;
+}
+
+export type ParamPattern = FormalParameter | TSParameterProperty | FormalParameterRest;
+
+export type FunctionType =
+ | "FunctionDeclaration"
+ | "FunctionExpression"
+ | "TSDeclareFunction"
+ | "TSEmptyBodyFunctionExpression";
+
+export interface FormalParameterRest extends Span {
+ type: "RestElement";
+ argument: BindingPattern;
+ decorators?: [];
+ optional?: boolean;
+ typeAnnotation?: TSTypeAnnotation | null;
+ value?: null;
+ parent?: Node;
+}
+
+export type FormalParameter = {
+ decorators?: Array;
+} & BindingPattern;
+
+export interface TSParameterProperty extends Span {
+ type: "TSParameterProperty";
+ accessibility: TSAccessibility | null;
+ decorators: Array;
+ override: boolean;
+ parameter: FormalParameter;
+ readonly: boolean;
+ static: boolean;
+ parent?: Node;
+}
+
+export interface FunctionBody extends Span {
+ type: "BlockStatement";
+ body: Array;
+ parent?: Node;
+}
+
+export interface ArrowFunctionExpression extends Span {
+ type: "ArrowFunctionExpression";
+ expression: boolean;
+ async: boolean;
+ typeParameters?: TSTypeParameterDeclaration | null;
+ params: ParamPattern[];
+ returnType?: TSTypeAnnotation | null;
+ body: FunctionBody | Expression;
+ id: null;
+ generator: false;
+ parent?: Node;
+}
+
+export interface YieldExpression extends Span {
+ type: "YieldExpression";
+ delegate: boolean;
+ argument: Expression | null;
+ parent?: Node;
+}
+
+export interface Class extends Span {
+ type: ClassType;
+ decorators: Array;
+ id: BindingIdentifier | null;
+ typeParameters?: TSTypeParameterDeclaration | null;
+ superClass: Expression | null;
+ superTypeArguments?: TSTypeParameterInstantiation | null;
+ implements?: Array;
+ body: ClassBody;
+ abstract?: boolean;
+ declare?: boolean;
+ parent?: Node;
+}
+
+export type ClassType = "ClassDeclaration" | "ClassExpression";
+
+export interface ClassBody extends Span {
+ type: "ClassBody";
+ body: Array;
+ parent?: Node;
+}
+
+export type ClassElement =
+ | StaticBlock
+ | MethodDefinition
+ | PropertyDefinition
+ | AccessorProperty
+ | TSIndexSignature;
+
+export interface MethodDefinition extends Span {
+ type: MethodDefinitionType;
+ decorators: Array;
+ key: PropertyKey;
+ value: Function;
+ kind: MethodDefinitionKind;
+ computed: boolean;
+ static: boolean;
+ override?: boolean;
+ optional?: boolean;
+ accessibility?: TSAccessibility | null;
+ parent?: Node;
+}
+
+export type MethodDefinitionType = "MethodDefinition" | "TSAbstractMethodDefinition";
+
+export interface PropertyDefinition extends Span {
+ type: PropertyDefinitionType;
+ decorators: Array;
+ key: PropertyKey;
+ typeAnnotation?: TSTypeAnnotation | null;
+ value: Expression | null;
+ computed: boolean;
+ static: boolean;
+ declare?: boolean;
+ override?: boolean;
+ optional?: boolean;
+ definite?: boolean;
+ readonly?: boolean;
+ accessibility?: TSAccessibility | null;
+ parent?: Node;
+}
+
+export type PropertyDefinitionType = "PropertyDefinition" | "TSAbstractPropertyDefinition";
+
+export type MethodDefinitionKind = "constructor" | "method" | "get" | "set";
+
+export interface PrivateIdentifier extends Span {
+ type: "PrivateIdentifier";
+ name: string;
+ parent?: Node;
+}
+
+export interface StaticBlock extends Span {
+ type: "StaticBlock";
+ body: Array;
+ parent?: Node;
+}
+
+export type ModuleDeclaration =
+ | ImportDeclaration
+ | ExportAllDeclaration
+ | ExportDefaultDeclaration
+ | ExportNamedDeclaration
+ | TSExportAssignment
+ | TSNamespaceExportDeclaration;
+
+export type AccessorPropertyType = "AccessorProperty" | "TSAbstractAccessorProperty";
+
+export interface AccessorProperty extends Span {
+ type: AccessorPropertyType;
+ decorators: Array;
+ key: PropertyKey;
+ typeAnnotation?: TSTypeAnnotation | null;
+ value: Expression | null;
+ computed: boolean;
+ static: boolean;
+ override?: boolean;
+ definite?: boolean;
+ accessibility?: TSAccessibility | null;
+ declare?: false;
+ optional?: false;
+ readonly?: false;
+ parent?: Node;
+}
+
+export interface ImportExpression extends Span {
+ type: "ImportExpression";
+ source: Expression;
+ options: Expression | null;
+ phase: ImportPhase | null;
+ parent?: Node;
+}
+
+export interface ImportDeclaration extends Span {
+ type: "ImportDeclaration";
+ specifiers: Array;
+ source: StringLiteral;
+ phase: ImportPhase | null;
+ attributes: Array;
+ importKind?: ImportOrExportKind;
+ parent?: Node;
+}
+
+export type ImportPhase = "source" | "defer";
+
+export type ImportDeclarationSpecifier =
+ | ImportSpecifier
+ | ImportDefaultSpecifier
+ | ImportNamespaceSpecifier;
+
+export interface ImportSpecifier extends Span {
+ type: "ImportSpecifier";
+ imported: ModuleExportName;
+ local: BindingIdentifier;
+ importKind?: ImportOrExportKind;
+ parent?: Node;
+}
+
+export interface ImportDefaultSpecifier extends Span {
+ type: "ImportDefaultSpecifier";
+ local: BindingIdentifier;
+ parent?: Node;
+}
+
+export interface ImportNamespaceSpecifier extends Span {
+ type: "ImportNamespaceSpecifier";
+ local: BindingIdentifier;
+ parent?: Node;
+}
+
+export interface ImportAttribute extends Span {
+ type: "ImportAttribute";
+ key: ImportAttributeKey;
+ value: StringLiteral;
+ parent?: Node;
+}
+
+export type ImportAttributeKey = IdentifierName | StringLiteral;
+
+export interface ExportNamedDeclaration extends Span {
+ type: "ExportNamedDeclaration";
+ declaration: Declaration | null;
+ specifiers: Array;
+ source: StringLiteral | null;
+ exportKind?: ImportOrExportKind;
+ attributes: Array;
+ parent?: Node;
+}
+
+export interface ExportDefaultDeclaration extends Span {
+ type: "ExportDefaultDeclaration";
+ declaration: ExportDefaultDeclarationKind;
+ exportKind?: "value";
+ parent?: Node;
+}
+
+export interface ExportAllDeclaration extends Span {
+ type: "ExportAllDeclaration";
+ exported: ModuleExportName | null;
+ source: StringLiteral;
+ attributes: Array;
+ exportKind?: ImportOrExportKind;
+ parent?: Node;
+}
+
+export interface ExportSpecifier extends Span {
+ type: "ExportSpecifier";
+ local: ModuleExportName;
+ exported: ModuleExportName;
+ exportKind?: ImportOrExportKind;
+ parent?: Node;
+}
+
+export type ExportDefaultDeclarationKind = Function | Class | TSInterfaceDeclaration | Expression;
+
+export type ModuleExportName = IdentifierName | IdentifierReference | StringLiteral;
+
+export interface V8IntrinsicExpression extends Span {
+ type: "V8IntrinsicExpression";
+ name: IdentifierName;
+ arguments: Array;
+ parent?: Node;
+}
+
+export interface BooleanLiteral extends Span {
+ type: "Literal";
+ value: boolean;
+ raw: string | null;
+ parent?: Node;
+}
+
+export interface NullLiteral extends Span {
+ type: "Literal";
+ value: null;
+ raw: "null" | null;
+ parent?: Node;
+}
+
+export interface NumericLiteral extends Span {
+ type: "Literal";
+ value: number;
+ raw: string | null;
+ parent?: Node;
+}
+
+export interface StringLiteral extends Span {
+ type: "Literal";
+ value: string;
+ raw: string | null;
+ parent?: Node;
+}
+
+export interface BigIntLiteral extends Span {
+ type: "Literal";
+ value: bigint;
+ raw: string | null;
+ bigint: string;
+ parent?: Node;
+}
+
+export interface RegExpLiteral extends Span {
+ type: "Literal";
+ value: RegExp | null;
+ raw: string | null;
+ regex: { pattern: string; flags: string };
+ parent?: Node;
+}
+
+export interface JSXElement extends Span {
+ type: "JSXElement";
+ openingElement: JSXOpeningElement;
+ children: Array;
+ closingElement: JSXClosingElement | null;
+ parent?: Node;
+}
+
+export interface JSXOpeningElement extends Span {
+ type: "JSXOpeningElement";
+ name: JSXElementName;
+ typeArguments?: TSTypeParameterInstantiation | null;
+ attributes: Array;
+ selfClosing: boolean;
+ parent?: Node;
+}
+
+export interface JSXClosingElement extends Span {
+ type: "JSXClosingElement";
+ name: JSXElementName;
+ parent?: Node;
+}
+
+export interface JSXFragment extends Span {
+ type: "JSXFragment";
+ openingFragment: JSXOpeningFragment;
+ children: Array;
+ closingFragment: JSXClosingFragment;
+ parent?: Node;
+}
+
+export interface JSXOpeningFragment extends Span {
+ type: "JSXOpeningFragment";
+ attributes?: [];
+ selfClosing?: false;
+ parent?: Node;
+}
+
+export interface JSXClosingFragment extends Span {
+ type: "JSXClosingFragment";
+ parent?: Node;
+}
+
+export type JSXElementName = JSXIdentifier | JSXNamespacedName | JSXMemberExpression;
+
+export interface JSXNamespacedName extends Span {
+ type: "JSXNamespacedName";
+ namespace: JSXIdentifier;
+ name: JSXIdentifier;
+ parent?: Node;
+}
+
+export interface JSXMemberExpression extends Span {
+ type: "JSXMemberExpression";
+ object: JSXMemberExpressionObject;
+ property: JSXIdentifier;
+ parent?: Node;
+}
+
+export type JSXMemberExpressionObject = JSXIdentifier | JSXMemberExpression;
+
+export interface JSXExpressionContainer extends Span {
+ type: "JSXExpressionContainer";
+ expression: JSXExpression;
+ parent?: Node;
+}
+
+export type JSXExpression = JSXEmptyExpression | Expression;
+
+export interface JSXEmptyExpression extends Span {
+ type: "JSXEmptyExpression";
+ parent?: Node;
+}
+
+export type JSXAttributeItem = JSXAttribute | JSXSpreadAttribute;
+
+export interface JSXAttribute extends Span {
+ type: "JSXAttribute";
+ name: JSXAttributeName;
+ value: JSXAttributeValue | null;
+ parent?: Node;
+}
+
+export interface JSXSpreadAttribute extends Span {
+ type: "JSXSpreadAttribute";
+ argument: Expression;
+ parent?: Node;
+}
+
+export type JSXAttributeName = JSXIdentifier | JSXNamespacedName;
+
+export type JSXAttributeValue = StringLiteral | JSXExpressionContainer | JSXElement | JSXFragment;
+
+export interface JSXIdentifier extends Span {
+ type: "JSXIdentifier";
+ name: string;
+ parent?: Node;
+}
+
+export type JSXChild = JSXText | JSXElement | JSXFragment | JSXExpressionContainer | JSXSpreadChild;
+
+export interface JSXSpreadChild extends Span {
+ type: "JSXSpreadChild";
+ expression: Expression;
+ parent?: Node;
+}
+
+export interface JSXText extends Span {
+ type: "JSXText";
+ value: string;
+ raw: string | null;
+ parent?: Node;
+}
+
+export interface TSThisParameter extends Span {
+ type: "Identifier";
+ decorators: [];
+ name: "this";
+ optional: false;
+ typeAnnotation: TSTypeAnnotation | null;
+ parent?: Node;
+}
+
+export interface TSEnumDeclaration extends Span {
+ type: "TSEnumDeclaration";
+ id: BindingIdentifier;
+ body: TSEnumBody;
+ const: boolean;
+ declare: boolean;
+ parent?: Node;
+}
+
+export interface TSEnumBody extends Span {
+ type: "TSEnumBody";
+ members: Array;
+ parent?: Node;
+}
+
+export interface TSEnumMember extends Span {
+ type: "TSEnumMember";
+ id: TSEnumMemberName;
+ initializer: Expression | null;
+ computed: boolean;
+ parent?: Node;
+}
+
+export type TSEnumMemberName = IdentifierName | StringLiteral | TemplateLiteral;
+
+export interface TSTypeAnnotation extends Span {
+ type: "TSTypeAnnotation";
+ typeAnnotation: TSType;
+ parent?: Node;
+}
+
+export interface TSLiteralType extends Span {
+ type: "TSLiteralType";
+ literal: TSLiteral;
+ parent?: Node;
+}
+
+export type TSLiteral =
+ | BooleanLiteral
+ | NumericLiteral
+ | BigIntLiteral
+ | StringLiteral
+ | TemplateLiteral
+ | UnaryExpression;
+
+export type TSType =
+ | TSAnyKeyword
+ | TSBigIntKeyword
+ | TSBooleanKeyword
+ | TSIntrinsicKeyword
+ | TSNeverKeyword
+ | TSNullKeyword
+ | TSNumberKeyword
+ | TSObjectKeyword
+ | TSStringKeyword
+ | TSSymbolKeyword
+ | TSUndefinedKeyword
+ | TSUnknownKeyword
+ | TSVoidKeyword
+ | TSArrayType
+ | TSConditionalType
+ | TSConstructorType
+ | TSFunctionType
+ | TSImportType
+ | TSIndexedAccessType
+ | TSInferType
+ | TSIntersectionType
+ | TSLiteralType
+ | TSMappedType
+ | TSNamedTupleMember
+ | TSTemplateLiteralType
+ | TSThisType
+ | TSTupleType
+ | TSTypeLiteral
+ | TSTypeOperator
+ | TSTypePredicate
+ | TSTypeQuery
+ | TSTypeReference
+ | TSUnionType
+ | TSParenthesizedType
+ | JSDocNullableType
+ | JSDocNonNullableType
+ | JSDocUnknownType;
+
+export interface TSConditionalType extends Span {
+ type: "TSConditionalType";
+ checkType: TSType;
+ extendsType: TSType;
+ trueType: TSType;
+ falseType: TSType;
+ parent?: Node;
+}
+
+export interface TSUnionType extends Span {
+ type: "TSUnionType";
+ types: Array;
+ parent?: Node;
+}
+
+export interface TSIntersectionType extends Span {
+ type: "TSIntersectionType";
+ types: Array;
+ parent?: Node;
+}
+
+export interface TSParenthesizedType extends Span {
+ type: "TSParenthesizedType";
+ typeAnnotation: TSType;
+ parent?: Node;
+}
+
+export interface TSTypeOperator extends Span {
+ type: "TSTypeOperator";
+ operator: TSTypeOperatorOperator;
+ typeAnnotation: TSType;
+ parent?: Node;
+}
+
+export type TSTypeOperatorOperator = "keyof" | "unique" | "readonly";
+
+export interface TSArrayType extends Span {
+ type: "TSArrayType";
+ elementType: TSType;
+ parent?: Node;
+}
+
+export interface TSIndexedAccessType extends Span {
+ type: "TSIndexedAccessType";
+ objectType: TSType;
+ indexType: TSType;
+ parent?: Node;
+}
+
+export interface TSTupleType extends Span {
+ type: "TSTupleType";
+ elementTypes: Array;
+ parent?: Node;
+}
+
+export interface TSNamedTupleMember extends Span {
+ type: "TSNamedTupleMember";
+ label: IdentifierName;
+ elementType: TSTupleElement;
+ optional: boolean;
+ parent?: Node;
+}
+
+export interface TSOptionalType extends Span {
+ type: "TSOptionalType";
+ typeAnnotation: TSType;
+ parent?: Node;
+}
+
+export interface TSRestType extends Span {
+ type: "TSRestType";
+ typeAnnotation: TSType;
+ parent?: Node;
+}
+
+export type TSTupleElement = TSOptionalType | TSRestType | TSType;
+
+export interface TSAnyKeyword extends Span {
+ type: "TSAnyKeyword";
+ parent?: Node;
+}
+
+export interface TSStringKeyword extends Span {
+ type: "TSStringKeyword";
+ parent?: Node;
+}
+
+export interface TSBooleanKeyword extends Span {
+ type: "TSBooleanKeyword";
+ parent?: Node;
+}
+
+export interface TSNumberKeyword extends Span {
+ type: "TSNumberKeyword";
+ parent?: Node;
+}
+
+export interface TSNeverKeyword extends Span {
+ type: "TSNeverKeyword";
+ parent?: Node;
+}
+
+export interface TSIntrinsicKeyword extends Span {
+ type: "TSIntrinsicKeyword";
+ parent?: Node;
+}
+
+export interface TSUnknownKeyword extends Span {
+ type: "TSUnknownKeyword";
+ parent?: Node;
+}
+
+export interface TSNullKeyword extends Span {
+ type: "TSNullKeyword";
+ parent?: Node;
+}
+
+export interface TSUndefinedKeyword extends Span {
+ type: "TSUndefinedKeyword";
+ parent?: Node;
+}
+
+export interface TSVoidKeyword extends Span {
+ type: "TSVoidKeyword";
+ parent?: Node;
+}
+
+export interface TSSymbolKeyword extends Span {
+ type: "TSSymbolKeyword";
+ parent?: Node;
+}
+
+export interface TSThisType extends Span {
+ type: "TSThisType";
+ parent?: Node;
+}
+
+export interface TSObjectKeyword extends Span {
+ type: "TSObjectKeyword";
+ parent?: Node;
+}
+
+export interface TSBigIntKeyword extends Span {
+ type: "TSBigIntKeyword";
+ parent?: Node;
+}
+
+export interface TSTypeReference extends Span {
+ type: "TSTypeReference";
+ typeName: TSTypeName;
+ typeArguments: TSTypeParameterInstantiation | null;
+ parent?: Node;
+}
+
+export type TSTypeName = IdentifierReference | TSQualifiedName | ThisExpression;
+
+export interface TSQualifiedName extends Span {
+ type: "TSQualifiedName";
+ left: TSTypeName;
+ right: IdentifierName;
+ parent?: Node;
+}
+
+export interface TSTypeParameterInstantiation extends Span {
+ type: "TSTypeParameterInstantiation";
+ params: Array;
+ parent?: Node;
+}
+
+export interface TSTypeParameter extends Span {
+ type: "TSTypeParameter";
+ name: BindingIdentifier;
+ constraint: TSType | null;
+ default: TSType | null;
+ in: boolean;
+ out: boolean;
+ const: boolean;
+ parent?: Node;
+}
+
+export interface TSTypeParameterDeclaration extends Span {
+ type: "TSTypeParameterDeclaration";
+ params: Array;
+ parent?: Node;
+}
+
+export interface TSTypeAliasDeclaration extends Span {
+ type: "TSTypeAliasDeclaration";
+ id: BindingIdentifier;
+ typeParameters: TSTypeParameterDeclaration | null;
+ typeAnnotation: TSType;
+ declare: boolean;
+ parent?: Node;
+}
+
+export type TSAccessibility = "private" | "protected" | "public";
+
+export interface TSClassImplements extends Span {
+ type: "TSClassImplements";
+ expression: IdentifierReference | ThisExpression | MemberExpression;
+ typeArguments: TSTypeParameterInstantiation | null;
+ parent?: Node;
+}
+
+export interface TSInterfaceDeclaration extends Span {
+ type: "TSInterfaceDeclaration";
+ id: BindingIdentifier;
+ typeParameters: TSTypeParameterDeclaration | null;
+ extends: Array;
+ body: TSInterfaceBody;
+ declare: boolean;
+ parent?: Node;
+}
+
+export interface TSInterfaceBody extends Span {
+ type: "TSInterfaceBody";
+ body: Array;
+ parent?: Node;
+}
+
+export interface TSPropertySignature extends Span {
+ type: "TSPropertySignature";
+ computed: boolean;
+ optional: boolean;
+ readonly: boolean;
+ key: PropertyKey;
+ typeAnnotation: TSTypeAnnotation | null;
+ accessibility: null;
+ static: false;
+ parent?: Node;
+}
+
+export type TSSignature =
+ | TSIndexSignature
+ | TSPropertySignature
+ | TSCallSignatureDeclaration
+ | TSConstructSignatureDeclaration
+ | TSMethodSignature;
+
+export interface TSIndexSignature extends Span {
+ type: "TSIndexSignature";
+ parameters: Array;
+ typeAnnotation: TSTypeAnnotation;
+ readonly: boolean;
+ static: boolean;
+ accessibility: null;
+ parent?: Node;
+}
+
+export interface TSCallSignatureDeclaration extends Span {
+ type: "TSCallSignatureDeclaration";
+ typeParameters: TSTypeParameterDeclaration | null;
+ params: ParamPattern[];
+ returnType: TSTypeAnnotation | null;
+ parent?: Node;
+}
+
+export type TSMethodSignatureKind = "method" | "get" | "set";
+
+export interface TSMethodSignature extends Span {
+ type: "TSMethodSignature";
+ key: PropertyKey;
+ computed: boolean;
+ optional: boolean;
+ kind: TSMethodSignatureKind;
+ typeParameters: TSTypeParameterDeclaration | null;
+ params: ParamPattern[];
+ returnType: TSTypeAnnotation | null;
+ accessibility: null;
+ readonly: false;
+ static: false;
+ parent?: Node;
+}
+
+export interface TSConstructSignatureDeclaration extends Span {
+ type: "TSConstructSignatureDeclaration";
+ typeParameters: TSTypeParameterDeclaration | null;
+ params: ParamPattern[];
+ returnType: TSTypeAnnotation | null;
+ parent?: Node;
+}
+
+export interface TSIndexSignatureName extends Span {
+ type: "Identifier";
+ decorators: [];
+ name: string;
+ optional: false;
+ typeAnnotation: TSTypeAnnotation;
+ parent?: Node;
+}
+
+export interface TSInterfaceHeritage extends Span {
+ type: "TSInterfaceHeritage";
+ expression: Expression;
+ typeArguments: TSTypeParameterInstantiation | null;
+ parent?: Node;
+}
+
+export interface TSTypePredicate extends Span {
+ type: "TSTypePredicate";
+ parameterName: TSTypePredicateName;
+ asserts: boolean;
+ typeAnnotation: TSTypeAnnotation | null;
+ parent?: Node;
+}
+
+export type TSTypePredicateName = IdentifierName | TSThisType;
+
+export interface TSModuleDeclaration extends Span {
+ type: "TSModuleDeclaration";
+ id: BindingIdentifier | StringLiteral | TSQualifiedName;
+ body: TSModuleBlock | null;
+ kind: TSModuleDeclarationKind;
+ declare: boolean;
+ global: false;
+ parent?: Node;
+}
+
+export type TSModuleDeclarationKind = "module" | "namespace";
+
+export interface TSGlobalDeclaration extends Span {
+ type: "TSModuleDeclaration";
+ id: IdentifierName;
+ body: TSModuleBlock;
+ kind: "global";
+ declare: boolean;
+ global: true;
+ parent?: Node;
+}
+
+export interface TSModuleBlock extends Span {
+ type: "TSModuleBlock";
+ body: Array;
+ parent?: Node;
+}
+
+export interface TSTypeLiteral extends Span {
+ type: "TSTypeLiteral";
+ members: Array;
+ parent?: Node;
+}
+
+export interface TSInferType extends Span {
+ type: "TSInferType";
+ typeParameter: TSTypeParameter;
+ parent?: Node;
+}
+
+export interface TSTypeQuery extends Span {
+ type: "TSTypeQuery";
+ exprName: TSTypeQueryExprName;
+ typeArguments: TSTypeParameterInstantiation | null;
+ parent?: Node;
+}
+
+export type TSTypeQueryExprName = TSImportType | TSTypeName;
+
+export interface TSImportType extends Span {
+ type: "TSImportType";
+ source: StringLiteral;
+ options: ObjectExpression | null;
+ qualifier: TSImportTypeQualifier | null;
+ typeArguments: TSTypeParameterInstantiation | null;
+ parent?: Node;
+}
+
+export type TSImportTypeQualifier = IdentifierName | TSImportTypeQualifiedName;
+
+export interface TSImportTypeQualifiedName extends Span {
+ type: "TSQualifiedName";
+ left: TSImportTypeQualifier;
+ right: IdentifierName;
+ parent?: Node;
+}
+
+export interface TSFunctionType extends Span {
+ type: "TSFunctionType";
+ typeParameters: TSTypeParameterDeclaration | null;
+ params: ParamPattern[];
+ returnType: TSTypeAnnotation;
+ parent?: Node;
+}
+
+export interface TSConstructorType extends Span {
+ type: "TSConstructorType";
+ abstract: boolean;
+ typeParameters: TSTypeParameterDeclaration | null;
+ params: ParamPattern[];
+ returnType: TSTypeAnnotation;
+ parent?: Node;
+}
+
+export interface TSMappedType extends Span {
+ type: "TSMappedType";
+ key: BindingIdentifier;
+ constraint: TSType;
+ nameType: TSType | null;
+ typeAnnotation: TSType | null;
+ optional: TSMappedTypeModifierOperator | false;
+ readonly: TSMappedTypeModifierOperator | null;
+ parent?: Node;
+}
+
+export type TSMappedTypeModifierOperator = true | "+" | "-";
+
+export interface TSTemplateLiteralType extends Span {
+ type: "TSTemplateLiteralType";
+ quasis: Array;
+ types: Array;
+ parent?: Node;
+}
+
+export interface TSAsExpression extends Span {
+ type: "TSAsExpression";
+ expression: Expression;
+ typeAnnotation: TSType;
+ parent?: Node;
+}
+
+export interface TSSatisfiesExpression extends Span {
+ type: "TSSatisfiesExpression";
+ expression: Expression;
+ typeAnnotation: TSType;
+ parent?: Node;
+}
+
+export interface TSTypeAssertion extends Span {
+ type: "TSTypeAssertion";
+ typeAnnotation: TSType;
+ expression: Expression;
+ parent?: Node;
+}
+
+export interface TSImportEqualsDeclaration extends Span {
+ type: "TSImportEqualsDeclaration";
+ id: BindingIdentifier;
+ moduleReference: TSModuleReference;
+ importKind: ImportOrExportKind;
+ parent?: Node;
+}
+
+export type TSModuleReference = TSExternalModuleReference | IdentifierReference | TSQualifiedName;
+
+export interface TSExternalModuleReference extends Span {
+ type: "TSExternalModuleReference";
+ expression: StringLiteral;
+ parent?: Node;
+}
+
+export interface TSNonNullExpression extends Span {
+ type: "TSNonNullExpression";
+ expression: Expression;
+ parent?: Node;
+}
+
+export interface Decorator extends Span {
+ type: "Decorator";
+ expression: Expression;
+ parent?: Node;
+}
+
+export interface TSExportAssignment extends Span {
+ type: "TSExportAssignment";
+ expression: Expression;
+ parent?: Node;
+}
+
+export interface TSNamespaceExportDeclaration extends Span {
+ type: "TSNamespaceExportDeclaration";
+ id: IdentifierName;
+ parent?: Node;
+}
+
+export interface TSInstantiationExpression extends Span {
+ type: "TSInstantiationExpression";
+ expression: Expression;
+ typeArguments: TSTypeParameterInstantiation;
+ parent?: Node;
+}
+
+export type ImportOrExportKind = "value" | "type";
+
+export interface JSDocNullableType extends Span {
+ type: "TSJSDocNullableType";
+ typeAnnotation: TSType;
+ postfix: boolean;
+ parent?: Node;
+}
+
+export interface JSDocNonNullableType extends Span {
+ type: "TSJSDocNonNullableType";
+ typeAnnotation: TSType;
+ postfix: boolean;
+ parent?: Node;
+}
+
+export interface JSDocUnknownType extends Span {
+ type: "TSJSDocUnknownType";
+ parent?: Node;
+}
+
+export type ModuleKind = "script" | "module" | "commonjs";
+
+export interface Span {
+ start: number;
+ end: number;
+ range?: [number, number];
+}
+
+export type AssignmentOperator =
+ | "="
+ | "+="
+ | "-="
+ | "*="
+ | "/="
+ | "%="
+ | "**="
+ | "<<="
+ | ">>="
+ | ">>>="
+ | "|="
+ | "^="
+ | "&="
+ | "||="
+ | "&&="
+ | "??=";
+
+export type BinaryOperator =
+ | "=="
+ | "!="
+ | "==="
+ | "!=="
+ | "<"
+ | "<="
+ | ">"
+ | ">="
+ | "+"
+ | "-"
+ | "*"
+ | "/"
+ | "%"
+ | "**"
+ | "<<"
+ | ">>"
+ | ">>>"
+ | "|"
+ | "^"
+ | "&"
+ | "in"
+ | "instanceof";
+
+export type LogicalOperator = "||" | "&&" | "??";
+
+export type UnaryOperator = "+" | "-" | "!" | "~" | "typeof" | "void" | "delete";
+
+export type UpdateOperator = "++" | "--";
+
+export type Node =
+ | Program
+ | IdentifierName
+ | IdentifierReference
+ | BindingIdentifier
+ | LabelIdentifier
+ | ThisExpression
+ | ArrayExpression
+ | ObjectExpression
+ | ObjectProperty
+ | TemplateLiteral
+ | TaggedTemplateExpression
+ | TemplateElement
+ | ComputedMemberExpression
+ | StaticMemberExpression
+ | PrivateFieldExpression
+ | CallExpression
+ | NewExpression
+ | MetaProperty
+ | SpreadElement
+ | UpdateExpression
+ | UnaryExpression
+ | BinaryExpression
+ | PrivateInExpression
+ | LogicalExpression
+ | ConditionalExpression
+ | AssignmentExpression
+ | ArrayAssignmentTarget
+ | ObjectAssignmentTarget
+ | AssignmentTargetRest
+ | AssignmentTargetWithDefault
+ | AssignmentTargetPropertyIdentifier
+ | AssignmentTargetPropertyProperty
+ | SequenceExpression
+ | Super
+ | AwaitExpression
+ | ChainExpression
+ | ParenthesizedExpression
+ | Directive
+ | Hashbang
+ | BlockStatement
+ | VariableDeclaration
+ | VariableDeclarator
+ | EmptyStatement
+ | ExpressionStatement
+ | IfStatement
+ | DoWhileStatement
+ | WhileStatement
+ | ForStatement
+ | ForInStatement
+ | ForOfStatement
+ | ContinueStatement
+ | BreakStatement
+ | ReturnStatement
+ | WithStatement
+ | SwitchStatement
+ | SwitchCase
+ | LabeledStatement
+ | ThrowStatement
+ | TryStatement
+ | CatchClause
+ | DebuggerStatement
+ | AssignmentPattern
+ | ObjectPattern
+ | BindingProperty
+ | ArrayPattern
+ | BindingRestElement
+ | Function
+ | FunctionBody
+ | ArrowFunctionExpression
+ | YieldExpression
+ | Class
+ | ClassBody
+ | MethodDefinition
+ | PropertyDefinition
+ | PrivateIdentifier
+ | StaticBlock
+ | AccessorProperty
+ | ImportExpression
+ | ImportDeclaration
+ | ImportSpecifier
+ | ImportDefaultSpecifier
+ | ImportNamespaceSpecifier
+ | ImportAttribute
+ | ExportNamedDeclaration
+ | ExportDefaultDeclaration
+ | ExportAllDeclaration
+ | ExportSpecifier
+ | V8IntrinsicExpression
+ | BooleanLiteral
+ | NullLiteral
+ | NumericLiteral
+ | StringLiteral
+ | BigIntLiteral
+ | RegExpLiteral
+ | JSXElement
+ | JSXOpeningElement
+ | JSXClosingElement
+ | JSXFragment
+ | JSXOpeningFragment
+ | JSXClosingFragment
+ | JSXNamespacedName
+ | JSXMemberExpression
+ | JSXExpressionContainer
+ | JSXEmptyExpression
+ | JSXAttribute
+ | JSXSpreadAttribute
+ | JSXIdentifier
+ | JSXSpreadChild
+ | JSXText
+ | TSThisParameter
+ | TSEnumDeclaration
+ | TSEnumBody
+ | TSEnumMember
+ | TSTypeAnnotation
+ | TSLiteralType
+ | TSConditionalType
+ | TSUnionType
+ | TSIntersectionType
+ | TSParenthesizedType
+ | TSTypeOperator
+ | TSArrayType
+ | TSIndexedAccessType
+ | TSTupleType
+ | TSNamedTupleMember
+ | TSOptionalType
+ | TSRestType
+ | TSAnyKeyword
+ | TSStringKeyword
+ | TSBooleanKeyword
+ | TSNumberKeyword
+ | TSNeverKeyword
+ | TSIntrinsicKeyword
+ | TSUnknownKeyword
+ | TSNullKeyword
+ | TSUndefinedKeyword
+ | TSVoidKeyword
+ | TSSymbolKeyword
+ | TSThisType
+ | TSObjectKeyword
+ | TSBigIntKeyword
+ | TSTypeReference
+ | TSQualifiedName
+ | TSTypeParameterInstantiation
+ | TSTypeParameter
+ | TSTypeParameterDeclaration
+ | TSTypeAliasDeclaration
+ | TSClassImplements
+ | TSInterfaceDeclaration
+ | TSInterfaceBody
+ | TSPropertySignature
+ | TSIndexSignature
+ | TSCallSignatureDeclaration
+ | TSMethodSignature
+ | TSConstructSignatureDeclaration
+ | TSIndexSignatureName
+ | TSInterfaceHeritage
+ | TSTypePredicate
+ | TSModuleDeclaration
+ | TSGlobalDeclaration
+ | TSModuleBlock
+ | TSTypeLiteral
+ | TSInferType
+ | TSTypeQuery
+ | TSImportType
+ | TSImportTypeQualifiedName
+ | TSFunctionType
+ | TSConstructorType
+ | TSMappedType
+ | TSTemplateLiteralType
+ | TSAsExpression
+ | TSSatisfiesExpression
+ | TSTypeAssertion
+ | TSImportEqualsDeclaration
+ | TSExternalModuleReference
+ | TSNonNullExpression
+ | Decorator
+ | TSExportAssignment
+ | TSNamespaceExportDeclaration
+ | TSInstantiationExpression
+ | JSDocNullableType
+ | JSDocNonNullableType
+ | JSDocUnknownType
+ | ParamPattern;
diff --git a/frontend/node_modules/@reduxjs/toolkit/LICENSE b/frontend/node_modules/@reduxjs/toolkit/LICENSE
new file mode 100644
index 0000000..1daa252
--- /dev/null
+++ b/frontend/node_modules/@reduxjs/toolkit/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Mark Erikson
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/frontend/node_modules/@reduxjs/toolkit/README.md b/frontend/node_modules/@reduxjs/toolkit/README.md
new file mode 100644
index 0000000..a2514f3
--- /dev/null
+++ b/frontend/node_modules/@reduxjs/toolkit/README.md
@@ -0,0 +1,110 @@
+# Redux Toolkit
+
+
+[](https://www.npmjs.com/package/@reduxjs/toolkit)
+[](https://www.npmjs.com/package/@reduxjs/toolkit)
+
+**The official, opinionated, batteries-included toolset for efficient Redux development**
+
+## Installation
+
+### Create a React Redux App
+
+The recommended way to start new apps with React and Redux Toolkit is by using [our official Redux Toolkit + TS template for Vite](https://github.com/reduxjs/redux-templates), or by creating a new Next.js project using [Next's `with-redux` template](https://github.com/vercel/next.js/tree/canary/examples/with-redux).
+
+Both of these already have Redux Toolkit and React-Redux configured appropriately for that build tool, and come with a small example app that demonstrates how to use several of Redux Toolkit's features.
+
+```bash
+# Vite with our Redux+TS template
+# (using the `degit` tool to clone and extract the template)
+npx degit reduxjs/redux-templates/packages/vite-template-redux my-app
+
+# Next.js using the `with-redux` template
+npx create-next-app --example with-redux my-app
+```
+
+We do not currently have official React Native templates, but recommend these templates for standard React Native and for Expo:
+
+- https://github.com/rahsheen/react-native-template-redux-typescript
+- https://github.com/rahsheen/expo-template-redux-typescript
+
+### An Existing App
+
+Redux Toolkit is available as a package on NPM for use with a module bundler or in a Node application:
+
+```bash
+# NPM
+npm install @reduxjs/toolkit
+
+# Yarn
+yarn add @reduxjs/toolkit
+```
+
+The package includes a precompiled ESM build that can be used as a [`