-
Objetos y pruebas
-
- Los objetos que guardas en Objetos pueden servir para abrir otros nodos. Si encuentras una
- tarjeta, palabra, QR, NFC, sobre o prop, registralo desde Prueba.
-
- {needsItem ? (
-
Este nodo comprueba si llevas el objeto requerido antes de avanzar.
- ) : null}
- {givesItem ?
Este nodo puede darte una recompensa: {rewardItem}.
: null}
- {!physical ? (
-
No hay prueba fisica especial detectada en este nodo.
- ) : null}
+
+ {/* Step 1: Ubicación */}
+ {needsGps && (
+
+
1
+
+
Ve al punto en el mapa
+
+ Acércate físicamente a la ubicación marcada. Necesitas estar a menos de {radius || 50} metros para que tu escáner detecte el nodo.
+
+
+
+ )}
+
+ {/* Step 2: Objeto Requerido (Optional) */}
+ {requiredItem && (
+
+
{needsGps ? '2' : '1'}
+
+
Equipa el objeto necesario
+
+ Para poder interactuar con este nodo, necesitas tener esto en tu mochila:
+
+
+
+ {requiredItem.replace(/_/g, ' ')}
+
+
+
+ )}
+
+ {/* Step 3: Acción del Juego */}
+
+
{requiredItem ? (needsGps ? '3' : '2') : (needsGps ? '2' : '1')}
+
+
+ {isSignal && 'Captura la señal estable'}
+ {isCompass && 'Calibra la brújula y gira'}
+ {isPuzzle && 'Resuelve el código'}
+ {isPhysical && 'Escanea el elemento'}
+ {!isSignal && !isCompass && !isPuzzle && !isPhysical && 'Sigue las instrucciones'}
+
+
+ {isSignal && 'Una vez en la zona, mantén tu posición sin salir del círculo hasta que la barra se llene por completo al 100%.'}
+ {isCompass && 'Gira lentamente sobre ti mismo hasta que tu teléfono apunte en la dirección correcta para decodificar.'}
+ {isPuzzle && 'Observa tu entorno físico y la información que tienes. Introduce el patrón o código correcto para acceder.'}
+ {isPhysical && 'Busca un código QR, etiqueta NFC o pista física en la vida real. Usa el botón inferior para escanearlo.'}
+ {!isSignal && !isCompass && !isPuzzle && !isPhysical && 'Lee cuidadosamente la descripción de la misión para saber qué hacer a continuación.'}
+
+
+
-
-
Jugador local: {user}
)
}
-function Mini({ label, value }: { label: string; value: string }) {
- return (
-
- {value}
- {label}
-
- )
-}
-
+// ─── Estilos Modernos ────────────────────────────────────────────────────────
const panel: CSSProperties = {
- display: 'grid',
- gap: 10,
- padding: 8,
+ display: 'flex',
+ flexDirection: 'column',
+ gap: 16,
+ padding: '20px',
+ background: 'linear-gradient(to bottom, rgba(15,23,42,0.8), rgba(15,23,42,0.95))',
+ borderRadius: '16px',
+ border: '1px solid rgba(255,255,255,0.05)',
+ boxShadow: '0 8px 32px rgba(0,0,0,0.4)',
}
const header: CSSProperties = {
display: 'flex',
- justifyContent: 'space-between',
- gap: 10,
+ flexDirection: 'column',
+ gap: 4,
}
const eyebrow: CSSProperties = {
- color: '#bbf7d0',
- fontSize: 10,
- fontWeight: 950,
- letterSpacing: '0.14em',
+ color: '#38bdf8',
+ fontSize: 11,
+ fontWeight: 800,
+ letterSpacing: '0.15em',
+ textTransform: 'uppercase',
}
const title: CSSProperties = {
- marginTop: 4,
color: '#ffffff',
- fontSize: 15,
- lineHeight: 1.1,
- fontWeight: 950,
+ fontSize: 18,
+ lineHeight: 1.2,
+ fontWeight: 900,
}
-const badge: CSSProperties = {
- alignSelf: 'flex-start',
- borderRadius: 999,
- border: '1px solid rgba(125,211,252,.22)',
- background: 'rgba(14,165,233,.14)',
- color: '#dbeafe',
- padding: '7px 9px',
- fontSize: 9,
- fontWeight: 950,
+const copy: CSSProperties = {
+ color: '#94a3b8',
+ fontSize: 13,
+ lineHeight: 1.5,
+ margin: 0,
}
-const statusGrid: CSSProperties = {
- display: 'grid',
- gridTemplateColumns: 'repeat(3, minmax(0, 1fr))',
- gap: 8,
+const stepsContainer: CSSProperties = {
+ display: 'flex',
+ flexDirection: 'column',
+ gap: 16,
+ position: 'relative',
}
-const mini: CSSProperties = {
- display: 'grid',
- gap: 2,
- padding: 4,
- textAlign: 'center',
+const stepRow: CSSProperties = {
+ display: 'flex',
+ gap: 12,
+ position: 'relative',
}
-const block: CSSProperties = {
- display: 'grid',
- gap: 6,
- padding: 4,
+const stepNumber: CSSProperties = {
+ flexShrink: 0,
+ width: 24,
+ height: 24,
+ borderRadius: '12px',
+ background: 'rgba(56,189,248,0.15)',
+ color: '#38bdf8',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ fontSize: 13,
+ fontWeight: 800,
+ border: '1px solid rgba(56,189,248,0.3)',
+ zIndex: 2,
}
-const list: CSSProperties = {
- display: 'grid',
- gap: 5,
- margin: 0,
- paddingLeft: 18,
- color: 'rgba(226,232,240,.78)',
- fontSize: 12,
- lineHeight: 1.35,
- fontWeight: 750,
+const stepContent: CSSProperties = {
+ display: 'flex',
+ flexDirection: 'column',
+ gap: 4,
+ paddingTop: 2,
}
-const copy: CSSProperties = {
- margin: 0,
- color: 'rgba(226,232,240,.78)',
- fontSize: 12,
+const stepTitle: CSSProperties = {
+ color: '#f8fafc',
+ fontSize: 14,
+ fontWeight: 700,
+}
+
+const stepDesc: CSSProperties = {
+ color: '#94a3b8',
+ fontSize: 13,
lineHeight: 1.4,
- fontWeight: 750,
}
-const footer: CSSProperties = {
- color: 'rgba(226,232,240,.46)',
- fontSize: 10,
- fontWeight: 800,
+const requiredItemCard: CSSProperties = {
+ marginTop: 8,
+ display: 'inline-flex',
+ alignItems: 'center',
+ gap: 8,
+ background: 'rgba(255,255,255,0.05)',
+ border: '1px solid rgba(255,255,255,0.1)',
+ padding: '6px 12px',
+ borderRadius: '8px',
+ color: '#e2e8f0',
+ fontSize: 13,
+ fontWeight: 600,
+ textTransform: 'capitalize',
}
diff --git a/frontend/src/styles/mobile-shell.css b/frontend/src/styles/mobile-shell.css
index 3dd08fe7..59178050 100644
--- a/frontend/src/styles/mobile-shell.css
+++ b/frontend/src/styles/mobile-shell.css
@@ -266,3 +266,81 @@ html.saga-route-player .saga-tools-language-row {
background: #22c55e;
color: #052e16;
}
+
+
+/* --- PREMIUM DESIGN UTILITIES (v2.1.0) --- */
+
+:root {
+ --saga-glass-bg: rgba(15, 23, 42, 0.65);
+ --saga-glass-border: rgba(255, 255, 255, 0.08);
+ --saga-glass-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
+ --saga-accent-glow: 0 0 20px rgba(34, 197, 94, 0.4);
+ --saga-transition-smooth: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
+}
+
+.saga-glass-panel {
+ background: var(--saga-glass-bg);
+ backdrop-filter: blur(16px);
+ -webkit-backdrop-filter: blur(16px);
+ border: 1px solid var(--saga-glass-border);
+ box-shadow: var(--saga-glass-shadow);
+ border-radius: 24px;
+}
+
+.saga-premium-button {
+ transition: var(--saga-transition-smooth);
+}
+.saga-premium-button:active {
+ transform: scale(0.96);
+}
+
+.saga-pulse-glow {
+ animation: sagaPulseGlow 2.5s infinite alternate;
+}
+
+@keyframes sagaPulseGlow {
+ 0% { box-shadow: 0 0 10px rgba(34, 197, 94, 0.2); }
+ 100% { box-shadow: 0 0 25px rgba(34, 197, 94, 0.6); }
+}
+
+.saga-slide-up-enter {
+ animation: sagaSlideUp 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;
+}
+
+@keyframes sagaSlideUp {
+ 0% { opacity: 0; transform: translateY(40px) scale(0.98); }
+ 100% { opacity: 1; transform: translateY(0) scale(1); }
+}
+
+/* --- MAP SMOOTHNESS UTILITIES (v2.1.0) ---
+ Promote map layers to GPU compositing to prevent paint flicker
+ on GPS position updates and tile loads. */
+
+.leaflet-marker-icon,
+.leaflet-marker-shadow {
+ will-change: transform;
+}
+
+.leaflet-tile {
+ will-change: transform;
+}
+
+.leaflet-pane,
+.leaflet-map-pane,
+.leaflet-tile-pane {
+ will-change: transform;
+}
+
+/* Smooth tile fade-in — avoids the hard white/black blink on load */
+.leaflet-tile-container img {
+ transition: opacity 0.2s ease-out;
+}
+
+/* Prevent layout shift on the map container when GPS updates arrive */
+.saga-map-root,
+[class*="saga-map"] .leaflet-container {
+ contain: layout paint;
+ transform: translateZ(0);
+ backface-visibility: hidden;
+ -webkit-backface-visibility: hidden;
+}
diff --git a/main.py b/main.py
index 03454d85..a3d39b3d 100755
--- a/main.py
+++ b/main.py
@@ -34,6 +34,15 @@
from backend.app.security import client_ip as client_ip_security
from backend.app.security import player_session as player_session_security
+from backend.app.runtime.core_engine import (
+ normalize_stage,
+ validate_stage,
+ preserve_physical_stage_fields
+)
+from backend.app.runtime.minigames import (
+ _clean_code,
+ build_stage_minigame_runtime
+)
def _split_csv_env(name, default=""):
raw = str(os.getenv(name, default) or "").strip()
@@ -48,8 +57,10 @@ def _split_csv_env(name, default=""):
API_OPENAPI_URL = "/openapi.json" if ENABLE_API_DOCS else None
app = FastAPI(docs_url=API_DOCS_URL, redoc_url=API_REDOC_URL, openapi_url=API_OPENAPI_URL)
-from backend.app.routers import field_proofs
+from backend.app.routers import field_proofs, admin, game
app.include_router(field_proofs.router)
+app.include_router(admin.router)
+app.include_router(game.router)
app.add_middleware(
CORSMiddleware,
@@ -710,360 +721,18 @@ def project_live_profile_status(profile, raw=None, now=None):
"debug_enabled": _as_bool(raw.get("debug_enabled"), False),
}
-def _build_success_conditions(raw):
- conditions = [{"kind": "minigame_ok", "value": MINIGAME_OK_CODE}]
-
- answer = _clean_code(raw.get("answer"))
- rune = _clean_code(raw.get("rune"))
-
- if answer:
- conditions.append({"kind": "answer", "value": answer})
- if rune:
- conditions.append({"kind": "rune", "value": rune})
-
- return conditions
-# RUNTIME_CONTRACT_CLEANUP_V1: el player React debe recibir family-native minigames.
-# Avoid letting incomplete data silently fall back to outdated minigame defaults.
-SAGA_PHYSICAL_STAGE_FIELDS = (
- "physical_node_kind",
- "physical_item_kind",
- "physical_item_id",
- "physical_item_label",
- "qr_payload",
- "physical_qr",
+from backend.app.runtime.core_engine import (
+ _build_success_conditions,
+ preserve_physical_stage_fields,
+ normalize_stage,
+ stage_has_manual_fallback,
+ evaluate_entry,
+ validate_stage,
+ _positive_int,
+ read_stage_item_requirement,
)
-
-def preserve_physical_stage_fields(raw_stage, node):
- if not isinstance(raw_stage, dict) or not isinstance(node, dict):
- return node
-
- for key in SAGA_PHYSICAL_STAGE_FIELDS:
- if key in raw_stage:
- node[key] = raw_stage[key]
-
- return node
-
-
-def normalize_stage(raw):
- raw = raw or {}
-
- cfg = raw.get("config")
- if not isinstance(cfg, dict):
- cfg = {}
-
- raw_entry = raw.get("entry")
- if not isinstance(raw_entry, dict):
- raw_entry = {}
-
- raw_messages = raw.get("messages")
- if not isinstance(raw_messages, dict):
- raw_messages = {}
-
- raw_debug = raw.get("debug")
- if not isinstance(raw_debug, dict):
- raw_debug = {}
-
- entry_mode = _as_str(
- raw_entry.get("mode") or raw.get("entry_mode") or "gps"
- ).strip().lower() or "gps"
-
- require_proximity = _as_bool(
- raw_entry.get("require_proximity", raw.get("require_proximity")),
- default=(entry_mode != "free")
- )
-
- raw_minigame = raw.get("minigame")
- if not isinstance(raw_minigame, dict):
- raw_minigame = {}
-
- raw_interaction_type = _as_str(
- raw_minigame.get("type") or raw.get("type")
- ).strip().lower()
-
- interaction_type_fallback_reason = ""
- if not raw_interaction_type:
- interaction_type = "signal_hunt"
- interaction_type_fallback_reason = "missing_minigame_type"
- elif raw_interaction_type not in SUPPORTED_MINIGAME_TYPES:
- interaction_type = "signal_hunt"
- interaction_type_fallback_reason = f"unsupported_minigame_type:{raw_interaction_type}"
- else:
- interaction_type = raw_interaction_type
-
- raw_minigame_config = raw_minigame.get("config")
- if not isinstance(raw_minigame_config, dict):
- raw_minigame_config = None
-
- interaction_config = normalize_minigame_config(
- interaction_type,
- raw_minigame_config if raw_minigame_config is not None else cfg
- )
-
- item_requirement = read_stage_item_requirement(raw)
-
- node = {
- "id": raw.get("id"),
- "version": 2,
- "enabled": _as_bool(raw.get("enabled", True), True),
- "presentation": {
- "title": _as_str(raw.get("title")).strip(),
- "content": _as_str(raw.get("content")).strip(),
- },
- "location": {
- "lat": _as_float(raw.get("lat")),
- "lon": _as_float(raw.get("lon")),
- "radius_m": _as_radius(raw.get("radius", 0), 0),
- },
- "entry": {
- "mode": entry_mode,
- "require_proximity": require_proximity,
- "allow_debug_bypass": _as_bool(
- raw_entry.get("allow_debug_bypass", raw.get("allow_debug_bypass")),
- True
- ),
- "allow_manual_fallback_without_gps": _as_bool(
- raw_entry.get(
- "allow_manual_fallback_without_gps",
- raw.get("allow_manual_fallback_without_gps")
- ),
- True
- ),
- },
- "interaction": {
- "type": interaction_type,
- "config": interaction_config,
- },
- "success": {
- "mode": "any_of",
- "conditions": _build_success_conditions(raw),
- "case_sensitive": False,
- },
- "requirements": {
- "items": [item_requirement] if item_requirement else [],
- },
- "messages": {
- "locked": _as_str(
- raw_messages.get("locked") or raw.get("locked_message")
- ).strip(),
- "gps_unavailable": _as_str(
- raw_messages.get("gps_unavailable") or raw.get("gps_unavailable_message")
- ).strip(),
- "hint": _as_str(
- raw_messages.get("hint") or raw.get("hint")
- ).strip(),
- },
- "debug": {
- "force_unlock": _as_bool(
- raw_debug.get("force_unlock", raw.get("force_unlock")),
- False
- ),
- "raw_interaction_type": raw_interaction_type,
- "interaction_type_fallback_reason": interaction_type_fallback_reason,
- },
- }
-
- return preserve_physical_stage_fields(raw, node)
-
-def stage_has_manual_fallback(node):
- for condition in node["success"]["conditions"]:
- if condition.get("kind") in {"answer", "rune"} and _clean_code(condition.get("value")):
- return True
- return False
-
-def evaluate_entry(node, distance_m=None, gps_available=True, debug_enabled=False):
- entry = node.get("entry") or {}
- debug = node.get("debug") or {}
- location = node.get("location") or {}
-
- if not node.get("enabled", True):
- return {
- "can_enter": False,
- "can_submit_manual_code": False,
- "reason": "disabled",
- }
-
- if debug_enabled and (entry.get("allow_debug_bypass") or debug.get("force_unlock")):
- return {
- "can_enter": True,
- "can_submit_manual_code": True,
- "reason": "debug_bypass",
- }
-
- require_proximity = bool(entry.get("require_proximity", True))
- mode = _as_str(entry.get("mode") or "gps").strip().lower()
-
- if mode == "free" or not require_proximity:
- return {
- "can_enter": True,
- "can_submit_manual_code": True,
- "reason": "free_entry",
- }
-
- if not gps_available:
- return {
- "can_enter": False,
- "can_submit_manual_code": bool(entry.get("allow_manual_fallback_without_gps")) and stage_has_manual_fallback(node),
- "reason": "gps_unavailable",
- }
-
- if distance_m is None:
- return {
- "can_enter": False,
- "can_submit_manual_code": stage_has_manual_fallback(node),
- "reason": "distance_unknown",
- }
-
- radius = location.get("radius_m") or 0
- if distance_m <= radius:
- return {
- "can_enter": True,
- "can_submit_manual_code": True,
- "reason": "within_radius",
- }
-
- return {
- "can_enter": False,
- "can_submit_manual_code": stage_has_manual_fallback(node),
- "reason": "out_of_range",
- }
-
-def validate_stage(raw_stage, idx=None):
- node = normalize_stage(raw_stage)
- errors = []
-
- def add(field, detail):
- errors.append({
- "index": idx,
- "field": field,
- "detail": detail,
- })
-
- title = node["presentation"]["title"]
- if not title:
- add("title", "title is required")
-
- raw_minigame_for_type = raw_stage.get("minigame") if isinstance(raw_stage, dict) else {}
- if not isinstance(raw_minigame_for_type, dict):
- raw_minigame_for_type = {}
-
- raw_type_for_validation = _as_str(
- raw_minigame_for_type.get("type") or raw_stage.get("type")
- ).strip().lower()
-
- if not raw_type_for_validation:
- add("type", "minigame type is required")
- elif raw_type_for_validation not in SUPPORTED_MINIGAME_TYPES:
- add("type", f"unsupported minigame type: {raw_type_for_validation}")
-
- raw_minigame = raw_stage.get("minigame") if isinstance(raw_stage, dict) else {}
- if not isinstance(raw_minigame, dict):
- raw_minigame = {}
-
- raw_interaction_type = _as_str(
- raw_minigame.get("type") or (raw_stage.get("type") if isinstance(raw_stage, dict) else "")
- ).strip().lower()
- interaction_type = raw_interaction_type or node["interaction"]["type"]
- if interaction_type not in SUPPORTED_MINIGAME_TYPES:
- add("type", f"unsupported minigame type: {interaction_type}")
-
- raw_config = raw_minigame.get("config") if isinstance(raw_minigame.get("config"), dict) else (
- raw_stage.get("config") if isinstance(raw_stage, dict) else {}
- )
- if raw_config is not None and not isinstance(raw_config, dict):
- add("config", "config must be an object")
- raw_config = {}
-
- for field, detail in validate_minigame_config(node["interaction"]["type"], raw_config):
- add(field, detail)
-
- entry_mode = node["entry"]["mode"]
- if entry_mode not in {"gps", "free"}:
- add("entry.mode", f"unsupported entry mode: {entry_mode}")
-
- location = node["location"]
- if node["entry"]["mode"] == "gps" and node["entry"]["require_proximity"]:
- if location["lat"] is None:
- add("lat", "lat is required for gps entry")
- if location["lon"] is None:
- add("lon", "lon is required for gps entry")
- if location["radius_m"] is None or location["radius_m"] <= 0:
- add("radius", "radius must be > 0 for gps entry")
-
- conditions = node["success"]["conditions"]
- if not isinstance(conditions, list) or not conditions:
- add("success.conditions", "at least one success condition is required")
-
- for i, condition in enumerate(conditions):
- kind = _as_str(condition.get("kind")).strip()
- value = _clean_code(condition.get("value"))
-
- if kind not in {"minigame_ok", "answer", "rune"}:
- add(f"success.conditions[{i}].kind", f"unsupported success condition kind: {kind}")
- if not value:
- add(f"success.conditions[{i}].value", "success condition value is required")
-
- return errors
-
-def validate_stages(raw_stages):
- if not isinstance(raw_stages, list):
- return [{"index": None, "field": "stages", "detail": "stages payload must be a list"}]
-
- errors = []
- for idx, stage in enumerate(raw_stages):
- if not isinstance(stage, dict):
- errors.append({"index": idx, "field": "node", "detail": "each node must be an object"})
- continue
- errors.extend(validate_stage(stage, idx=idx))
- return errors
-
-def get_runtime_stages():
- raw_stages = load_stages(STAGES_DB)
- if not isinstance(raw_stages, list):
- return []
- return [normalize_stage(stage) for stage in raw_stages]
-
-def project_stage_for_player(raw_stage, include_runtime=False):
- node = raw_stage if isinstance(raw_stage, dict) and raw_stage.get("version") == 2 else normalize_stage(raw_stage)
-
- out = {
- "id": node["id"],
- "title": node["presentation"]["title"],
- "lat": node["location"]["lat"],
- "lon": node["location"]["lon"],
- "radius": node["location"]["radius_m"],
- }
-
- if include_runtime:
- out.update({
- "content": node["presentation"]["content"],
- "type": node["interaction"]["type"],
- "config": node["interaction"]["config"],
- "minigame": build_stage_minigame_runtime(node),
- "entry": node["entry"],
- "success": node["success"],
- "requirements": node.get("requirements", {"items": []}),
- "messages": node["messages"],
- })
-
- out = preserve_physical_stage_fields(node, out)
- return out
-
-def stage_accepts_code(raw_stage, code):
- node = raw_stage if isinstance(raw_stage, dict) and raw_stage.get("version") == 2 else normalize_stage(raw_stage)
- submitted = _clean_code(code)
-
- if not submitted:
- return False
-
- for condition in node["success"]["conditions"]:
- expected = _clean_code(condition.get("value"))
- if expected and submitted == expected:
- return True
-
- return False
-
APP_DIR = Path(__file__).resolve().parent
REACT_DIST_DIR = APP_DIR / "frontend" / "dist"
REACT_INDEX_FILE = REACT_DIST_DIR / "index.html"
@@ -1325,68 +994,63 @@ async def get_config():
"player_profiles": get_player_profiles(cfg)
}
-@app.get("/api/state/{user}")
-async def get_state(user: str):
- stages = load_stages(STAGES_DB)
- profile = get_player_profile(user)
- profile_id = profile.get("id") or _as_str(user).strip() or "PLAYER 1"
- lvl = get_player_progress_level(profile_id, get_player_progress_level(user, 0))
- return {"user": profile_id, "level": lvl, "finished": lvl >= len(stages)}
-
-@app.get("/api/game/{user}")
-async def get_game_payload(user: str, request: Request, offline_pack: bool = False):
- runtime_stages = get_runtime_stages()
- profile = get_player_profile(user)
- profile_id = profile.get("id") or user
- live_positions = load_live_positions()
+def validate_stages(raw_stages):
+ if not isinstance(raw_stages, list):
+ return [{"index": None, "field": "stages", "detail": "stages payload must be a list"}]
- lvl = get_player_progress_level(profile_id, get_player_progress_level(user, 0))
- finished = lvl >= len(runtime_stages)
+ errors = []
+ for idx, stage in enumerate(raw_stages):
+ if not isinstance(stage, dict):
+ errors.append({"index": idx, "field": "node", "detail": "each node must be an object"})
+ continue
+ errors.extend(validate_stage(stage, idx=idx))
+ return errors
- current_stage = None
- if not finished and 0 <= lvl < len(runtime_stages):
- current_stage = project_stage_for_player(runtime_stages[lvl], include_runtime=True)
+def get_runtime_stages():
+ raw_stages = load_stages(STAGES_DB)
+ if not isinstance(raw_stages, list):
+ return []
+ return [normalize_stage(stage) for stage in raw_stages]
- stages = [
- project_stage_for_player(stage, include_runtime=(offline_pack or (i == lvl and not finished)))
- for i, stage in enumerate(runtime_stages)
- ]
+def project_stage_for_player(raw_stage, include_runtime=False):
+ node = raw_stage if isinstance(raw_stage, dict) and raw_stage.get("version") == 2 else normalize_stage(raw_stage)
- payload = {
- "user": profile_id,
- "display_name": profile.get("display_name", profile_id),
- "session_mode": profile.get("mode", "solo"),
- "profile": profile,
- "live_status": project_live_profile_status(profile, live_positions.get(profile_id)),
- "level": lvl,
- "finished": finished,
- "stages": stages,
- "current_stage": current_stage
+ out = {
+ "id": node["id"],
+ "title": node["presentation"]["title"],
+ "lat": node["location"]["lat"],
+ "lon": node["location"]["lon"],
+ "radius": node["location"]["radius_m"],
}
- response = JSONResponse(payload)
- if resolve_known_player_profile(profile_id):
- set_player_session_cookie(response, request, profile_id)
- return response
-@app.get("/api/team/{user}")
-async def get_team_payload(user: str):
- cfg = load_config()
- current_profile = get_player_profile(user, cfg)
- current_profile_id = current_profile.get("id") or _as_str(user).strip() or "PLAYER 1"
- live_positions = load_live_positions()
- now = int(time.time())
+ if include_runtime:
+ out.update({
+ "content": node["presentation"]["content"],
+ "type": node["interaction"]["type"],
+ "config": node["interaction"]["config"],
+ "minigame": build_stage_minigame_runtime(node),
+ "entry": node["entry"],
+ "success": node["success"],
+ "requirements": node.get("requirements", {"items": []}),
+ "messages": node["messages"],
+ })
- profiles = []
- for profile in get_player_profiles(cfg):
- projected = project_live_profile_status(profile, live_positions.get(profile.get("id")), now)
- projected["is_self"] = _as_str(profile.get("id")).strip() == _as_str(current_profile_id).strip()
- profiles.append(projected)
+ out = preserve_physical_stage_fields(node, out)
+ return out
- return {
- "status": "ok",
- "user": current_profile_id,
- "profiles": profiles
- }
+def stage_accepts_code(raw_stage, code):
+ node = raw_stage if isinstance(raw_stage, dict) and raw_stage.get("version") == 2 else normalize_stage(raw_stage)
+ submitted = _clean_code(code)
+
+ if not submitted:
+ return False
+
+ for condition in node["success"]["conditions"]:
+ expected = _clean_code(condition.get("value"))
+ if expected and submitted == expected:
+ return True
+
+ return False
PLAYER_EVENT_TYPES = {
@@ -1566,241 +1230,6 @@ def apply_synced_player_event(normalized_event, user, profile):
return append_event(EVENT_LOG_DB, event)
-@app.post("/api/events/sync")
-async def sync_player_events(request: Request):
- data = await request.json()
- user = _as_str(data.get("user")).strip()
- require_player_session(request, user)
- enforce_player_rate_limit("events_sync", request, user, EVENT_SYNC_RATE_LIMIT_MAX)
-
- profile = resolve_known_player_profile(user)
- if not profile:
- raise HTTPException(status_code=403, detail="unknown player")
-
- events = data.get("events")
- if not isinstance(events, list):
- raise HTTPException(status_code=400, detail="events must be a list")
-
- if len(events) > 100:
- raise HTTPException(status_code=400, detail="too many events")
-
- stored = []
- seen_client_events = {}
- for raw_event in events:
- normalized = normalize_player_event(raw_event, user, profile)
- client_event_id = _as_str(normalized.get("client_event_id")).strip()
-
- if client_event_id:
- payload = normalized.get("payload") if isinstance(normalized.get("payload"), dict) else {}
- normalized["payload"] = {
- **payload,
- "client_event_id": client_event_id,
- }
-
- existing = seen_client_events.get(client_event_id) or find_existing_player_client_event(user, client_event_id)
- if existing:
- duplicate = {
- **existing,
- "status": existing.get("status") or "synced",
- "duplicate": True,
- }
- stored.append(duplicate)
- seen_client_events[client_event_id] = duplicate
- continue
-
- stored_event = apply_synced_player_event(normalized, user, profile)
- stored.append(stored_event)
-
- if client_event_id:
- seen_client_events[client_event_id] = stored_event
-
- append_event(
- EVENT_LOG_DB,
- {
- "type": "offline_sync_received",
- "status": "synced",
- "source": "server",
- "user": user,
- "team_id": _as_str(profile.get("id")),
- "payload": {
- "event_count": len(stored),
- },
- },
- )
-
- return {
- "status": "ok",
- "accepted": len(stored),
- "events": [
- {
- "id": event.get("id"),
- "type": event.get("type"),
- "status": event.get("status"),
- "client_event_id": event.get("client_event_id") or (
- event.get("payload", {}).get("client_event_id")
- if isinstance(event.get("payload"), dict)
- else None
- ),
- "node_id": event.get("node_id"),
- "error": event.get("error"),
- "duplicate": bool(event.get("duplicate")),
- }
- for event in stored
- ],
- }
-
-@app.post("/api/admin/events")
-async def admin_events(request: Request):
- data = await request.json()
-
- if not admin_request_authorized(request, data):
- raise HTTPException(status_code=403, detail="forbidden")
-
- limit = data.get("limit", 100)
- try:
- limit = max(1, min(500, int(limit)))
- except (TypeError, ValueError):
- limit = 100
-
- status = sanitize_event_text(data.get("status"), 80) or None
- user = sanitize_event_text(data.get("user"), 120) or None
- event_type = sanitize_event_text(data.get("type"), 80) or None
-
- return {
- "status": "ok",
- "events": list_events(
- EVENT_LOG_DB,
- status=status,
- user=user,
- event_type=event_type,
- limit=limit,
- ),
- }
-
-@app.post("/api/admin/events/mark")
-async def admin_mark_event(request: Request):
- data = await request.json()
-
- if not admin_request_authorized(request, data):
- raise HTTPException(status_code=403, detail="forbidden")
-
- event_id = sanitize_event_text(data.get("event_id"), 120)
- next_status = sanitize_event_text(data.get("status"), 40)
-
- if not event_id:
- raise HTTPException(status_code=400, detail="event_id is required")
-
- updated = mark_event_status(
- EVENT_LOG_DB,
- event_id,
- next_status,
- error=sanitize_event_text(data.get("error"), 300) or None,
- )
-
- if not updated:
- raise HTTPException(status_code=404, detail="event not found")
-
- return {
- "status": "ok",
- "event": updated,
- }
-
-
-@app.post("/api/heartbeat")
-async def heartbeat(request: Request):
- data = await request.json()
-
- user = _as_str(data.get("user")).strip()
- if not user:
- return JSONResponse(
- status_code=400,
- content={"status": "error", "detail": "user required"}
- )
-
- cfg = load_config()
- profile = resolve_known_player_profile(user, cfg)
- if not profile:
- return JSONResponse(
- status_code=404,
- content={"status": "error", "detail": "unknown profile"}
- )
-
- profile_id = profile.get("id") or user
-
- now = time.time()
- ip = get_heartbeat_client_ip(request)
- rate_key = f"{ip}:{profile_id}"
-
- prune_heartbeat_rate_state(now)
- last_seen_for_key = float(HEARTBEAT_LAST_SEEN_BY_KEY.get(rate_key) or 0)
- if last_seen_for_key and (now - last_seen_for_key) < HEARTBEAT_MIN_INTERVAL_SECONDS:
- retry_after = max(1, int(HEARTBEAT_MIN_INTERVAL_SECONDS - (now - last_seen_for_key)))
- return JSONResponse(
- status_code=429,
- headers={"Retry-After": str(retry_after)},
- content={"status": "error", "detail": f"heartbeat too frequent; retry in {retry_after}s"}
- )
-
- lat_present = data.get("lat") is not None
- lon_present = data.get("lon") is not None
-
- if lat_present != lon_present:
- return JSONResponse(
- status_code=400,
- content={"status": "error", "detail": "lat and lon must be sent together"}
- )
-
- lat = _as_float(data.get("lat"))
- lon = _as_float(data.get("lon"))
-
- if lat_present and (lat is None or lon is None):
- return JSONResponse(
- status_code=400,
- content={"status": "error", "detail": "invalid coordinates"}
- )
-
- if lat is not None and not (-90 <= lat <= 90):
- return JSONResponse(
- status_code=400,
- content={"status": "error", "detail": "lat out of range"}
- )
-
- if lon is not None and not (-180 <= lon <= 180):
- return JSONResponse(
- status_code=400,
- content={"status": "error", "detail": "lon out of range"}
- )
-
- current = get_live_position(profile_id)
- if not isinstance(current, dict):
- current = {}
-
- if lat is not None and lon is not None:
- current["lat"] = lat
- current["lon"] = lon
-
- current["last_seen"] = int(now)
- current["gps_status"] = normalize_heartbeat_gps_status(
- data.get("gps_status") or current.get("gps_status") or "unknown"
- )
- current["source"] = normalize_heartbeat_source(
- data.get("source") or current.get("source") or "player"
- )
-
- # Public heartbeat must not be able to toggle debug state remotely.
- current["debug_enabled"] = False
-
- upsert_live_position_for_user(profile_id, current)
- HEARTBEAT_LAST_SEEN_BY_KEY[rate_key] = now
-
- return {
- "status": "ok",
- "user": profile_id,
- "live_status": project_live_profile_status(profile, current)
- }
-
-
-
def _safe_runtime_json_file(global_names, fallback):
for name in global_names:
path = globals().get(name)
@@ -2170,63 +1599,6 @@ async def save_config_endpoint(request: Request):
-def _positive_int(value, default=1):
- try:
- parsed = int(value)
- except (TypeError, ValueError):
- parsed = default
- return max(1, parsed)
-
-
-def read_stage_item_requirement(raw_stage):
- node = raw_stage if isinstance(raw_stage, dict) else {}
- config = {}
-
- interaction = node.get("interaction")
- if isinstance(interaction, dict) and isinstance(interaction.get("config"), dict):
- config.update(interaction.get("config") or {})
-
- if isinstance(node.get("config"), dict):
- config.update(node.get("config") or {})
-
- requirements = node.get("requirements")
- if isinstance(requirements, dict):
- items = requirements.get("items")
- if isinstance(items, list) and items and isinstance(items[0], dict):
- config.update(items[0])
-
- item_id = _as_str(
- config.get("required_item_id")
- or config.get("item_id")
- or config.get("id")
- ).strip()
-
- if not item_id:
- return None
-
- label = _as_str(
- config.get("required_item_label")
- or config.get("label")
- or item_id
- ).strip() or item_id
-
- return {
- "item_id": item_id,
- "label": label,
- "quantity": _positive_int(
- config.get("required_item_quantity")
- or config.get("quantity")
- or 1,
- 1,
- ),
- "consume": _as_bool(
- config.get("required_item_consume")
- or config.get("consume"),
- False,
- ),
- }
-
-
def _event_payload(event):
payload = event.get("payload") if isinstance(event, dict) else {}
return payload if isinstance(payload, dict) else {}
@@ -2327,229 +1699,6 @@ def append_inventory_item_used_event(user, profile_id, current_node, requirement
)
-@app.post("/api/advance")
-async def advance(request: Request):
- data = await request.json()
- user = data.get("user")
- code = (data.get("code") or "").strip().upper()
- require_player_session(request, user)
- enforce_player_rate_limit("advance", request, user, ADVANCE_RATE_LIMIT_MAX)
-
- profile = get_player_profile(user)
- profile_id = profile.get("id") or _as_str(user).strip() or "PLAYER 1"
-
- stages = get_runtime_stages()
- lvl = get_player_progress_level(profile_id, get_player_progress_level(user, 0))
-
- if lvl < len(stages):
- current_node = stages[lvl]
-
- if stage_accepts_code(current_node, code):
- requirement_status = evaluate_stage_item_requirement(current_node, profile_id)
-
- if not requirement_status["ok"]:
- return {
- "status": "fail",
- "user": profile_id,
- "reason": "missing_required_item",
- "requirement": requirement_status,
- }
-
- if requirement_status["required"] and requirement_status["consume"]:
- append_inventory_item_used_event(user, profile_id, current_node, requirement_status)
-
- set_player_progress_level(profile_id, lvl + 1)
- return {
- "status": "ok",
- "user": profile_id,
- "requirement": requirement_status,
- }
-
- return {"status": "fail", "user": profile_id}
-
-@app.post("/api/reset")
-async def reset(request: Request):
- data = await request.json()
-
- # /api/reset mutates player progress. Keep it admin-only.
- if not admin_request_authorized(request, data):
- raise HTTPException(status_code=403, detail="forbidden")
-
- user = _as_str(data.get("user")).strip()
- if not user:
- raise HTTPException(status_code=400, detail="user is required")
-
- reset_player_level(GAME_DB, user)
- return {"status": "ok"}
-
-def _clamp_game_level(value, max_level):
- try:
- value = int(value)
- except Exception:
- value = 0
- if value < 0:
- return 0
- if value > max_level:
- return max_level
- return value
-
-@app.post("/api/admin/profile-action")
-async def admin_profile_action(request: Request):
- data = await request.json()
-
- if not admin_request_authorized(request, data):
- return JSONResponse(
- status_code=403,
- content={"status": "error", "detail": "bad password"}
- )
-
- if admin_password_change_required():
- return JSONResponse(
- status_code=403,
- content={"status": "error", "detail": "password change required"}
- )
-
- profile_id = _as_str(data.get("profile_id")).strip()
- action = _as_str(data.get("action")).strip().lower()
-
- allowed_actions = {
- "reset_profile",
- "level_prev",
- "level_next",
- "mark_finished",
- }
-
- if action not in allowed_actions:
- return JSONResponse(
- status_code=400,
- content={"status": "error", "detail": "invalid action"}
- )
-
- cfg = load_config()
- profiles = {
- _as_str((p or {}).get("id")).strip(): (p or {})
- for p in get_player_profiles(cfg)
- }
-
- if not profile_id or profile_id not in profiles:
- return JSONResponse(
- status_code=404,
- content={"status": "error", "detail": "unknown profile"}
- )
-
- runtime_stages = get_runtime_stages()
- max_level = len(runtime_stages)
-
- previous_level = _clamp_game_level(get_player_progress_level(profile_id, 0), max_level)
-
- if action == "reset_profile":
- new_level = 0
- elif action == "level_prev":
- new_level = max(0, previous_level - 1)
- elif action == "level_next":
- new_level = min(max_level, previous_level + 1)
- else: # mark_finished
- new_level = max_level
-
- set_player_progress_level(profile_id, new_level)
-
- return {
- "status": "ok",
- "profile_id": profile_id,
- "action": action,
- "previous_level": previous_level,
- "level": new_level,
- "finished": new_level >= max_level,
- "total_stages": max_level,
- }
-
-
-@app.post("/api/admin/save")
-async def save_stages_endpoint(request: Request):
- data = await request.json()
- if not admin_request_authorized(request, data):
- return JSONResponse(status_code=403, content={"status": "error"})
- if admin_password_change_required():
- return JSONResponse(status_code=403, content={"status": "error", "detail": "password change required"})
-
- stages = data.get("stages")
- errors = validate_stages(stages)
- if errors:
- return JSONResponse(
- status_code=400,
- content={"status": "error", "detail": "invalid stages", "errors": errors}
- )
-
- save_stages(STAGES_DB, stages)
- return {"status": "ok"}
-
-@app.post("/api/admin/login")
-async def admin_login(request: Request):
- data = await request.json()
- now = time.time()
- ip = get_client_ip(request)
-
- remaining = get_admin_lock_remaining_seconds(ip, now)
- if remaining > 0:
- raise HTTPException(
- status_code=429,
- detail=f"too many failed attempts; retry in {remaining}s"
- )
-
- if verify_admin_password(data.get("password")):
- clear_admin_login_state(ip)
- expires_at = int(time.time()) + ADMIN_SESSION_TTL_SECONDS
- response = JSONResponse(
- {
- "status": "ok",
- "must_change": admin_password_change_required(),
- "session_expires_at": expires_at,
- }
- )
- set_admin_session_cookie(response, request, create_admin_session())
- return response
-
- register_admin_login_failure(ip, now)
- raise HTTPException(status_code=401, detail="invalid admin password")
-
-@app.post("/api/admin/logout")
-async def admin_logout(request: Request):
- token = request.cookies.get(ADMIN_SESSION_COOKIE)
- if token:
- ADMIN_SESSIONS.pop(token, None)
- admin_auth_security.clear_persistent_admin_session(ADMIN_SESSIONS_DB, token)
-
- response = JSONResponse({"status": "ok"})
- clear_admin_session_cookie(response, request)
- clear_player_session_cookie(response, request)
- return response
-
-@app.post("/api/admin/change-password")
-async def admin_change_password(request: Request):
- data = await request.json()
- if not admin_request_authorized(request, data):
- raise HTTPException(status_code=403, detail="forbidden")
-
- current_password = (data.get("password") or "").strip()
- new_password = (data.get("new_password") or "").strip()
- confirm_password = (data.get("confirm_password") or "").strip()
-
- if not verify_admin_password(current_password):
- return JSONResponse(status_code=403, content={"status": "error", "detail": "bad password"})
-
- if not new_password:
- return JSONResponse(status_code=400, content={"status": "error", "detail": "new password required"})
-
- if new_password != confirm_password:
- return JSONResponse(status_code=400, content={"status": "error", "detail": "passwords do not match"})
-
- if is_weak_admin_password(new_password):
- return JSONResponse(status_code=400, content={"status": "error", "detail": "choose a stronger password (minimum 10 chars, avoid temporary/default values)"})
-
- set_admin_password(new_password, must_change=False, source="web_change")
- clear_admin_sessions()
- return {"status": "ok"}
-
@app.api_route("/sw.js", methods=["GET", "HEAD"])
def player_service_worker():
sw_file = REACT_DIST_DIR / "sw.js"
From 663170d96975a7f1c9c65f16eb28b02888618509 Mon Sep 17 00:00:00 2001
From: odegaard12 <153871346+odegaard12@users.noreply.github.com>
Date: Tue, 7 Jul 2026 09:51:46 +0200
Subject: [PATCH 2/3] chore: bump version to 2.1.0
---
CHANGELOG.md | 20 +++++++++++---------
frontend/package-lock.json | 4 ++--
frontend/package.json | 2 +-
3 files changed, 14 insertions(+), 12 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f872d383..307028d2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,21 +1,23 @@
-# 2.1.0
+# Changelog
+
+All notable changes to SAGA Engine are documented in this file.
+
+---
-### Diseño Premium (Player)
-- Añadidos efectos de cristalizado (glassmorphism) en los controles y menús del jugador usando ackdrop-filter: blur().
+## [2.1.0] — 2026-07-07
+
+### 🎨 Diseño Premium (Player)
+- Añadidos efectos de cristalizado (glassmorphism) en los controles y menús del jugador usando backdrop-filter: blur().
- Incorporadas nuevas micro-animaciones (escala al tocar, deslizamiento suave) en mobile-shell.css.
- Actualizada la paleta visual a tonos dark blue/purple más ricos (Premium Design).
-### Refactorización y Mantenimiento (Admin)
+### 🛠️ Refactorización y Mantenimiento (Admin)
- Refactorización mayor del editor guiado (GuidedNodeEditorFlow.tsx). Extraídas más de 700 líneas de lógica pura, tipos y constantes a un nuevo archivo guidedEditorUtils.ts para mejorar la mantenibilidad y reducir la deuda técnica.
- Solucionado el bug visual en el paso 5 de 'Revisar' que mostraba erróneamente un tipo de juego cuando se configuraba un Coleccionable de Mapa.
-### PWA y Offline
+### 📱 PWA y Offline
- Confirmada la disponibilidad de la PWA para instalaciones offline. *Nota: Requiere acceder mediante HTTPS (por ejemplo a través de un túnel Cloudflare) para que el navegador ofrezca la opción de instalación e inicie el Service Worker.*
-# Changelog
-
-All notable changes to SAGA Engine are documented in this file.
-
---
## [2.0.1] — 2026-07-02
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index aba29862..8b36e9c6 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "saga-engine-frontend",
- "version": "2.0.1",
+ "version": "2.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "saga-engine-frontend",
- "version": "2.0.1",
+ "version": "2.1.0",
"dependencies": {
"@tanstack/react-query": "^5.101.2",
"jsqr": "^1.4.0",
diff --git a/frontend/package.json b/frontend/package.json
index 10e88810..e5d4998e 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,7 +1,7 @@
{
"name": "saga-engine-frontend",
"private": true,
- "version": "2.0.1",
+ "version": "2.1.0",
"type": "module",
"scripts": {
"dev": "vite",
From 0974c67ba39741f4980edb7e00a78d2ac539044a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 7 Jul 2026 07:55:08 +0000
Subject: [PATCH 3/3] chore(deps-dev): bump the frontend-tooling group across 1
directory with 3 updates
Bumps the frontend-tooling group with 2 updates in the /frontend directory: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [eslint](https://github.com/eslint/eslint).
Updates `@typescript-eslint/eslint-plugin` from 8.62.1 to 8.63.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.63.0/packages/eslint-plugin)
Updates `@typescript-eslint/parser` from 8.62.1 to 8.63.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.63.0/packages/parser)
Updates `eslint` from 8.57.1 to 10.6.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v8.57.1...v10.6.0)
---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
dependency-version: 8.63.0
dependency-type: direct:development
update-type: version-update:semver-minor
dependency-group: frontend-tooling
- dependency-name: "@typescript-eslint/parser"
dependency-version: 8.63.0
dependency-type: direct:development
update-type: version-update:semver-minor
dependency-group: frontend-tooling
- dependency-name: eslint
dependency-version: 10.6.0
dependency-type: direct:development
update-type: version-update:semver-major
dependency-group: frontend-tooling
...
Signed-off-by: dependabot[bot]
---
frontend/package-lock.json | 942 +++++++++----------------------------
frontend/package.json | 4 +-
2 files changed, 230 insertions(+), 716 deletions(-)
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 8b36e9c6..a51d3209 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -23,10 +23,10 @@
"@types/leaflet": "^1.9.21",
"@types/react": "^18.3.0",
"@types/react-dom": "^18.3.0",
- "@typescript-eslint/eslint-plugin": "^8.62.1",
+ "@typescript-eslint/eslint-plugin": "^8.63.0",
"@typescript-eslint/parser": "^8.62.0",
"@vitejs/plugin-react": "^6.0.3",
- "eslint": "^8.57.1",
+ "eslint": "^10.6.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.1.1",
@@ -340,126 +340,107 @@
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
- "node_modules/@eslint/eslintrc": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
- "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "node_modules/@eslint/config-array": {
+ "version": "0.23.5",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz",
+ "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==",
"dev": true,
- "license": "MIT",
+ "license": "Apache-2.0",
"dependencies": {
- "ajv": "^6.12.4",
- "debug": "^4.3.2",
- "espree": "^9.6.0",
- "globals": "^13.19.0",
- "ignore": "^5.2.0",
- "import-fresh": "^3.2.1",
- "js-yaml": "^4.1.0",
- "minimatch": "^3.1.2",
- "strip-json-comments": "^3.1.1"
+ "@eslint/object-schema": "^3.0.5",
+ "debug": "^4.3.1",
+ "minimatch": "^10.2.4"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
- "node_modules/@eslint/eslintrc/node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
- "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz",
+ "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==",
"dev": true,
- "license": "MIT",
+ "license": "Apache-2.0",
"dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/ignore": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
- "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
- "dev": true,
- "license": "MIT",
+ "@eslint/core": "^1.2.1"
+ },
"engines": {
- "node": ">= 4"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
- "node_modules/@eslint/eslintrc/node_modules/minimatch": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
- "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "node_modules/@eslint/core": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz",
+ "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==",
"dev": true,
- "license": "ISC",
+ "license": "Apache-2.0",
"dependencies": {
- "brace-expansion": "^1.1.7"
+ "@types/json-schema": "^7.0.15"
},
"engines": {
- "node": "*"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
- "node_modules/@eslint/js": {
- "version": "8.57.1",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
- "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
+ "node_modules/@eslint/object-schema": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz",
+ "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==",
"dev": true,
- "license": "MIT",
+ "license": "Apache-2.0",
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
- "node_modules/@humanwhocodes/config-array": {
- "version": "0.13.0",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
- "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==",
- "deprecated": "Use @eslint/config-array instead",
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz",
+ "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@humanwhocodes/object-schema": "^2.0.3",
- "debug": "^4.3.1",
- "minimatch": "^3.0.5"
+ "@eslint/core": "^1.2.1",
+ "levn": "^0.4.1"
},
"engines": {
- "node": ">=10.10.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
- "node_modules/@humanwhocodes/config-array/node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
- "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+ "node_modules/@humanfs/core": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
+ "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
"dev": true,
- "license": "MIT",
+ "license": "Apache-2.0",
"dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
+ "@humanfs/types": "^0.15.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
}
},
- "node_modules/@humanwhocodes/config-array/node_modules/minimatch": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
- "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "node_modules/@humanfs/node": {
+ "version": "0.16.8",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
+ "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
"dev": true,
- "license": "ISC",
+ "license": "Apache-2.0",
"dependencies": {
- "brace-expansion": "^1.1.7"
+ "@humanfs/core": "^0.19.2",
+ "@humanfs/types": "^0.15.0",
+ "@humanwhocodes/retry": "^0.4.0"
},
"engines": {
- "node": "*"
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/types": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
+ "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
}
},
"node_modules/@humanwhocodes/module-importer": {
@@ -475,13 +456,19 @@
"url": "https://github.com/sponsors/nzakas"
}
},
- "node_modules/@humanwhocodes/object-schema": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
- "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
- "deprecated": "Use @eslint/object-schema instead",
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
"dev": true,
- "license": "BSD-3-Clause"
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
@@ -547,44 +534,6 @@
"@emnapi/runtime": "^1.7.1"
}
},
- "node_modules/@nodelib/fs.scandir": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
- "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@nodelib/fs.stat": "2.0.5",
- "run-parallel": "^1.1.9"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.stat": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
- "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.walk": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
- "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@nodelib/fs.scandir": "2.1.5",
- "fastq": "^1.6.0"
- },
- "engines": {
- "node": ">= 8"
- }
- },
"node_modules/@oxc-project/types": {
"version": "0.138.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.138.0.tgz",
@@ -893,12 +842,33 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@types/esrecurse": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
+ "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/geojson": {
"version": "7946.0.16",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
"dev": true
},
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/leaflet": {
"version": "1.9.21",
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.21.tgz",
@@ -934,17 +904,17 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.62.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.62.1.tgz",
- "integrity": "sha512-4EQM77WgVNxj7OkL/5b/D/xZsw00G577+UriYTC7JF5opcF3T2AuoeY7ueLaZgSVjSgCS6yOAJB5bRGLPSJUzA==",
+ "version": "8.63.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.63.0.tgz",
+ "integrity": "sha512-rvwSgqT+DHpWdzfSzPatRLm02a0GlESt++9iy3hLCDY4BgkaLcl8LBi9Yh7XGFBpwcBE/K3024QuXWTpbz4FfQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
- "@typescript-eslint/scope-manager": "8.62.1",
- "@typescript-eslint/type-utils": "8.62.1",
- "@typescript-eslint/utils": "8.62.1",
- "@typescript-eslint/visitor-keys": "8.62.1",
+ "@typescript-eslint/scope-manager": "8.63.0",
+ "@typescript-eslint/type-utils": "8.63.0",
+ "@typescript-eslint/utils": "8.63.0",
+ "@typescript-eslint/visitor-keys": "8.63.0",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.5.0"
@@ -957,22 +927,22 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "@typescript-eslint/parser": "^8.62.1",
+ "@typescript-eslint/parser": "^8.63.0",
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "8.62.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.62.1.tgz",
- "integrity": "sha512-sPhE4iHuJDSvoAiec+Ro8JyXw8f0ql13HFR82P99nCm9GwTEKG0KYLvDe6REk8BCXuit6vJAv/Yxg5ABaNS2rA==",
+ "version": "8.63.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.63.0.tgz",
+ "integrity": "sha512-gwh4gvvlaVDKKxyfxMG+Gnu1u9X0OQBwyGLkbwB65dIzBKnxeRiJlNFqlI3zwVhNXJIs6qV7mlFCn/BIajlVig==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.62.1",
- "@typescript-eslint/types": "8.62.1",
- "@typescript-eslint/typescript-estree": "8.62.1",
- "@typescript-eslint/visitor-keys": "8.62.1",
+ "@typescript-eslint/scope-manager": "8.63.0",
+ "@typescript-eslint/types": "8.63.0",
+ "@typescript-eslint/typescript-estree": "8.63.0",
+ "@typescript-eslint/visitor-keys": "8.63.0",
"debug": "^4.4.3"
},
"engines": {
@@ -988,14 +958,14 @@
}
},
"node_modules/@typescript-eslint/project-service": {
- "version": "8.62.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.62.1.tgz",
- "integrity": "sha512-yQ3RgY5RkSBpsNS1Bx/JQEcA24FOSdfGktoyprAr5u18390UQdtVcfnEv4nIrIshNnavlVyZBKxQwT1fIAE6cg==",
+ "version": "8.63.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.63.0.tgz",
+ "integrity": "sha512-e5dh0/UI0ok53AlZ5wRkXCB32z/f2jUZqPR/ygAw5WYaSw8j9EoJWlS7wQjr/dmOaqWjnPIn2m+HhVPCMWGZVQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.62.1",
- "@typescript-eslint/types": "^8.62.1",
+ "@typescript-eslint/tsconfig-utils": "^8.63.0",
+ "@typescript-eslint/types": "^8.63.0",
"debug": "^4.4.3"
},
"engines": {
@@ -1010,14 +980,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "8.62.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.62.1.tgz",
- "integrity": "sha512-r4d249KbQ1SFdpeStvob8Ih6aPPIzfqllPVOtvhve6ZcpuVcYo5/7zUWckKpHE7StASX4kTKZTLf0WQm/wPkcg==",
+ "version": "8.63.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.63.0.tgz",
+ "integrity": "sha512-uUyfMWCnDSN8bCpcrY8nGP2BLkQ9Xn0GsipcONcpIDWhwhO4ZSyHvyS14U3X75mzxWxL3I2UZIrenTzdzcJO8A==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.62.1",
- "@typescript-eslint/visitor-keys": "8.62.1"
+ "@typescript-eslint/types": "8.63.0",
+ "@typescript-eslint/visitor-keys": "8.63.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1028,9 +998,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.62.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.1.tgz",
- "integrity": "sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g==",
+ "version": "8.63.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.63.0.tgz",
+ "integrity": "sha512-sUAbkulqBAsncKnbRP3+7CtQFRKicexnj7ZwNC6ddCR7EmrXvjvdCYMJbUIqMd6lwoEriZjwLo08aS5tSjVMHg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1045,15 +1015,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.62.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.62.1.tgz",
- "integrity": "sha512-aXM5xlqXiTxPibXB93cLAURfT3rlizf7uMXISCXy66Isr/9hISJx3yDsKl0L7lKa51b8JpFuNKby0/O0pEm9jg==",
+ "version": "8.63.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.63.0.tgz",
+ "integrity": "sha512-Nzzh/OGxVCOjObjaj1CQF2RUasyYy2Jfuh+zZ3PjLzG2fYRriAiZLib9UKtO+CpQAS3YHiAS+ckZDclwqI1TPA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.62.1",
- "@typescript-eslint/typescript-estree": "8.62.1",
- "@typescript-eslint/utils": "8.62.1",
+ "@typescript-eslint/types": "8.63.0",
+ "@typescript-eslint/typescript-estree": "8.63.0",
+ "@typescript-eslint/utils": "8.63.0",
"debug": "^4.4.3",
"ts-api-utils": "^2.5.0"
},
@@ -1070,9 +1040,9 @@
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.62.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.62.1.tgz",
- "integrity": "sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q==",
+ "version": "8.63.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.63.0.tgz",
+ "integrity": "sha512-xyLtl9DUBBFrcJS4x2pIqGLH68/tC2uOa4Z7pUteW09D3bXnnXUom4dyPikzWgB7llmIc1zoeI3aoUdC4rPK/Q==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1084,16 +1054,16 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.62.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.62.1.tgz",
- "integrity": "sha512-xMcW9oP9u7fAMXYs9A65CVmtLQe2r//oXINHfi8HV+oiqhih17sbLdhXr4540YWlgpDKQdY854OL5ZrdCiQsAA==",
+ "version": "8.63.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.63.0.tgz",
+ "integrity": "sha512-ygBkU+B7ex5UI/gKhaqexWev79uISfIv7XQCRNYO/jmD8rGLPyWLAb3KMRT6nd8Gt9bmUBi9+iX6tBdYfOY81Q==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/project-service": "8.62.1",
- "@typescript-eslint/tsconfig-utils": "8.62.1",
- "@typescript-eslint/types": "8.62.1",
- "@typescript-eslint/visitor-keys": "8.62.1",
+ "@typescript-eslint/project-service": "8.63.0",
+ "@typescript-eslint/tsconfig-utils": "8.63.0",
+ "@typescript-eslint/types": "8.63.0",
+ "@typescript-eslint/visitor-keys": "8.63.0",
"debug": "^4.4.3",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
@@ -1112,16 +1082,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "8.62.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.62.1.tgz",
- "integrity": "sha512-sHtbPfuKNZCG+ih8SyjjucqRntSVmp8XgL5u6o9mAhiSn8ds5o/M/XdM0abweme2Tln3szOstOrZ9OXitvPh0g==",
+ "version": "8.63.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.63.0.tgz",
+ "integrity": "sha512-fUKaeAvrTuQg/Tgt3nliAUSZHJM6DlCcfyEmxCvlX8kieWSStBX+5O5Fnidtc3i2JrH+9c/GL4RY2iasd/GPTA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
- "@typescript-eslint/scope-manager": "8.62.1",
- "@typescript-eslint/types": "8.62.1",
- "@typescript-eslint/typescript-estree": "8.62.1"
+ "@typescript-eslint/scope-manager": "8.63.0",
+ "@typescript-eslint/types": "8.63.0",
+ "@typescript-eslint/typescript-estree": "8.63.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1136,13 +1106,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.62.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.62.1.tgz",
- "integrity": "sha512-4g3BLxfdTMy8iZG0MaBkadnlRrCJ74cQiFbyEVMrkwIoqdyaXXQM22cotDvrl4x28wgIZ9rEJRoM+mmhSJpJ1g==",
+ "version": "8.63.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.63.0.tgz",
+ "integrity": "sha512-UexrHGnGTpbuQHct2ExOc2ZcFbGUS9FOesCxxqdBGcpI1BxYu/LZ6U8Aq6/72XtF/qRBk9nhuGHFJIXXMhPMdw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.62.1",
+ "@typescript-eslint/types": "8.63.0",
"eslint-visitor-keys": "^5.0.0"
},
"engines": {
@@ -1166,13 +1136,6 @@
"url": "https://opencollective.com/eslint"
}
},
- "node_modules/@ungap/structured-clone": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.2.tgz",
- "integrity": "sha512-5jsZFwgR5rTdKwidH9Qmat75RKwqfpKlWWB1frDkljN127mwqBu8K0PYo7/hFpF03IEJpfVPpCQDY/eDx3iHvA==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/@vitejs/plugin-react": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.3.tgz",
@@ -1266,29 +1229,6 @@
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
- "node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true,
- "license": "Python-2.0"
- },
"node_modules/array-buffer-byte-length": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
@@ -1559,16 +1499,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/callsites": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
- "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/caniuse-lite": {
"version": "1.0.30001799",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz",
@@ -1589,23 +1519,6 @@
}
]
},
- "node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
"node_modules/cli-cursor": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
@@ -1637,26 +1550,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -2073,60 +1966,62 @@
}
},
"node_modules/eslint": {
- "version": "8.57.1",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
- "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
- "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.6.0.tgz",
+ "integrity": "sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==",
"dev": true,
"license": "MIT",
+ "workspaces": [
+ "packages/*"
+ ],
"dependencies": {
- "@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.6.1",
- "@eslint/eslintrc": "^2.1.4",
- "@eslint/js": "8.57.1",
- "@humanwhocodes/config-array": "^0.13.0",
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.2",
+ "@eslint/config-array": "^0.23.5",
+ "@eslint/config-helpers": "^0.6.0",
+ "@eslint/core": "^1.2.1",
+ "@eslint/plugin-kit": "^0.7.2",
+ "@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
- "@nodelib/fs.walk": "^1.2.8",
- "@ungap/structured-clone": "^1.2.0",
- "ajv": "^6.12.4",
- "chalk": "^4.0.0",
- "cross-spawn": "^7.0.2",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "ajv": "^6.14.0",
+ "cross-spawn": "^7.0.6",
"debug": "^4.3.2",
- "doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^7.2.2",
- "eslint-visitor-keys": "^3.4.3",
- "espree": "^9.6.1",
- "esquery": "^1.4.2",
+ "eslint-scope": "^9.1.2",
+ "eslint-visitor-keys": "^5.0.1",
+ "espree": "^11.2.0",
+ "esquery": "^1.7.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
- "file-entry-cache": "^6.0.1",
+ "file-entry-cache": "^8.0.0",
"find-up": "^5.0.0",
"glob-parent": "^6.0.2",
- "globals": "^13.19.0",
- "graphemer": "^1.4.0",
"ignore": "^5.2.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
- "is-path-inside": "^3.0.3",
- "js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
- "levn": "^0.4.1",
- "lodash.merge": "^4.6.2",
- "minimatch": "^3.1.2",
+ "minimatch": "^10.2.4",
"natural-compare": "^1.4.0",
- "optionator": "^0.9.3",
- "strip-ansi": "^6.0.1",
- "text-table": "^0.2.0"
+ "optionator": "^0.9.3"
},
"bin": {
"eslint": "bin/eslint.js"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
- "url": "https://opencollective.com/eslint"
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
}
},
"node_modules/eslint-config-prettier": {
@@ -2233,17 +2128,19 @@
}
},
"node_modules/eslint-scope": {
- "version": "7.2.2",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
- "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "version": "9.1.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz",
+ "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
+ "@types/esrecurse": "^4.3.1",
+ "@types/estree": "^1.0.8",
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://opencollective.com/eslint"
@@ -2261,45 +2158,17 @@
"url": "https://opencollective.com/eslint"
}
},
- "node_modules/eslint/node_modules/ansi-regex": {
+ "node_modules/eslint/node_modules/eslint-visitor-keys": {
"version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/eslint/node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/eslint/node_modules/brace-expansion": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
- "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/eslint/node_modules/doctrine": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
- "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
"dev": true,
"license": "Apache-2.0",
- "dependencies": {
- "esutils": "^2.0.2"
- },
"engines": {
- "node": ">=6.0.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint/node_modules/ignore": {
@@ -2311,45 +2180,32 @@
"node": ">= 4"
}
},
- "node_modules/eslint/node_modules/minimatch": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
- "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "node_modules/espree": {
+ "version": "11.2.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz",
+ "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==",
"dev": true,
- "license": "ISC",
+ "license": "BSD-2-Clause",
"dependencies": {
- "brace-expansion": "^1.1.7"
+ "acorn": "^8.16.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^5.0.1"
},
"engines": {
- "node": "*"
- }
- },
- "node_modules/eslint/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
- "engines": {
- "node": ">=8"
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
- "node_modules/espree": {
- "version": "9.6.1",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
- "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "node_modules/espree/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
"dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "acorn": "^8.9.0",
- "acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^3.4.1"
- },
+ "license": "Apache-2.0",
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://opencollective.com/eslint"
@@ -2424,16 +2280,6 @@
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true
},
- "node_modules/fastq": {
- "version": "1.20.1",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
- "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "reusify": "^1.0.4"
- }
- },
"node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
@@ -2452,16 +2298,16 @@
}
},
"node_modules/file-entry-cache": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
- "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "flat-cache": "^3.0.4"
+ "flat-cache": "^4.0.0"
},
"engines": {
- "node": "^10.12.0 || >=12.0.0"
+ "node": ">=16.0.0"
}
},
"node_modules/find-up": {
@@ -2481,18 +2327,17 @@
}
},
"node_modules/flat-cache": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
- "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
"dev": true,
"license": "MIT",
"dependencies": {
"flatted": "^3.2.9",
- "keyv": "^4.5.3",
- "rimraf": "^3.0.2"
+ "keyv": "^4.5.4"
},
"engines": {
- "node": "^10.12.0 || >=12.0.0"
+ "node": ">=16"
}
},
"node_modules/flatted": {
@@ -2517,13 +2362,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -2663,28 +2501,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.1.1",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- },
- "engines": {
- "node": "*"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -2697,53 +2513,6 @@
"node": ">=10.13.0"
}
},
- "node_modules/glob/node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/glob/node_modules/brace-expansion": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
- "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/glob/node_modules/minimatch": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
- "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/globals": {
- "version": "13.24.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
- "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "type-fest": "^0.20.2"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/globalthis": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
@@ -2772,13 +2541,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/graphemer": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
- "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/has-bigints": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
@@ -2791,16 +2553,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/has-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
@@ -2911,23 +2663,6 @@
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
},
- "node_modules/import-fresh": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
- "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "parent-module": "^1.0.0",
- "resolve-from": "^4.0.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
@@ -2937,18 +2672,6 @@
"node": ">=0.8.19"
}
},
- "node_modules/inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
- "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@@ -3220,16 +2943,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-path-inside": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
- "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/is-regex": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
@@ -3408,29 +3121,6 @@
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
- "node_modules/js-yaml": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
- "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/puzrin"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/nodeca"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "argparse": "^2.0.1"
- },
- "bin": {
- "js-yaml": "bin/js-yaml.js"
- }
- },
"node_modules/jsesc": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
@@ -3866,13 +3556,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/lodash.merge": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
- "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/log-update": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
@@ -4179,16 +3862,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "wrappy": "1"
- }
- },
"node_modules/onetime": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
@@ -4273,19 +3946,6 @@
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
},
- "node_modules/parent-module": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
- "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "callsites": "^3.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -4295,16 +3955,6 @@
"node": ">=8"
}
},
- "node_modules/path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -4435,27 +4085,6 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
- "node_modules/queue-microtask": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
- "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
"node_modules/react": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
@@ -4564,16 +4193,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/resolve-from": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
- "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/restore-cursor": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
@@ -4590,40 +4209,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/reusify": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
- "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "iojs": ">=1.0.0",
- "node": ">=0.10.0"
- }
- },
"node_modules/rfdc": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
"dev": true
},
- "node_modules/rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "deprecated": "Rimraf versions prior to v4 are no longer supported",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "glob": "^7.1.3"
- },
- "bin": {
- "rimraf": "bin.js"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/rolldown": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.4.tgz",
@@ -4658,30 +4249,6 @@
"@rolldown/binding-win32-x64-msvc": "1.1.4"
}
},
- "node_modules/run-parallel": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
- "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "queue-microtask": "^1.2.2"
- }
- },
"node_modules/safe-array-concat": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz",
@@ -5121,32 +4688,6 @@
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
- "node_modules/strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
@@ -5159,13 +4700,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/text-table": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
- "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/tinyexec": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz",
@@ -5223,19 +4757,6 @@
"node": ">= 0.8.0"
}
},
- "node_modules/type-fest": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
- "dev": true,
- "license": "(MIT OR CC0-1.0)",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/typed-array-buffer": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
@@ -5608,13 +5129,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "node_modules/wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/y-indexeddb": {
"version": "9.0.12",
"resolved": "https://registry.npmjs.org/y-indexeddb/-/y-indexeddb-9.0.12.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index e5d4998e..9a0fdaac 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -25,10 +25,10 @@
"@types/leaflet": "^1.9.21",
"@types/react": "^18.3.0",
"@types/react-dom": "^18.3.0",
- "@typescript-eslint/eslint-plugin": "^8.62.1",
+ "@typescript-eslint/eslint-plugin": "^8.63.0",
"@typescript-eslint/parser": "^8.62.0",
"@vitejs/plugin-react": "^6.0.3",
- "eslint": "^8.57.1",
+ "eslint": "^10.6.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.1.1",