commit e2b9b85a40accba9f955925b9bf6a29082265b43 Author: Luis Sanchez Date: Mon Dec 22 22:52:38 2025 -0500 Primer commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5f90350 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +**/.DS_Store +datasumaq/** +backend/venv/** +backend/alembic/.DS_Store +diseño.md +backend/uploads/** diff --git a/backend/.env b/backend/.env new file mode 100644 index 0000000..85f1e8a --- /dev/null +++ b/backend/.env @@ -0,0 +1,11 @@ +# Google Gemini API Key +GOOGLE_API_KEY="****" + +# Base de Datos (PostgreSQL) +DATABASE_URL=postgresql://***:***@localhost:5432/postgres + +DB_USER=*** +DB_PASSWORD=*** +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=postgres \ No newline at end of file diff --git a/backend/alembic.ini b/backend/alembic.ini new file mode 100644 index 0000000..49b477a --- /dev/null +++ b/backend/alembic.ini @@ -0,0 +1,146 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts. +# this is typically a path given in POSIX (e.g. forward slashes) +# format, relative to the token %(here)s which refers to the location of this +# ini file +script_location = %(here)s/alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. for multiple paths, the path separator +# is defined by "path_separator" below. +prepend_sys_path = . + + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to /versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "path_separator" +# below. +# version_locations = %(here)s/bar:%(here)s/bat:%(here)s/alembic/versions + +# path_separator; This indicates what character is used to split lists of file +# paths, including version_locations and prepend_sys_path within configparser +# files such as alembic.ini. +# The default rendered in new alembic.ini files is "os", which uses os.pathsep +# to provide os-dependent path splitting. +# +# Note that in order to support legacy alembic.ini files, this default does NOT +# take place if path_separator is not present in alembic.ini. If this +# option is omitted entirely, fallback logic is as follows: +# +# 1. Parsing of the version_locations option falls back to using the legacy +# "version_path_separator" key, which if absent then falls back to the legacy +# behavior of splitting on spaces and/or commas. +# 2. Parsing of the prepend_sys_path option falls back to the legacy +# behavior of splitting on spaces, commas, or colons. +# +# Valid values for path_separator are: +# +# path_separator = : +# path_separator = ; +# path_separator = space +# path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +# database URL. This is consumed by the user-maintained env.py script only. +# other means of configuring database URLs may be customized within the env.py +# file. +sqlalchemy.url = postgresql://postgres:postgresql@localhost:5432/postgres + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the module runner, against the "ruff" module +# hooks = ruff +# ruff.type = module +# ruff.module = ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Alternatively, use the exec runner to execute a binary found on your PATH +# hooks = ruff +# ruff.type = exec +# ruff.executable = ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration. This is also consumed by the user-maintained +# env.py script only. +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/backend/alembic/README b/backend/alembic/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/backend/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/backend/alembic/env.py b/backend/alembic/env.py new file mode 100644 index 0000000..33b1e2b --- /dev/null +++ b/backend/alembic/env.py @@ -0,0 +1,88 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context + +from database import Base +from models import * +import os +from dotenv import load_dotenv + +load_dotenv() + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + section = config.get_section(config.config_ini_section, {}) + section["sqlalchemy.url"] = os.getenv("DATABASE_URL", section.get("sqlalchemy.url")) + + connectable = engine_from_config( + section, + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/backend/alembic/script.py.mako b/backend/alembic/script.py.mako new file mode 100644 index 0000000..1101630 --- /dev/null +++ b/backend/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/backend/alembic/versions/e59d56a35fe9_crear_tablas.py b/backend/alembic/versions/e59d56a35fe9_crear_tablas.py new file mode 100644 index 0000000..8c36666 --- /dev/null +++ b/backend/alembic/versions/e59d56a35fe9_crear_tablas.py @@ -0,0 +1,34 @@ +"""crear tablas + +Revision ID: e59d56a35fe9 +Revises: +Create Date: 2025-12-22 19:05:11.969285 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'e59d56a35fe9' +down_revision: Union[str, Sequence[str], None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('projects', sa.Column('parent_id', sa.Integer(), nullable=True)) + op.create_foreign_key(None, 'projects', 'projects', ['parent_id'], ['id']) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(None, 'projects', type_='foreignkey') + op.drop_column('projects', 'parent_id') + # ### end Alembic commands ### diff --git a/backend/database.py b/backend/database.py new file mode 100644 index 0000000..b33945b --- /dev/null +++ b/backend/database.py @@ -0,0 +1,24 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +import os +from dotenv import load_dotenv + +load_dotenv() + +# Using PostgreSQL for production +SQLALCHEMY_DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://postgres:postgresql@localhost:5432/postgres") + +engine = create_engine( + SQLALCHEMY_DATABASE_URL +) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +Base = declarative_base() + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() diff --git a/backend/init_db.py b/backend/init_db.py new file mode 100644 index 0000000..b8749d5 --- /dev/null +++ b/backend/init_db.py @@ -0,0 +1,70 @@ +from database import engine, Base, SessionLocal +from models import User, Project, Specialty, Contractor, Activity, NonConformity, Evidence, UserRole +from security import get_password_hash # Import hashing function +import datetime + +def init_db(): + print("Creating tables...") + Base.metadata.create_all(bind=engine) + print("Tables created.") + +def seed_data(): + db = SessionLocal() + + # Check if data exists + if db.query(User).first(): + print("Data already exists.") + db.close() + return + + print("Seeding data...") + + # Users - Password is 'secret' for everyone + hashed = get_password_hash("secret") + + admin = User(email="admin@fritosfresh.com", hashed_password=hashed, full_name="Admin User", role=UserRole.ADMIN) + supervisor = User(email="super@fritosfresh.com", hashed_password=hashed, full_name="Juan Perez", role=UserRole.SUPERVISOR) + db.add(admin) + db.add(supervisor) + db.commit() + + # Specialties + specs = ["Civil", "Mecánica", "Eléctrica", "SSOMA"] + for s_name in specs: + db.add(Specialty(name=s_name)) + db.commit() + + # Project + proj = Project(name="Planta ATE - Expansion", code="ATE-EXP-2025", location="Ate, Lima", start_date=datetime.datetime.now()) + db.add(proj) + db.commit() + + # Contractor + cont = Contractor(name="Constructora Global", ruc="20123456789") + db.add(cont) + db.commit() + + # Activity + civil_spec = db.query(Specialty).filter_by(name="Civil").first() + act = Activity( + project_id=proj.id, + specialty_id=civil_spec.id, + contractor_id=cont.id, + user_id=supervisor.id, + description="Inspeccion de cimientos", + area="Zona Norte" + ) + db.add(act) + db.commit() + + # NC + nc = NonConformity(activity_id=act.id, description="Grieta en muro", level="major") + db.add(nc) + db.commit() + + print("Seeding complete.") + db.close() + +if __name__ == "__main__": + init_db() + seed_data() diff --git a/backend/main.py b/backend/main.py new file mode 100644 index 0000000..adf7557 --- /dev/null +++ b/backend/main.py @@ -0,0 +1,49 @@ +import sys +# Compatibility patch for importlib.metadata in Python < 3.10 +# This fixes the AttributeError: module 'importlib.metadata' has no attribute 'packages_distributions' +if sys.version_info < (3, 10): + try: + import importlib_metadata + import importlib + importlib.metadata = importlib_metadata + except ImportError: + pass + +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware +from routers import auth, users, projects, activities, specialties, contractors, transcription, non_conformities +import os +from fastapi.staticfiles import StaticFiles + +app = FastAPI(title="Sistema de Supervision API", version="0.1.0") + +# CORS (allow all for dev) +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +app.include_router(auth.router) +app.include_router(users.router) +app.include_router(projects.router) +app.include_router(activities.router) +app.include_router(specialties.router) +app.include_router(contractors.router) +app.include_router(transcription.router) +app.include_router(non_conformities.router) + +# Mount uploads directory to serve files +if not os.path.exists("uploads"): + os.makedirs("uploads") +app.mount("/uploads", StaticFiles(directory="uploads"), name="uploads") + +@app.get("/") +def read_root(): + return {"message": "Sistema de Supervision API is running"} + +@app.get("/health") +def health_check(): + return {"status": "ok"} diff --git a/backend/migrate.py b/backend/migrate.py new file mode 100644 index 0000000..3b0e730 --- /dev/null +++ b/backend/migrate.py @@ -0,0 +1,48 @@ +import os +from dotenv import load_dotenv +from sqlalchemy import create_engine, text +from alembic import command +from alembic.config import Config + +# Cargamos variables de entorno +load_dotenv() + +# Construimos la URL de conexión +DATABASE_URL = os.getenv("DATABASE_URL") +if not DATABASE_URL: + DATABASE_URL = ( + f"postgresql://{os.getenv('DB_USER')}:{os.getenv('DB_PASSWORD')}" + f"@{os.getenv('DB_HOST')}:{os.getenv('DB_PORT')}/{os.getenv('DB_NAME')}" + ) + +# Configuración de Alembic +alembic_cfg = Config("alembic.ini") + +# Limpiar alembic_version huérfana si existe +engine = create_engine(DATABASE_URL) + +with engine.connect() as conn: + result = conn.execute( + text( + "SELECT EXISTS (" + "SELECT 1 FROM information_schema.tables " + "WHERE table_name='alembic_version'" + ")" + ) + ) + exists = result.scalar() + + if exists: + print("⚠️ Tabla alembic_version existente detectada. Eliminando...") + conn.execute(text("DROP TABLE alembic_version")) + conn.commit() + +# Crear migración automáticamente +print("🛠️ Generando migración...") +command.revision(alembic_cfg, message="crear tablas", autogenerate=True) + +# Aplicar migración +print("⬆️ Aplicando migración (upgrade head)...") +command.upgrade(alembic_cfg, "head") + +print("✅ Migración completada correctamente") diff --git a/backend/models.py b/backend/models.py new file mode 100644 index 0000000..a9031d6 --- /dev/null +++ b/backend/models.py @@ -0,0 +1,167 @@ +from sqlalchemy import Table, Column, Integer, String, Boolean, ForeignKey, DateTime, Text, Enum, JSON +from sqlalchemy.orm import relationship +from database import Base +import datetime +import enum + +# Junction Tables +project_specialties = Table( + 'project_specialties', + Base.metadata, + Column('project_id', Integer, ForeignKey('projects.id'), primary_key=True), + Column('specialty_id', Integer, ForeignKey('specialties.id'), primary_key=True) +) + +project_contractors = Table( + 'project_contractors', + Base.metadata, + Column('project_id', Integer, ForeignKey('projects.id'), primary_key=True), + Column('contractor_id', Integer, ForeignKey('contractors.id'), primary_key=True) +) + +class UserRole(str, enum.Enum): + ADMIN = "admin" + DIRECTOR = "director" + SUPERVISOR = "supervisor" + COORDINATOR = "coordinator" + CONTRACTOR = "contractor" + CLIENT = "client" + +class ActivityType(str, enum.Enum): + INSPECTION = "inspection" + MEETING = "meeting" + VIRTUAL_MEETING = "virtual_meeting" + COORDINATION = "coordination" + TEST = "test" + OTHER = "other" + +class NCLevel(str, enum.Enum): + CRITICAL = "critical" + MAJOR = "major" + MINOR = "minor" + +class NCType(str, enum.Enum): + HUMAN_ERROR = "Errores humanos" + PROCESS_FAILURE = "Fallas en los procesos" + DESIGN_ISSUE = "Problemas de diseño" + UNCONTROLLED_CHANGE = "Cambios no controlados" + COMMUNICATION_FAILURE = "Falta de comunicación" + +class User(Base): + __tablename__ = "users" + + id = Column(Integer, primary_key=True, index=True) + email = Column(String, unique=True, index=True, nullable=False) + hashed_password = Column(String, nullable=False) + full_name = Column(String) + role = Column(Enum(UserRole), default=UserRole.SUPERVISOR) + is_active = Column(Boolean, default=True) + +class Project(Base): + __tablename__ = "projects" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String, index=True, nullable=False) + code = Column(String, unique=True, index=True) + location = Column(String) + start_date = Column(DateTime) + end_date = Column(DateTime) + status = Column(String, default="active") + parent_id = Column(Integer, ForeignKey("projects.id"), nullable=True) + + # Relationships + parent = relationship("Project", remote_side=[id], back_populates="subprojects") + subprojects = relationship("Project", back_populates="parent") + activities = relationship("Activity", back_populates="project") + specialties = relationship("Specialty", secondary=project_specialties) + contractors = relationship("Contractor", secondary=project_contractors) + +class Specialty(Base): + __tablename__ = "specialties" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String, unique=True, nullable=False) # Civil, Mecánica, Eléctrica, SSOMA + +class Contractor(Base): + __tablename__ = "contractors" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String, nullable=False) + ruc = Column(String, unique=True) + contact_name = Column(String) + email = Column(String, nullable=True) + phone = Column(String, nullable=True) + address = Column(String, nullable=True) + is_active = Column(Boolean, default=True) + + specialty_id = Column(Integer, ForeignKey("specialties.id"), nullable=True) + parent_id = Column(Integer, ForeignKey("contractors.id"), nullable=True) + + specialty = relationship("Specialty") + parent = relationship("Contractor", remote_side=[id], back_populates="subcontractors") + subcontractors = relationship("Contractor", back_populates="parent") + +class Activity(Base): + __tablename__ = "activities" + + id = Column(Integer, primary_key=True, index=True) + project_id = Column(Integer, ForeignKey("projects.id")) + specialty_id = Column(Integer, ForeignKey("specialties.id")) + contractor_id = Column(Integer, ForeignKey("contractors.id"), nullable=True) + user_id = Column(Integer, ForeignKey("users.id")) # Reporter + + date = Column(DateTime, default=datetime.datetime.utcnow) + end_date = Column(DateTime, nullable=True) + type = Column(Enum(ActivityType), default=ActivityType.INSPECTION) + area = Column(String) # Frente de obra / Linea + description = Column(Text) + observations = Column(Text) + audio_transcription = Column(Text, nullable=True) + status = Column(String, default="completed") + + project = relationship("Project", back_populates="activities") + specialty = relationship("Specialty") + contractor = relationship("Contractor") + reporter = relationship("User") + + non_conformities = relationship("NonConformity", back_populates="activity") + evidences = relationship("Evidence", back_populates="activity") + +class NonConformity(Base): + __tablename__ = "non_conformities" + + id = Column(Integer, primary_key=True, index=True) + activity_id = Column(Integer, ForeignKey("activities.id")) + level = Column(Enum(NCLevel), default=NCLevel.MINOR) + description = Column(Text, nullable=False) + status = Column(String, default="open") # open, closed + + # New Fields + due_date = Column(DateTime, nullable=True) + responsible_person = Column(String, nullable=True) + action_checklist = Column(JSON, nullable=True) # List of dicts {text: str, completed: bool} + nc_type = Column(Enum(NCType), nullable=True) + impact_description = Column(Text, nullable=True) + closure_description = Column(Text, nullable=True) + + activity = relationship("Activity", back_populates="non_conformities") + evidences = relationship("Evidence", back_populates="non_conformity") + +class Evidence(Base): + __tablename__ = "evidences" + + id = Column(Integer, primary_key=True, index=True) + activity_id = Column(Integer, ForeignKey("activities.id"), nullable=True) + non_conformity_id = Column(Integer, ForeignKey("non_conformities.id"), nullable=True) + + file_path = Column(String, nullable=False) + media_type = Column(String) # image, video, document + description = Column(String) + captured_at = Column(DateTime, default=datetime.datetime.utcnow) + + # Transcription fields for audio + transcription = Column(Text, nullable=True) + transcription_status = Column(String, default="none") # none, pending, processing, completed, error + + activity = relationship("Activity", back_populates="evidences") + non_conformity = relationship("NonConformity", back_populates="evidences") diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000..cb6d087 --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,82 @@ +alembic==1.16.5 +annotated-doc==0.0.4 +annotated-types==0.7.0 +anyio==4.12.0 +bcrypt==3.2.2 +cachetools==6.2.4 +certifi==2025.11.12 +cffi==2.0.0 +charset-normalizer==3.4.4 +click==8.1.8 +cryptography==46.0.3 +dnspython==2.7.0 +ecdsa==0.19.1 +email-validator==2.3.0 +exceptiongroup==1.3.1 +fastapi==0.125.0 +fastapi-cli==0.0.16 +fastapi-cloud-cli==0.7.0 +fastar==0.8.0 +google-ai-generativelanguage==0.6.15 +google-api-core==2.28.1 +google-api-python-client==2.187.0 +google-auth==2.45.0 +google-auth-httplib2==0.3.0 +google-generativeai==0.8.6 +googleapis-common-protos==1.72.0 +grpcio==1.76.0 +grpcio-status==1.71.2 +h11==0.16.0 +httpcore==1.0.9 +httplib2==0.31.0 +httptools==0.7.1 +httpx==0.28.1 +idna==3.11 +iniconfig==2.1.0 +Jinja2==3.1.6 +Mako==1.3.10 +markdown-it-py==3.0.0 +MarkupSafe==3.0.3 +mdurl==0.1.2 +numpy==2.0.2 +opencv-python-headless==4.12.0.88 +packaging==25.0 +passlib==1.7.4 +pillow==11.3.0 +pluggy==1.6.0 +proto-plus==1.27.0 +protobuf==5.29.5 +psycopg2-binary==2.9.11 +pyasn1==0.6.1 +pyasn1_modules==0.4.2 +pycparser==2.23 +pydantic==2.12.5 +pydantic_core==2.41.5 +Pygments==2.19.2 +pyparsing==3.2.5 +pytest==8.4.2 +python-dotenv==1.2.1 +python-jose==3.5.0 +python-multipart==0.0.20 +PyYAML==6.0.3 +requests==2.32.5 +rich==14.2.0 +rich-toolkit==0.17.1 +rignore==0.7.6 +rsa==4.9.1 +sentry-sdk==2.48.0 +shellingham==1.5.4 +six==1.17.0 +SQLAlchemy==2.0.45 +starlette==0.49.3 +tomli==2.3.0 +tqdm==4.67.1 +typer==0.20.0 +typing-inspection==0.4.2 +typing_extensions==4.15.0 +uritemplate==4.2.0 +urllib3==2.6.2 +uvicorn==0.38.0 +uvloop==0.22.1 +watchfiles==1.1.1 +websockets==15.0.1 diff --git a/backend/routers/activities.py b/backend/routers/activities.py new file mode 100644 index 0000000..08e0216 --- /dev/null +++ b/backend/routers/activities.py @@ -0,0 +1,190 @@ +import os +import shutil +from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File, BackgroundTasks +from sqlalchemy.orm import Session +from typing import List, Optional +from database import get_db +from models import Activity, Evidence, User +from security import get_current_active_user +import schemas +import uuid +from services import activities + +router = APIRouter( + prefix="/activities", + tags=["Activities"] +) + +UPLOAD_DIR = "uploads" +if not os.path.exists(UPLOAD_DIR): + os.makedirs(UPLOAD_DIR) + +@router.post("/", response_model=schemas.Activity) +def create_activity( + activity: schemas.ActivityCreate, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_active_user) +): + return activities.create_activity(db, activity, current_user) + +@router.get("/", response_model=List[schemas.Activity]) +def read_activities( + project_id: Optional[int] = None, + specialty_id: Optional[int] = None, + skip: int = 0, + limit: int = 100, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_active_user) +): + db_activities = activities.get_activities(db, current_user, project_id, specialty_id, skip, limit) + return db_activities + +@router.get("/{activity_id}", response_model=schemas.Activity) +def read_activity( + activity_id: int, + db: Session = Depends(get_db) +): + return activities.get_activity(db, activity_id) + +@router.put("/{activity_id}", response_model=schemas.Activity) +def update_activity( + activity_id: int, + activity: schemas.ActivityUpdate, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_active_user) +): + db_activity = db.query(Activity).filter(Activity.id == activity_id).first() + if not db_activity: + raise HTTPException(status_code=404, detail="Activity not found") + + update_data = activity.dict(exclude_unset=True) + for key, value in update_data.items(): + setattr(db_activity, key, value) + + db.commit() + db.refresh(db_activity) + return db_activity + +@router.post("/{activity_id}/upload", response_model=schemas.Evidence) +async def upload_evidence( + activity_id: int, + background_tasks: BackgroundTasks, + file: UploadFile = File(...), + description: Optional[str] = None, + captured_at: Optional[str] = None, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_active_user) +): + # Verify activity exists + db_activity = db.query(Activity).filter(Activity.id == activity_id).first() + if not db_activity: + raise HTTPException(status_code=404, detail="Activity not found") + + # Generate unique filename + file_ext = os.path.splitext(file.filename)[1] + unique_filename = f"{uuid.uuid4()}{file_ext}" + file_path = os.path.join(UPLOAD_DIR, unique_filename) + + # Save file + with open(file_path, "wb") as buffer: + shutil.copyfileobj(file.file, buffer) + + import datetime + db_captured_at = None + if captured_at: + try: + db_captured_at = datetime.datetime.fromisoformat(captured_at.replace('Z', '+00:00')) + except: + db_captured_at = datetime.datetime.utcnow() + + # Determine transcription status + initial_status = "none" + if file.content_type and "audio" in file.content_type: + initial_status = "pending" + + # Save to database + db_evidence = Evidence( + activity_id=activity_id, + file_path=file_path, + media_type=file.content_type, + description=description, + captured_at=db_captured_at, + transcription_status=initial_status + ) + db.add(db_evidence) + db.commit() + db.refresh(db_evidence) + + # If it's audio, queue transcription + if initial_status == "pending": + from services.transcription_worker import process_transcription + background_tasks.add_task(process_transcription, db_evidence.id) + + return db_evidence + +@router.post("/evidence/{evidence_id}/retry-transcription", response_model=schemas.Evidence) +async def retry_transcription( + evidence_id: int, + background_tasks: BackgroundTasks, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_active_user) +): + db_evidence = db.query(Evidence).filter(Evidence.id == evidence_id).first() + if not db_evidence: + raise HTTPException(status_code=404, detail="Evidence not found") + + if not db_evidence.media_type or "audio" not in db_evidence.media_type: + raise HTTPException(status_code=400, detail="Only audio evidence can be transcribed") + + # Update status to pending + db_evidence.transcription_status = "pending" + db_evidence.transcription = None + db.commit() + db.refresh(db_evidence) + + # Queue transcription task + from services.transcription_worker import process_transcription + background_tasks.add_task(process_transcription, db_evidence.id) + + return db_evidence + return db_evidence + +@router.put("/evidence/{evidence_id}", response_model=schemas.Evidence) +def update_evidence( + evidence_id: int, + evidence: schemas.EvidenceUpdate, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_active_user) +): + db_evidence = db.query(Evidence).filter(Evidence.id == evidence_id).first() + if not db_evidence: + raise HTTPException(status_code=404, detail="Evidence not found") + + update_data = evidence.dict(exclude_unset=True) + for key, value in update_data.items(): + setattr(db_evidence, key, value) + + db.commit() + db.refresh(db_evidence) + return db_evidence + +@router.delete("/evidence/{evidence_id}") +def delete_evidence( + evidence_id: int, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_active_user) +): + db_evidence = db.query(Evidence).filter(Evidence.id == evidence_id).first() + if not db_evidence: + raise HTTPException(status_code=404, detail="Evidence not found") + + # Optional: Delete file from disk + if db_evidence.file_path and os.path.exists(db_evidence.file_path): + try: + os.remove(db_evidence.file_path) + except: + pass + + db.delete(db_evidence) + db.commit() + return {"detail": "Evidence deleted"} diff --git a/backend/routers/auth.py b/backend/routers/auth.py new file mode 100644 index 0000000..1db1374 --- /dev/null +++ b/backend/routers/auth.py @@ -0,0 +1,25 @@ +from fastapi import APIRouter, Depends, HTTPException, status +from fastapi.security import OAuth2PasswordRequestForm +from sqlalchemy.orm import Session +from database import get_db +from models import User +from security import verify_password, create_access_token +import schemas +from datetime import timedelta + +router = APIRouter(tags=["Authentication"]) + +@router.post("/token", response_model=schemas.Token) +def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)): + user = db.query(User).filter(User.email == form_data.username).first() + if not user or not verify_password(form_data.password, user.hashed_password): + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Incorrect username or password", + headers={"WWW-Authenticate": "Bearer"}, + ) + access_token_expires = timedelta(minutes=300) + access_token = create_access_token( + data={"sub": user.email}, expires_delta=access_token_expires + ) + return {"access_token": access_token, "token_type": "bearer"} diff --git a/backend/routers/contractors.py b/backend/routers/contractors.py new file mode 100644 index 0000000..af101dc --- /dev/null +++ b/backend/routers/contractors.py @@ -0,0 +1,75 @@ +from fastapi import APIRouter, Depends, HTTPException, status +from sqlalchemy.orm import Session +from typing import List, Optional +from database import get_db +from models import Contractor +from security import get_current_active_user +import schemas + +router = APIRouter( + prefix="/contractors", + tags=["Contractors"], + dependencies=[Depends(get_current_active_user)] +) + +@router.post("/", response_model=schemas.Contractor) +def create_contractor(contractor: schemas.ContractorCreate, db: Session = Depends(get_db)): + db_contractor = Contractor(**contractor.dict()) + db.add(db_contractor) + db.commit() + db.refresh(db_contractor) + return db_contractor + +@router.get("/", response_model=List[schemas.Contractor]) +def read_contractors( + parent_id: Optional[int] = None, + only_parents: bool = False, + is_active: Optional[bool] = None, + db: Session = Depends(get_db) +): + query = db.query(Contractor) + if only_parents: + query = query.filter(Contractor.parent_id == None) + elif parent_id is not None: + query = query.filter(Contractor.parent_id == parent_id) + + if is_active is not None: + query = query.filter(Contractor.is_active == is_active) + return query.all() + +@router.get("/{contractor_id}", response_model=schemas.Contractor) +def read_contractor(contractor_id: int, db: Session = Depends(get_db)): + db_contractor = db.query(Contractor).filter(Contractor.id == contractor_id).first() + if not db_contractor: + raise HTTPException(status_code=404, detail="Contractor not found") + return db_contractor + +@router.put("/{contractor_id}", response_model=schemas.Contractor) +@router.patch("/{contractor_id}", response_model=schemas.Contractor) +def update_contractor( + contractor_id: int, + contractor: schemas.ContractorUpdate, + db: Session = Depends(get_db) +): + db_contractor = db.query(Contractor).filter(Contractor.id == contractor_id).first() + if not db_contractor: + raise HTTPException(status_code=404, detail="Contractor not found") + + update_data = contractor.dict(exclude_unset=True) + for key, value in update_data.items(): + setattr(db_contractor, key, value) + + db.commit() + db.refresh(db_contractor) + return db_contractor + +@router.delete("/{contractor_id}", status_code=status.HTTP_204_NO_CONTENT) +def delete_contractor(contractor_id: int, db: Session = Depends(get_db)): + db_contractor = db.query(Contractor).filter(Contractor.id == contractor_id).first() + if not db_contractor: + raise HTTPException(status_code=404, detail="Contractor not found") + + # Optional: instead of hard delete, maybe just deactivate + db.delete(db_contractor) + db.commit() + return None diff --git a/backend/routers/non_conformities.py b/backend/routers/non_conformities.py new file mode 100644 index 0000000..9e0a24b --- /dev/null +++ b/backend/routers/non_conformities.py @@ -0,0 +1,142 @@ +import os +import shutil +import uuid +import datetime +from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File +from sqlalchemy.orm import Session +from typing import List, Optional +from database import get_db +from models import NonConformity, User, Activity, Evidence +from security import get_current_active_user +import schemas + +router = APIRouter( + prefix="/non-conformities", + tags=["Non-Conformities"] +) + +UPLOAD_DIR = "uploads" +if not os.path.exists(UPLOAD_DIR): + os.makedirs(UPLOAD_DIR) + +@router.post("/", response_model=schemas.NonConformity) +def create_nc( + nc: schemas.NonConformityCreate, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_active_user) +): + db_activity = db.query(Activity).filter(Activity.id == nc.activity_id).first() + if not db_activity: + raise HTTPException(status_code=404, detail="Activity not found") + + db_nc = NonConformity(**nc.dict()) + db.add(db_nc) + db.commit() + db.refresh(db_nc) + return db_nc + +@router.get("/", response_model=List[schemas.NonConformity]) +def read_ncs( + activity_id: Optional[int] = None, + status: Optional[str] = None, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_active_user) +): + query = db.query(NonConformity) + if activity_id: + query = query.filter(NonConformity.activity_id == activity_id) + if status: + query = query.filter(NonConformity.status == status) + + return query.all() + +@router.get("/{nc_id}", response_model=schemas.NonConformity) +def read_nc( + nc_id: int, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_active_user) +): + db_nc = db.query(NonConformity).filter(NonConformity.id == nc_id).first() + if db_nc is None: + raise HTTPException(status_code=404, detail="Non-Conformity not found") + return db_nc + +@router.put("/{nc_id}", response_model=schemas.NonConformity) +@router.patch("/{nc_id}", response_model=schemas.NonConformity) +def update_nc( + nc_id: int, + nc: schemas.NonConformityUpdate, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_active_user) +): + db_nc = db.query(NonConformity).filter(NonConformity.id == nc_id).first() + if not db_nc: + raise HTTPException(status_code=404, detail="Non-Conformity not found") + + update_data = nc.dict(exclude_unset=True) + for key, value in update_data.items(): + setattr(db_nc, key, value) + + db.commit() + db.refresh(db_nc) + return db_nc + +@router.post("/{nc_id}/upload", response_model=schemas.Evidence) +async def upload_nc_evidence( + nc_id: int, + file: UploadFile = File(...), + description: Optional[str] = None, + captured_at: Optional[str] = None, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_active_user) +): + # Verify NC exists + db_nc = db.query(NonConformity).filter(NonConformity.id == nc_id).first() + if not db_nc: + raise HTTPException(status_code=404, detail="Non-Conformity not found") + + # Generate unique filename + file_ext = os.path.splitext(file.filename)[1] + unique_filename = f"nc_{uuid.uuid4()}{file_ext}" + file_path = os.path.join(UPLOAD_DIR, unique_filename) + + # Save file + with open(file_path, "wb") as buffer: + shutil.copyfileobj(file.file, buffer) + + db_captured_at = None + if captured_at: + try: + db_captured_at = datetime.datetime.fromisoformat(captured_at.replace('Z', '+00:00')) + except: + db_captured_at = datetime.datetime.utcnow() + else: + db_captured_at = datetime.datetime.utcnow() + + # Save to database + db_evidence = Evidence( + non_conformity_id=nc_id, + file_path=file_path, + media_type=file.content_type, + description=description, + captured_at=db_captured_at + ) + db.add(db_evidence) + db.commit() + db.refresh(db_evidence) + + return db_evidence + +@router.delete("/{nc_id}") +def delete_nc( + nc_id: int, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_active_user) +): + db_nc = db.query(NonConformity).filter(NonConformity.id == nc_id).first() + if not db_nc: + raise HTTPException(status_code=404, detail="Non-Conformity not found") + + db.delete(db_nc) + db.commit() + return {"detail": "Non-Conformity deleted"} diff --git a/backend/routers/projects.py b/backend/routers/projects.py new file mode 100644 index 0000000..bfa7745 --- /dev/null +++ b/backend/routers/projects.py @@ -0,0 +1,95 @@ +from fastapi import APIRouter, Depends, HTTPException, status +from sqlalchemy.orm import Session +from typing import List +from database import get_db +from models import Project, Specialty, Contractor +from security import get_current_active_user +import schemas + +router = APIRouter( + prefix="/projects", + tags=["Projects"], + dependencies=[Depends(get_current_active_user)] +) + +@router.post("/", response_model=schemas.Project) +def create_project(project: schemas.ProjectCreate, db: Session = Depends(get_db)): + db_project = db.query(Project).filter(Project.code == project.code).first() + if db_project: + raise HTTPException(status_code=400, detail="Project code already exists") + + + project_data = project.dict(exclude={'specialty_ids', 'contractor_ids'}) + db_project = Project(**project_data) + + # Handle Parent Project + if project.parent_id: + parent = db.query(Project).filter(Project.id == project.parent_id).first() + if not parent: + raise HTTPException(status_code=404, detail="Parent project not found") + if project.specialty_ids: + specialties = db.query(Specialty).filter(Specialty.id.in_(project.specialty_ids)).all() + db_project.specialties = specialties + + # Handle Contractors + if project.contractor_ids: + contractors = db.query(Contractor).filter(Contractor.id.in_(project.contractor_ids)).all() + db_project.contractors = contractors + + db.add(db_project) + db.commit() + db.refresh(db_project) + return db_project + +@router.get("/", response_model=List[schemas.Project]) +def read_projects(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): + projects = db.query(Project).offset(skip).limit(limit).all() + return projects + +@router.get("/{project_id}", response_model=schemas.Project) +def read_project(project_id: int, db: Session = Depends(get_db)): + db_project = db.query(Project).filter(Project.id == project_id).first() + if db_project is None: + raise HTTPException(status_code=404, detail="Project not found") + return db_project + +@router.put("/{project_id}", response_model=schemas.Project) +def update_project(project_id: int, project: schemas.ProjectCreate, db: Session = Depends(get_db)): + db_project = db.query(Project).filter(Project.id == project_id).first() + if db_project is None: + raise HTTPException(status_code=404, detail="Project not found") + + # Update simple fields + for key, value in project.dict(exclude={'specialty_ids', 'contractor_ids'}).items(): + setattr(db_project, key, value) + + # Handle Parent Project + if project.parent_id is not None: + parent = db.query(Project).filter(Project.id == project.parent_id).first() + if not parent and project.parent_id != 0: # Allow 0 or null to clear? Actually null is enough + raise HTTPException(status_code=404, detail="Parent project not found") + db_project.parent_id = project.parent_id if project.parent_id != 0 else None + + # Update Specialties + if project.specialty_ids is not None: + specialties = db.query(Specialty).filter(Specialty.id.in_(project.specialty_ids)).all() + db_project.specialties = specialties + + # Update Contractors + if project.contractor_ids is not None: + contractors = db.query(Contractor).filter(Contractor.id.in_(project.contractor_ids)).all() + db_project.contractors = contractors + + db.commit() + db.refresh(db_project) + return db_project + +@router.delete("/{project_id}", status_code=status.HTTP_204_NO_CONTENT) +def delete_project(project_id: int, db: Session = Depends(get_db)): + db_project = db.query(Project).filter(Project.id == project_id).first() + if db_project is None: + raise HTTPException(status_code=404, detail="Project not found") + + db.delete(db_project) + db.commit() + return None diff --git a/backend/routers/specialties.py b/backend/routers/specialties.py new file mode 100644 index 0000000..311342d --- /dev/null +++ b/backend/routers/specialties.py @@ -0,0 +1,17 @@ +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.orm import Session +from typing import List +from database import get_db +from models import Specialty +from security import get_current_active_user +import schemas + +router = APIRouter( + prefix="/specialties", + tags=["Specialties"], + dependencies=[Depends(get_current_active_user)] +) + +@router.get("/", response_model=List[schemas.Specialty]) +def read_specialties(db: Session = Depends(get_db)): + return db.query(Specialty).all() diff --git a/backend/routers/transcription.py b/backend/routers/transcription.py new file mode 100644 index 0000000..28874e1 --- /dev/null +++ b/backend/routers/transcription.py @@ -0,0 +1,54 @@ +import os +from fastapi import APIRouter, Depends, UploadFile, File, HTTPException +from security import get_current_active_user +import google.generativeai as genai +import tempfile +from dotenv import load_dotenv + +load_dotenv() + +router = APIRouter( + prefix="/transcription", + tags=["Transcription"], + dependencies=[Depends(get_current_active_user)] +) + +# Initialize Google Gemini +api_key = os.getenv("GOOGLE_API_KEY") +if api_key: + genai.configure(api_key=api_key) + + +@router.post("/") +async def transcribe_audio(file: UploadFile = File(...)): + if not os.getenv("GOOGLE_API_KEY"): + # Mock transcription for development if no key is present + return {"text": f"[MOCK GEMINI TRANSCRIPTION] Se ha recibido un archivo de audio de tipo {file.content_type}. Configure GOOGLE_API_KEY para transcripción real con Gemini."} + + try: + # Create a temporary file to store the upload + suffix = os.path.splitext(file.filename)[1] or ".wav" + with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as tmp: + content = await file.read() + tmp.write(content) + tmp_path = tmp.name + + # Upload to Gemini (Media Service) + audio_file = genai.upload_file(path=tmp_path, mime_type=file.content_type or "audio/wav") + + # Use Gemini 1.5 Flash for audio-to-text + model = genai.GenerativeModel("gemini-2.5-flash-lite") + response = model.generate_content([ + "Por favor, transcribe exactamente lo que se dice en este audio. Solo devuelve el texto transcrito.", + audio_file + ]) + + # Cleanup + os.unlink(tmp_path) + # Gemini files are ephemeral but we can delete explicitly if needed + # genai.delete_file(audio_file.name) + + return {"text": response.text} + + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) diff --git a/backend/routers/users.py b/backend/routers/users.py new file mode 100644 index 0000000..e2e39df --- /dev/null +++ b/backend/routers/users.py @@ -0,0 +1,27 @@ +from fastapi import APIRouter, Depends, HTTPException, status +from sqlalchemy.orm import Session +from typing import List +from database import get_db +from models import User +from security import get_password_hash, get_current_active_user +from services import users +from schemas import UserCreate, User + +router = APIRouter( + prefix="/users", + tags=["Users"], + dependencies=[Depends(get_current_active_user)] +) + +@router.post("/", response_model=User) +def create_user(user: UserCreate, db: Session = Depends(get_db)): + return users.create_user(db, user) + +@router.get("/", response_model=List[User]) +def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): + db_users = users.get_users(db, skip, limit) + return db_users + +@router.get("/me", response_model=User) +def read_users_me(current_user: User = Depends(get_current_active_user)): + return current_user diff --git a/backend/schemas.py b/backend/schemas.py new file mode 100644 index 0000000..3d1a6c6 --- /dev/null +++ b/backend/schemas.py @@ -0,0 +1,194 @@ +from pydantic import BaseModel, EmailStr, field_validator +from typing import Optional, List +from datetime import datetime +from models import UserRole, ActivityType, NCLevel, NCType + +# Token +class Token(BaseModel): + access_token: str + token_type: str + +class TokenData(BaseModel): + email: Optional[str] = None + +# User +class UserBase(BaseModel): + email: EmailStr + full_name: Optional[str] = None + role: UserRole = UserRole.SUPERVISOR + is_active: bool = True + +class UserCreate(UserBase): + password: str + +class User(UserBase): + id: int + + class Config: + from_attributes = True + +# Project +class ProjectBase(BaseModel): + name: str + code: str + location: Optional[str] = None + start_date: Optional[datetime] = None + end_date: Optional[datetime] = None + status: str = "active" + parent_id: Optional[int] = None + +class ProjectCreate(ProjectBase): + specialty_ids: Optional[List[int]] = [] + contractor_ids: Optional[List[int]] = [] + +class Project(ProjectBase): + id: int + specialties: List['Specialty'] = [] + contractors: List['Contractor'] = [] + subprojects: List['Project'] = [] + + class Config: + from_attributes = True + +# Specialty +class SpecialtyBase(BaseModel): + name: str + +class Specialty(SpecialtyBase): + id: int + class Config: + from_attributes = True + +# Contractor +class ContractorBase(BaseModel): + name: str + ruc: Optional[str] = None + contact_name: Optional[str] = None + email: Optional[EmailStr] = None + phone: Optional[str] = None + address: Optional[str] = None + specialty_id: Optional[int] = None + parent_id: Optional[int] = None + is_active: bool = True + + @field_validator('ruc', 'contact_name', 'email', 'phone', 'address', 'specialty_id', 'parent_id', mode='before') + @classmethod + def empty_string_to_none(cls, v): + if v == "": + return None + return v + +class ContractorCreate(ContractorBase): + pass + +class ContractorUpdate(BaseModel): + name: Optional[str] = None + ruc: Optional[str] = None + contact_name: Optional[str] = None + email: Optional[EmailStr] = None + phone: Optional[str] = None + address: Optional[str] = None + specialty_id: Optional[int] = None + parent_id: Optional[int] = None + is_active: Optional[bool] = None + +class Contractor(ContractorBase): + id: int + specialty: Optional[Specialty] = None + subcontractors: List['Contractor'] = [] + + class Config: + from_attributes = True + +# Evidence +class EvidenceBase(BaseModel): + file_path: str + media_type: Optional[str] = None + description: Optional[str] = None + captured_at: Optional[datetime] = None + transcription: Optional[str] = None + transcription_status: str = "none" + +class Evidence(EvidenceBase): + id: int + activity_id: Optional[int] = None + non_conformity_id: Optional[int] = None + class Config: + from_attributes = True + +class EvidenceUpdate(BaseModel): + description: Optional[str] = None + +# Activity +class ActivityBase(BaseModel): + project_id: int + specialty_id: int + contractor_id: Optional[int] = None + type: ActivityType = ActivityType.INSPECTION + area: Optional[str] = None + description: Optional[str] = None + observations: Optional[str] = None + audio_transcription: Optional[str] = None + status: str = "completed" + date: Optional[datetime] = None + end_date: Optional[datetime] = None + +class ActivityUpdate(BaseModel): + project_id: Optional[int] = None + specialty_id: Optional[int] = None + contractor_id: Optional[int] = None + area: Optional[str] = None + description: Optional[str] = None + observations: Optional[str] = None + status: Optional[str] = None + date: Optional[datetime] = None + end_date: Optional[datetime] = None + +class ActivityCreate(ActivityBase): + pass + +# NonConformity +class NonConformityBase(BaseModel): + level: NCLevel = NCLevel.MINOR + description: str + status: str = "open" + due_date: Optional[datetime] = None + responsible_person: Optional[str] = None + action_checklist: Optional[List[dict]] = None + nc_type: Optional[NCType] = None + impact_description: Optional[str] = None + closure_description: Optional[str] = None + +class NonConformityCreate(NonConformityBase): + activity_id: int + +class NonConformityUpdate(BaseModel): + level: Optional[NCLevel] = None + description: Optional[str] = None + status: Optional[str] = None + due_date: Optional[datetime] = None + responsible_person: Optional[str] = None + action_checklist: Optional[List[dict]] = None + nc_type: Optional[NCType] = None + impact_description: Optional[str] = None + closure_description: Optional[str] = None + +class NonConformity(NonConformityBase): + id: int + activity_id: int + evidences: List[Evidence] = [] + + class Config: + from_attributes = True + +class Activity(ActivityBase): + id: int + user_id: int + project: Optional[Project] = None + evidences: List[Evidence] = [] + non_conformities: List[NonConformity] = [] + + class Config: + from_attributes = True + +Contractor.model_rebuild() diff --git a/backend/security.py b/backend/security.py new file mode 100644 index 0000000..9b41482 --- /dev/null +++ b/backend/security.py @@ -0,0 +1,58 @@ +from datetime import datetime, timedelta +from typing import Optional +from jose import JWTError, jwt +from passlib.context import CryptContext +from fastapi import Depends, HTTPException, status +from fastapi.security import OAuth2PasswordBearer +from sqlalchemy.orm import Session +from database import get_db +from models import User +import schemas + +# Secret key for JWT (should be in env vars in production) +SECRET_KEY = "super_secret_key_for_fritos_fresh_supervision_system" +ALGORITHM = "HS256" +ACCESS_TOKEN_EXPIRE_MINUTES = 300 + +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") +oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") + +def verify_password(plain_password, hashed_password): + return pwd_context.verify(plain_password, hashed_password) + +def get_password_hash(password): + return pwd_context.hash(password) + +def create_access_token(data: dict, expires_delta: Optional[timedelta] = None): + to_encode = data.copy() + if expires_delta: + expire = datetime.utcnow() + expires_delta + else: + expire = datetime.utcnow() + timedelta(minutes=15) + to_encode.update({"exp": expire}) + encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) + return encoded_jwt + +async def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)): + credentials_exception = HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Could not validate credentials", + headers={"WWW-Authenticate": "Bearer"}, + ) + try: + payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) + email: str = payload.get("sub") + if email is None: + raise credentials_exception + token_data = schemas.TokenData(email=email) + except JWTError: + raise credentials_exception + user = db.query(User).filter(User.email == token_data.email).first() + if user is None: + raise credentials_exception + return user + +async def get_current_active_user(current_user: User = Depends(get_current_user)): + if not current_user.is_active: + raise HTTPException(status_code=400, detail="Inactive user") + return current_user diff --git a/backend/services/activities.py b/backend/services/activities.py new file mode 100644 index 0000000..733a62a --- /dev/null +++ b/backend/services/activities.py @@ -0,0 +1,35 @@ +from sqlalchemy.orm import Session +from typing import Optional +from models import Activity, User +from schemas import ActivityCreate +from fastapi import HTTPException + +def get_activities(db: Session, current_user: User, project_id: Optional[int] = None, + specialty_id: Optional[int] = None, + skip: int = 0, + limit: int = 100): + + query = db.query(Activity) + if project_id: + query = query.filter(Activity.project_id == project_id) + if specialty_id: + query = query.filter(Activity.specialty_id == specialty_id) + + activities = query.offset(skip).limit(limit).all() + return activities + +def get_activity(db: Session, activity_id: int): + db_activity = db.query(Activity).filter(Activity.id == activity_id).first() + if db_activity is None: + raise HTTPException(status_code=404, detail="Activity not found") + return db_activity + +def create_activity(db: Session, activity: ActivityCreate, current_user: User): + db_activity = Activity( + **activity.dict(), + user_id=current_user.id + ) + db.add(db_activity) + db.commit() + db.refresh(db_activity) + return db_activity \ No newline at end of file diff --git a/backend/services/transcription_worker.py b/backend/services/transcription_worker.py new file mode 100644 index 0000000..87f1f53 --- /dev/null +++ b/backend/services/transcription_worker.py @@ -0,0 +1,71 @@ +import os +import time +from sqlalchemy.orm import Session +from models import Evidence +import google.generativeai as genai +from database import SessionLocal + +def process_transcription(evidence_id: int): + """ + Background task to transcribe audio. + In a real scenario, this would call a local model runner like Ollama (Whisper) + or an external API like Gemini. + """ + db = SessionLocal() + try: + evidence = db.query(Evidence).filter(Evidence.id == evidence_id).first() + if not evidence: + return + + evidence.transcription_status = "processing" + db.commit() + + # Simulate local processing or call a local model + # For now, we'll try to use Gemini if available, or a mock + + file_path = evidence.file_path + if not os.path.exists(file_path): + evidence.transcription_status = "error" + evidence.transcription = "Error: File not found" + db.commit() + return + + api_key = os.getenv("GOOGLE_API_KEY") + if api_key: + try: + genai.configure(api_key=api_key) + + # Upload to Gemini (Media Service) + audio_file = genai.upload_file(path=file_path, mime_type=evidence.media_type or "audio/wav") + + # Use Gemini 1.5 Flash for audio-to-text + model = genai.GenerativeModel("gemini-2.5-flash-lite") + response = model.generate_content([ + "Por favor, transcribe exactamente lo que se dice en este audio. Solo devuelve el texto transcrito.", + audio_file + ]) + + evidence.transcription = response.text + evidence.transcription_status = "completed" + except Exception as e: + evidence.transcription_status = "error" + evidence.transcription = f"Error: {str(e)}" + else: + # Mock transcription if no API key (Local Model Simulation) + time.sleep(5) # Simulate work + evidence.transcription = f"[LOCAL MOCK TRANSCRIPTION] Transcripción asíncrona completada para {os.path.basename(file_path)}" + evidence.transcription_status = "completed" + + db.commit() + except Exception as e: + print(f"Transcription error: {e}") + try: + evidence = db.query(Evidence).filter(Evidence.id == evidence_id).first() + if evidence: + evidence.transcription_status = "error" + evidence.transcription = f"Unexpected error: {str(e)}" + db.commit() + except: + pass + finally: + db.close() diff --git a/backend/services/users.py b/backend/services/users.py new file mode 100644 index 0000000..4f39fb4 --- /dev/null +++ b/backend/services/users.py @@ -0,0 +1,24 @@ +from sqlalchemy.orm import Session +from models import User +from security import get_password_hash +from schemas import UserCreate +from fastapi import HTTPException + +def get_users(db: Session, skip: int = 0, limit: int = 100): + return db.query(User).offset(skip).limit(limit).all() + +def create_user(db: Session, user: UserCreate): + db_user = db.query(User).filter(User.email == user.email).first() + if db_user: + raise HTTPException(status_code=400, detail="Email already registered") + hashed_password = get_password_hash(user.password) + db_user = User( + email=user.email, + hashed_password=hashed_password, + full_name=user.full_name, + role=user.role + ) + db.add(db_user) + db.commit() + db.refresh(db_user) + return db_user \ No newline at end of file diff --git a/backend/supervision.db b/backend/supervision.db new file mode 100644 index 0000000..aca33de Binary files /dev/null and b/backend/supervision.db differ diff --git a/frontend/.editorconfig b/frontend/.editorconfig new file mode 100644 index 0000000..f166060 --- /dev/null +++ b/frontend/.editorconfig @@ -0,0 +1,17 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single +ij_typescript_use_double_quotes = false + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..b1d225e --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,43 @@ +# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. + +# Compiled output +/dist +/tmp +/out-tsc +/bazel-out + +# Node +/node_modules +npm-debug.log +yarn-error.log + +# IDEs and editors +.idea/ +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# Miscellaneous +/.angular/cache +.sass-cache/ +/connect.lock +/coverage +/libpeerconnection.log +testem.log +/typings +__screenshots__/ + +# System files +.DS_Store +Thumbs.db diff --git a/frontend/.vscode/extensions.json b/frontend/.vscode/extensions.json new file mode 100644 index 0000000..77b3745 --- /dev/null +++ b/frontend/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 + "recommendations": ["angular.ng-template"] +} diff --git a/frontend/.vscode/launch.json b/frontend/.vscode/launch.json new file mode 100644 index 0000000..925af83 --- /dev/null +++ b/frontend/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "ng serve", + "type": "chrome", + "request": "launch", + "preLaunchTask": "npm: start", + "url": "http://localhost:4200/" + }, + { + "name": "ng test", + "type": "chrome", + "request": "launch", + "preLaunchTask": "npm: test", + "url": "http://localhost:9876/debug.html" + } + ] +} diff --git a/frontend/.vscode/tasks.json b/frontend/.vscode/tasks.json new file mode 100644 index 0000000..a298b5b --- /dev/null +++ b/frontend/.vscode/tasks.json @@ -0,0 +1,42 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "start", + "isBackground": true, + "problemMatcher": { + "owner": "typescript", + "pattern": "$tsc", + "background": { + "activeOnStart": true, + "beginsPattern": { + "regexp": "(.*?)" + }, + "endsPattern": { + "regexp": "bundle generation complete" + } + } + } + }, + { + "type": "npm", + "script": "test", + "isBackground": true, + "problemMatcher": { + "owner": "typescript", + "pattern": "$tsc", + "background": { + "activeOnStart": true, + "beginsPattern": { + "regexp": "(.*?)" + }, + "endsPattern": { + "regexp": "bundle generation complete" + } + } + } + } + ] +} diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..784bda1 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,59 @@ +# SupervisionApp + +This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.0.3. + +## Development server + +To start a local development server, run: + +```bash +ng serve +``` + +Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files. + +## Code scaffolding + +Angular CLI includes powerful code scaffolding tools. To generate a new component, run: + +```bash +ng generate component component-name +``` + +For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run: + +```bash +ng generate --help +``` + +## Building + +To build the project run: + +```bash +ng build +``` + +This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed. + +## Running unit tests + +To execute unit tests with the [Vitest](https://vitest.dev/) test runner, use the following command: + +```bash +ng test +``` + +## Running end-to-end tests + +For end-to-end (e2e) testing, run: + +```bash +ng e2e +``` + +Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs. + +## Additional Resources + +For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. diff --git a/frontend/angular.json b/frontend/angular.json new file mode 100644 index 0000000..9bbd9d3 --- /dev/null +++ b/frontend/angular.json @@ -0,0 +1,84 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "cli": { + "packageManager": "npm" + }, + "newProjectRoot": "projects", + "projects": { + "supervision-app": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular/build:application", + "options": { + "browser": "src/main.ts", + "tsConfig": "tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": [ + "src/styles.scss" + ] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kB", + "maximumError": "1MB" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "4kB", + "maximumError": "8kB" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular/build:dev-server", + "configurations": { + "production": { + "buildTarget": "supervision-app:build:production" + }, + "development": { + "buildTarget": "supervision-app:build:development" + } + }, + "defaultConfiguration": "development" + }, + "test": { + "builder": "@angular/build:unit-test" + } + } + } + } +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..3d17233 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,9374 @@ +{ + "name": "supervision-app", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "supervision-app", + "version": "0.0.0", + "dependencies": { + "@angular/cdk": "^21.0.0", + "@angular/common": "^21.0.0", + "@angular/compiler": "^21.0.0", + "@angular/core": "^21.0.0", + "@angular/forms": "^21.0.0", + "@angular/platform-browser": "^21.0.0", + "@angular/router": "^21.0.0", + "lucide-angular": "^0.561.0", + "rxjs": "~7.8.0", + "tslib": "^2.3.0" + }, + "devDependencies": { + "@angular/build": "^21.0.3", + "@angular/cli": "^21.0.3", + "@angular/compiler-cli": "^21.0.0", + "jsdom": "^27.1.0", + "typescript": "~5.9.2", + "vitest": "^4.0.8" + } + }, + "node_modules/@acemir/cssom": { + "version": "0.9.29", + "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.29.tgz", + "integrity": "sha512-G90x0VW+9nW4dFajtjCoT+NM0scAfH9Mb08IcjgFHYbfiL/lU04dTF9JuVOi3/OH+DJCQdcIseSXkdCB9Ky6JA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@algolia/abtesting": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.6.1.tgz", + "integrity": "sha512-wV/gNRkzb7sI9vs1OneG129hwe3Q5zPj7zigz3Ps7M5Lpo2hSorrOnXNodHEOV+yXE/ks4Pd+G3CDFIjFTWhMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.40.1", + "@algolia/requester-browser-xhr": "5.40.1", + "@algolia/requester-fetch": "5.40.1", + "@algolia/requester-node-http": "5.40.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-abtesting": { + "version": "5.40.1", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.40.1.tgz", + "integrity": "sha512-cxKNATPY5t+Mv8XAVTI57altkaPH+DZi4uMrnexPxPHODMljhGYY+GDZyHwv9a+8CbZHcY372OkxXrDMZA4Lnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.40.1", + "@algolia/requester-browser-xhr": "5.40.1", + "@algolia/requester-fetch": "5.40.1", + "@algolia/requester-node-http": "5.40.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "5.40.1", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.40.1.tgz", + "integrity": "sha512-XP008aMffJCRGAY8/70t+hyEyvqqV7YKm502VPu0+Ji30oefrTn2al7LXkITz7CK6I4eYXWRhN6NaIUi65F1OA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.40.1", + "@algolia/requester-browser-xhr": "5.40.1", + "@algolia/requester-fetch": "5.40.1", + "@algolia/requester-node-http": "5.40.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-common": { + "version": "5.40.1", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.40.1.tgz", + "integrity": "sha512-gWfQuQUBtzUboJv/apVGZMoxSaB0M4Imwl1c9Ap+HpCW7V0KhjBddqF2QQt5tJZCOFsfNIgBbZDGsEPaeKUosw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-insights": { + "version": "5.40.1", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.40.1.tgz", + "integrity": "sha512-RTLjST/t+lsLMouQ4zeLJq2Ss+UNkLGyNVu+yWHanx6kQ3LT5jv8UvPwyht9s7R6jCPnlSI77WnL80J32ZuyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.40.1", + "@algolia/requester-browser-xhr": "5.40.1", + "@algolia/requester-fetch": "5.40.1", + "@algolia/requester-node-http": "5.40.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "5.40.1", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.40.1.tgz", + "integrity": "sha512-2FEK6bUomBzEYkTKzD0iRs7Ljtjb45rKK/VSkyHqeJnG+77qx557IeSO0qVFE3SfzapNcoytTofnZum0BQ6r3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.40.1", + "@algolia/requester-browser-xhr": "5.40.1", + "@algolia/requester-fetch": "5.40.1", + "@algolia/requester-node-http": "5.40.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-query-suggestions": { + "version": "5.40.1", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.40.1.tgz", + "integrity": "sha512-Nju4NtxAvXjrV2hHZNLKVJLXjOlW6jAXHef/CwNzk1b2qIrCWDO589ELi5ZHH1uiWYoYyBXDQTtHmhaOVVoyXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.40.1", + "@algolia/requester-browser-xhr": "5.40.1", + "@algolia/requester-fetch": "5.40.1", + "@algolia/requester-node-http": "5.40.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-search": { + "version": "5.40.1", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.40.1.tgz", + "integrity": "sha512-Mw6pAUF121MfngQtcUb5quZVqMC68pSYYjCRZkSITC085S3zdk+h/g7i6FxnVdbSU6OztxikSDMh1r7Z+4iPlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.40.1", + "@algolia/requester-browser-xhr": "5.40.1", + "@algolia/requester-fetch": "5.40.1", + "@algolia/requester-node-http": "5.40.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/ingestion": { + "version": "1.40.1", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.40.1.tgz", + "integrity": "sha512-z+BPlhs45VURKJIxsR99NNBWpUEEqIgwt10v/fATlNxc4UlXvALdOsWzaFfe89/lbP5Bu4+mbO59nqBC87ZM/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.40.1", + "@algolia/requester-browser-xhr": "5.40.1", + "@algolia/requester-fetch": "5.40.1", + "@algolia/requester-node-http": "5.40.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/monitoring": { + "version": "1.40.1", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.40.1.tgz", + "integrity": "sha512-VJMUMbO0wD8Rd2VVV/nlFtLJsOAQvjnVNGkMkspFiFhpBA7s/xJOb+fJvvqwKFUjbKTUA7DjiSi1ljSMYBasXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.40.1", + "@algolia/requester-browser-xhr": "5.40.1", + "@algolia/requester-fetch": "5.40.1", + "@algolia/requester-node-http": "5.40.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/recommend": { + "version": "5.40.1", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.40.1.tgz", + "integrity": "sha512-ehvJLadKVwTp9Scg9NfzVSlBKH34KoWOQNTaN8i1Ac64AnO6iH2apJVSP6GOxssaghZ/s8mFQsDH3QIZoluFHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.40.1", + "@algolia/requester-browser-xhr": "5.40.1", + "@algolia/requester-fetch": "5.40.1", + "@algolia/requester-node-http": "5.40.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "5.40.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.40.1.tgz", + "integrity": "sha512-PbidVsPurUSQIr6X9/7s34mgOMdJnn0i6p+N6Ab+lsNhY5eiu+S33kZEpZwkITYBCIbhzDLOvb7xZD3gDi+USA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.40.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-fetch": { + "version": "5.40.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.40.1.tgz", + "integrity": "sha512-ThZ5j6uOZCF11fMw9IBkhigjOYdXGXQpj6h4k+T9UkZrF2RlKcPynFzDeRgaLdpYk8Yn3/MnFbwUmib7yxj5Lw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.40.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-node-http": { + "version": "5.40.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.40.1.tgz", + "integrity": "sha512-H1gYPojO6krWHnUXu/T44DrEun/Wl95PJzMXRcM/szstNQczSbwq6wIFJPI9nyE95tarZfUNU3rgorT+wZ6iCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.40.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-devkit/architect": { + "version": "0.2100.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2100.3.tgz", + "integrity": "sha512-PcruWF0+IxXOTZd9MN/3y4A5aTfblALzT/+zWym26PtisaBgWQ3tRPQsf/CgT8EdmZl8eUOAWlNBSkbUj/S/lQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "21.0.3", + "rxjs": "7.8.2" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/core": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.0.3.tgz", + "integrity": "sha512-X1y3GMYru9+Vt7vz+R8SFAEmDtgf0aZ+1JOpiE7ubHsQOnhA++Pb94HBjQ6CHqlUhQli/XPOBksKNdZkpup8rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.3", + "rxjs": "7.8.2", + "source-map": "0.7.6" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/schematics": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.0.3.tgz", + "integrity": "sha512-E/Nja+RIyMzjqLXREOnTRwv7GMrycpAD7kGwDg7l8cWrNQ7phqBZcXAt74Jv9K9aYsOC8tw2Ms9t59aQ6iow8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "21.0.3", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.19", + "ora": "9.0.0", + "rxjs": "7.8.2" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/build": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-21.0.3.tgz", + "integrity": "sha512-3h2s0Igruei1RB/Hmu7nwbKvjJQ2ykNaiicXYuS2muWUBhDg+lm0QsGTGXrQV2BD0M9YdHU4Byh9upiZgMYpjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "2.3.0", + "@angular-devkit/architect": "0.2100.3", + "@babel/core": "7.28.4", + "@babel/helper-annotate-as-pure": "7.27.3", + "@babel/helper-split-export-declaration": "7.24.7", + "@inquirer/confirm": "5.1.19", + "@vitejs/plugin-basic-ssl": "2.1.0", + "beasties": "0.3.5", + "browserslist": "^4.26.0", + "esbuild": "0.26.0", + "https-proxy-agent": "7.0.6", + "istanbul-lib-instrument": "6.0.3", + "jsonc-parser": "3.3.1", + "listr2": "9.0.5", + "magic-string": "0.30.19", + "mrmime": "2.0.1", + "parse5-html-rewriting-stream": "8.0.0", + "picomatch": "4.0.3", + "piscina": "5.1.3", + "rolldown": "1.0.0-beta.47", + "sass": "1.93.2", + "semver": "7.7.3", + "source-map-support": "0.5.21", + "tinyglobby": "0.2.15", + "undici": "7.16.0", + "vite": "7.2.2", + "watchpack": "2.4.4" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "optionalDependencies": { + "lmdb": "3.4.3" + }, + "peerDependencies": { + "@angular/compiler": "^21.0.0", + "@angular/compiler-cli": "^21.0.0", + "@angular/core": "^21.0.0", + "@angular/localize": "^21.0.0", + "@angular/platform-browser": "^21.0.0", + "@angular/platform-server": "^21.0.0", + "@angular/service-worker": "^21.0.0", + "@angular/ssr": "^21.0.3", + "karma": "^6.4.0", + "less": "^4.2.0", + "ng-packagr": "^21.0.0", + "postcss": "^8.4.0", + "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0", + "tslib": "^2.3.0", + "typescript": ">=5.9 <6.0", + "vitest": "^4.0.8" + }, + "peerDependenciesMeta": { + "@angular/core": { + "optional": true + }, + "@angular/localize": { + "optional": true + }, + "@angular/platform-browser": { + "optional": true + }, + "@angular/platform-server": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "@angular/ssr": { + "optional": true + }, + "karma": { + "optional": true + }, + "less": { + "optional": true + }, + "ng-packagr": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tailwindcss": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/@angular/cdk": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-21.0.0.tgz", + "integrity": "sha512-wCr5D3mEC+p69IMDC7vf8bWx18mfUNNRdsiK3XD0m1PqfeNfnCJb+Bnkks37MC/SU01uCNrAokRaTbWL6pk1Wg==", + "license": "MIT", + "dependencies": { + "parse5": "^8.0.0", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": "^21.0.0 || ^22.0.0", + "@angular/core": "^21.0.0 || ^22.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/cli": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-21.0.3.tgz", + "integrity": "sha512-3lMR3J231JhLgAt37yEULSHFte3zPeta9VYpIIf92JiBsTnWrvKnaK8RXhfdiSQrvhqQ9FMQdl5AG62r1c4dbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/architect": "0.2100.3", + "@angular-devkit/core": "21.0.3", + "@angular-devkit/schematics": "21.0.3", + "@inquirer/prompts": "7.9.0", + "@listr2/prompt-adapter-inquirer": "3.0.5", + "@modelcontextprotocol/sdk": "1.24.0", + "@schematics/angular": "21.0.3", + "@yarnpkg/lockfile": "1.1.0", + "algoliasearch": "5.40.1", + "ini": "5.0.0", + "jsonc-parser": "3.3.1", + "listr2": "9.0.5", + "npm-package-arg": "13.0.1", + "pacote": "21.0.3", + "parse5-html-rewriting-stream": "8.0.0", + "resolve": "1.22.11", + "semver": "7.7.3", + "yargs": "18.0.0", + "zod": "4.1.13" + }, + "bin": { + "ng": "bin/ng.js" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/common": { + "version": "21.0.6", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.0.6.tgz", + "integrity": "sha512-Yd8PF0dR37FAzqEcBHAyVCiSGMJOezSJe6rV/4BC6AVLfaZ7oZLl8CNVxKsod2UHd6rKxt1hzx05QdVcVvYNeA==", + "license": "MIT", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/core": "21.0.6", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/compiler": { + "version": "21.0.6", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.0.6.tgz", + "integrity": "sha512-rBMzG7WnQMouFfDST+daNSAOVYdtw560645PhlxyVeIeHMlCm0j1jjBgVPGTBNpVgKRdT/sqbi6W6JYkY9mERA==", + "license": "MIT", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@angular/compiler-cli": { + "version": "21.0.6", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-21.0.6.tgz", + "integrity": "sha512-UcIUx+fbn0VLlCBCIYxntAzWG3zPRUo0K7wvuK0MC6ZFCWawgewx9SdLLZTqcaWe1g5FRQlQeVQcFgHAO5R2Mw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "7.28.4", + "@jridgewell/sourcemap-codec": "^1.4.14", + "chokidar": "^4.0.0", + "convert-source-map": "^1.5.1", + "reflect-metadata": "^0.2.0", + "semver": "^7.0.0", + "tslib": "^2.3.0", + "yargs": "^18.0.0" + }, + "bin": { + "ng-xi18n": "bundles/src/bin/ng_xi18n.js", + "ngc": "bundles/src/bin/ngc.js" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/compiler": "21.0.6", + "typescript": ">=5.9 <6.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@angular/core": { + "version": "21.0.6", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.0.6.tgz", + "integrity": "sha512-SvWbOkkrsqprYJSBmzQEWkWjfZB/jkRYyFp2ClMJBPqOLxP1a+i3Om2rolcNQjZPz87bs9FszwgRlXUy7sw5cQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/compiler": "21.0.6", + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.15.0 || ~0.16.0" + }, + "peerDependenciesMeta": { + "@angular/compiler": { + "optional": true + }, + "zone.js": { + "optional": true + } + } + }, + "node_modules/@angular/forms": { + "version": "21.0.6", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-21.0.6.tgz", + "integrity": "sha512-aAkAAKuUrP8U7R4aH/HbmG/CXP90GlML77ECBI5b4qCSb+bvaTEYsaf85mCyTpr9jvGkia2LTe42hPcOuyzdsQ==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/common": "21.0.6", + "@angular/core": "21.0.6", + "@angular/platform-browser": "21.0.6", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/platform-browser": { + "version": "21.0.6", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.0.6.tgz", + "integrity": "sha512-tPk8rlUEBPXIUPRYq6Xu7QhJgKtnVr0dOHHuhyi70biKTupr5VikpZC5X9dy2Q3H3zYbK6MHC6384YMuwfU2kg==", + "license": "MIT", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/animations": "21.0.6", + "@angular/common": "21.0.6", + "@angular/core": "21.0.6" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/router": { + "version": "21.0.6", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-21.0.6.tgz", + "integrity": "sha512-HOfomKq7jRSgxt/uUvpdbB8RNaYuGB/FJQ3BfQCFfGw1O9L3B72b7Hilk6AcjCruul6cfv/kmT4EB6Vqi3dQtA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/common": "21.0.6", + "@angular/core": "21.0.6", + "@angular/platform-browser": "21.0.6", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.1.tgz", + "integrity": "sha512-B0Hv6G3gWGMn0xKJ0txEi/jM5iFpT3MfDxmhZFb4W047GvytCf1DHQ1D69W3zHI4yWe2aTZAA0JnbMZ7Xc8DuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.4", + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "lru-cache": "^11.2.4" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "6.7.6", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.7.6.tgz", + "integrity": "sha512-hBaJER6A9MpdG3WgdlOolHmbOYvSk46y7IQN/1+iqiCuUu6iWdQrs9DGKF8ocqsEqWujWf/V7b7vaDgiUmIvUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.1.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.4" + } + }, + "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.0.21", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.21.tgz", + "integrity": "sha512-plP8N8zKfEZ26figX4Nvajx8DuzfuRpLTqglQ5d0chfnt35Qt3X+m6ASZ+rG0D0kxe/upDVNwSIVJP5n4FuNfw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@emnapi/core": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", + "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.26.0.tgz", + "integrity": "sha512-hj0sKNCQOOo2fgyII3clmJXP28VhgDfU5iy3GNHlWO76KG6N7x4D9ezH5lJtQTG+1J6MFDAJXC1qsI+W+LvZoA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.26.0.tgz", + "integrity": "sha512-C0hkDsYNHZkBtPxxDx177JN90/1MiCpvBNjz1f5yWJo1+5+c5zr8apjastpEG+wtPjo9FFtGG7owSsAxyKiHxA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.26.0.tgz", + "integrity": "sha512-DDnoJ5eoa13L8zPh87PUlRd/IyFaIKOlRbxiwcSbeumcJ7UZKdtuMCHa1Q27LWQggug6W4m28i4/O2qiQQ5NZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.26.0.tgz", + "integrity": "sha512-bKDkGXGZnj0T70cRpgmv549x38Vr2O3UWLbjT2qmIkdIWcmlg8yebcFWoT9Dku7b5OV3UqPEuNKRzlNhjwUJ9A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.26.0.tgz", + "integrity": "sha512-6Z3naJgOuAIB0RLlJkYc81An3rTlQ/IeRdrU3dOea8h/PvZSgitZV+thNuIccw0MuK1GmIAnAmd5TrMZad8FTQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.26.0.tgz", + "integrity": "sha512-OPnYj0zpYW0tHusMefyaMvNYQX5pNQuSsHFTHUBNp3vVXupwqpxofcjVsUx11CQhGVkGeXjC3WLjh91hgBG2xw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.26.0.tgz", + "integrity": "sha512-jix2fa6GQeZhO1sCKNaNMjfj5hbOvoL2F5t+w6gEPxALumkpOV/wq7oUBMHBn2hY2dOm+mEV/K+xfZy3mrsxNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.26.0.tgz", + "integrity": "sha512-tccJaH5xHJD/239LjbVvJwf6T4kSzbk6wPFerF0uwWlkw/u7HL+wnAzAH5GB2irGhYemDgiNTp8wJzhAHQ64oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.26.0.tgz", + "integrity": "sha512-JY8NyU31SyRmRpuc5W8PQarAx4TvuYbyxbPIpHAZdr/0g4iBr8KwQBS4kiiamGl2f42BBecHusYCsyxi7Kn8UQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.26.0.tgz", + "integrity": "sha512-IMJYN7FSkLttYyTbsbme0Ra14cBO5z47kpamo16IwggzzATFY2lcZAwkbcNkWiAduKrTgFJP7fW5cBI7FzcuNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.26.0.tgz", + "integrity": "sha512-XITaGqGVLgk8WOHw8We9Z1L0lbLFip8LyQzKYFKO4zFo1PFaaSKsbNjvkb7O8kEXytmSGRkYpE8LLVpPJpsSlw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.26.0.tgz", + "integrity": "sha512-MkggfbDIczStUJwq9wU7gQ7kO33d8j9lWuOCDifN9t47+PeI+9m2QVh51EI/zZQ1spZtFMC1nzBJ+qNGCjJnsg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.26.0.tgz", + "integrity": "sha512-fUYup12HZWAeccNLhQ5HwNBPr4zXCPgUWzEq2Rfw7UwqwfQrFZ0SR/JljaURR8xIh9t+o1lNUFTECUTmaP7yKA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.26.0.tgz", + "integrity": "sha512-MzRKhM0Ip+//VYwC8tialCiwUQ4G65WfALtJEFyU0GKJzfTYoPBw5XNWf0SLbCUYQbxTKamlVwPmcw4DgZzFxg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.26.0.tgz", + "integrity": "sha512-QhCc32CwI1I4Jrg1enCv292sm3YJprW8WHHlyxJhae/dVs+KRWkbvz2Nynl5HmZDW/m9ZxrXayHzjzVNvQMGQA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.26.0.tgz", + "integrity": "sha512-1D6vi6lfI18aNT1aTf2HV+RIlm6fxtlAp8eOJ4mmnbYmZ4boz8zYDar86sIYNh0wmiLJEbW/EocaKAX6Yso2fw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.26.0.tgz", + "integrity": "sha512-rnDcepj7LjrKFvZkx+WrBv6wECeYACcFjdNPvVPojCPJD8nHpb3pv3AuR9CXgdnjH1O23btICj0rsp0L9wAnHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.26.0.tgz", + "integrity": "sha512-FSWmgGp0mDNjEXXFcsf12BmVrb+sZBBBlyh3LwB/B9ac3Kkc8x5D2WimYW9N7SUkolui8JzVnVlWh7ZmjCpnxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.26.0.tgz", + "integrity": "sha512-0QfciUDFryD39QoSPUDshj4uNEjQhp73+3pbSAaxjV2qGOEDsM67P7KbJq7LzHoVl46oqhIhJ1S+skKGR7lMXA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.26.0.tgz", + "integrity": "sha512-vmAK+nHhIZWImwJ3RNw9hX3fU4UGN/OqbSE0imqljNbUQC3GvVJ1jpwYoTfD6mmXmQaxdJY6Hn4jQbLGJKg5Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.26.0.tgz", + "integrity": "sha512-GPXF7RMkJ7o9bTyUsnyNtrFMqgM3X+uM/LWw4CeHIjqc32fm0Ir6jKDnWHpj8xHFstgWDUYseSABK9KCkHGnpg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.26.0.tgz", + "integrity": "sha512-nUHZ5jEYqbBthbiBksbmHTlbb5eElyVfs/s1iHQ8rLBq1eWsd5maOnDpCocw1OM8kFK747d1Xms8dXJHtduxSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.26.0.tgz", + "integrity": "sha512-TMg3KCTCYYaVO+R6P5mSORhcNDDlemUVnUbb8QkboUtOhb5JWKAzd5uMIMECJQOxHZ/R+N8HHtDF5ylzLfMiLw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.26.0.tgz", + "integrity": "sha512-apqYgoAUd6ZCb9Phcs8zN32q6l0ZQzQBdVXOofa6WvHDlSOhwCWgSfVQabGViThS40Y1NA4SCvQickgZMFZRlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.26.0.tgz", + "integrity": "sha512-FGJAcImbJNZzLWu7U6WB0iKHl4RuY4TsXEwxJPl9UZLS47agIZuILZEX3Pagfw7I4J3ddflomt9f0apfaJSbaw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.26.0.tgz", + "integrity": "sha512-WAckBKaVnmFqbEhbymrPK7M086DQMpL1XoRbpmN0iW8k5JSXjDRQBhcZNa0VweItknLq9eAeCL34jK7/CDcw7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", + "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.19", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.19.tgz", + "integrity": "sha512-wQNz9cfcxrtEnUyG5PndC8g3gZ7lGDBzmWiXZkX8ot3vfZ+/BLjR8EvyGX4YzQLeVqtAlY/YScZpW7CW8qMoDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/external-editor": "^1.0.3", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", + "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", + "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", + "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", + "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.9.0.tgz", + "integrity": "sha512-X7/+dG9SLpSzRkwgG5/xiIzW0oMrV3C0HOa7YHG1WnrLK+vCQHfte4k/T80059YBdei29RBC3s+pSMvPJDU9/A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@inquirer/checkbox": "^4.3.0", + "@inquirer/confirm": "^5.1.19", + "@inquirer/editor": "^4.2.21", + "@inquirer/expand": "^4.0.21", + "@inquirer/input": "^4.2.5", + "@inquirer/number": "^3.0.21", + "@inquirer/password": "^4.0.21", + "@inquirer/rawlist": "^4.1.9", + "@inquirer/search": "^3.2.0", + "@inquirer/select": "^4.4.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", + "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", + "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", + "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@listr2/prompt-adapter-inquirer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-3.0.5.tgz", + "integrity": "sha512-WELs+hj6xcilkloBXYf9XXK8tYEnKsgLj01Xl5ONUJpKjmT5hGVUzNUS5tooUxs7pGMrw+jFD/41WpqW4V3LDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/type": "^3.0.8" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@inquirer/prompts": ">= 3 < 8", + "listr2": "9.0.5" + } + }, + "node_modules/@lmdb/lmdb-darwin-arm64": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.4.3.tgz", + "integrity": "sha512-zR6Y45VNtW5s+A+4AyhrJk0VJKhXdkLhrySCpCu7PSdnakebsOzNxf58p5Xoq66vOSuueGAxlqDAF49HwdrSTQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-darwin-x64": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.4.3.tgz", + "integrity": "sha512-nfGm5pQksBGfaj9uMbjC0YyQreny/Pl7mIDtHtw6g7WQuCgeLullr9FNRsYyKplaEJBPrCVpEjpAznxTBIrXBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.4.3.tgz", + "integrity": "sha512-Kjqomp7i0rgSbYSUmv9JnXpS55zYT/YcW3Bdf9oqOTjcH0/8tFAP8MLhu/i9V2pMKIURDZk63Ww49DTK0T3c/Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm64": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.4.3.tgz", + "integrity": "sha512-uX9eaPqWb740wg5D3TCvU/js23lSRSKT7lJrrQ8IuEG/VLgpPlxO3lHDywU44yFYdGS7pElBn6ioKFKhvALZlw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-x64": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.4.3.tgz", + "integrity": "sha512-7/8l20D55CfwdMupkc3fNxNJdn4bHsti2X0cp6PwiXlLeSFvAfWs5kCCx+2Cyje4l4GtN//LtKWjTru/9hDJQg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-win32-arm64": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-arm64/-/lmdb-win32-arm64-3.4.3.tgz", + "integrity": "sha512-yWVR0e5Gl35EGJBsAuqPOdjtUYuN8CcTLKrqpQFoM+KsMadViVCulhKNhkcjSGJB88Am5bRPjMro4MBB9FS23Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@lmdb/lmdb-win32-x64": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.4.3.tgz", + "integrity": "sha512-1JdBkcO0Vrua4LUgr4jAe4FUyluwCeq/pDkBrlaVjX3/BBWP1TzVjCL+TibWNQtPAL1BITXPAhlK5Ru4FBd/hg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.24.0.tgz", + "integrity": "sha512-D8h5KXY2vHFW8zTuxn2vuZGN0HGrQ5No6LkHwlEA9trVgNdPL3TF1dSqKA7Dny6BbBYKSW/rOBDXdC8KJAjUCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.0.1", + "express-rate-limit": "^7.5.0", + "jose": "^6.1.1", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@napi-rs/nice": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.1.1.tgz", + "integrity": "sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/nice-android-arm-eabi": "1.1.1", + "@napi-rs/nice-android-arm64": "1.1.1", + "@napi-rs/nice-darwin-arm64": "1.1.1", + "@napi-rs/nice-darwin-x64": "1.1.1", + "@napi-rs/nice-freebsd-x64": "1.1.1", + "@napi-rs/nice-linux-arm-gnueabihf": "1.1.1", + "@napi-rs/nice-linux-arm64-gnu": "1.1.1", + "@napi-rs/nice-linux-arm64-musl": "1.1.1", + "@napi-rs/nice-linux-ppc64-gnu": "1.1.1", + "@napi-rs/nice-linux-riscv64-gnu": "1.1.1", + "@napi-rs/nice-linux-s390x-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-musl": "1.1.1", + "@napi-rs/nice-openharmony-arm64": "1.1.1", + "@napi-rs/nice-win32-arm64-msvc": "1.1.1", + "@napi-rs/nice-win32-ia32-msvc": "1.1.1", + "@napi-rs/nice-win32-x64-msvc": "1.1.1" + } + }, + "node_modules/@napi-rs/nice-android-arm-eabi": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.1.1.tgz", + "integrity": "sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-android-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.1.1.tgz", + "integrity": "sha512-blG0i7dXgbInN5urONoUCNf+DUEAavRffrO7fZSeoRMJc5qD+BJeNcpr54msPF6qfDD6kzs9AQJogZvT2KD5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.1.1.tgz", + "integrity": "sha512-s/E7w45NaLqTGuOjC2p96pct4jRfo61xb9bU1unM/MJ/RFkKlJyJDx7OJI/O0ll/hrfpqKopuAFDV8yo0hfT7A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-x64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.1.1.tgz", + "integrity": "sha512-dGoEBnVpsdcC+oHHmW1LRK5eiyzLwdgNQq3BmZIav+9/5WTZwBYX7r5ZkQC07Nxd3KHOCkgbHSh4wPkH1N1LiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-freebsd-x64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.1.1.tgz", + "integrity": "sha512-kHv4kEHAylMYmlNwcQcDtXjklYp4FCf0b05E+0h6nDHsZ+F0bDe04U/tXNOqrx5CmIAth4vwfkjjUmp4c4JktQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm-gnueabihf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.1.1.tgz", + "integrity": "sha512-E1t7K0efyKXZDoZg1LzCOLxgolxV58HCkaEkEvIYQx12ht2pa8hoBo+4OB3qh7e+QiBlp1SRf+voWUZFxyhyqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.1.1.tgz", + "integrity": "sha512-CIKLA12DTIZlmTaaKhQP88R3Xao+gyJxNWEn04wZwC2wmRapNnxCUZkVwggInMJvtVElA+D4ZzOU5sX4jV+SmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-musl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.1.1.tgz", + "integrity": "sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-ppc64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.1.1.tgz", + "integrity": "sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-riscv64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.1.1.tgz", + "integrity": "sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-s390x-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.1.1.tgz", + "integrity": "sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.1.1.tgz", + "integrity": "sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-musl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.1.1.tgz", + "integrity": "sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-openharmony-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-openharmony-arm64/-/nice-openharmony-arm64-1.1.1.tgz", + "integrity": "sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-arm64-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.1.1.tgz", + "integrity": "sha512-uoTb4eAvM5B2aj/z8j+Nv8OttPf2m+HVx3UjA5jcFxASvNhQriyCQF1OB1lHL43ZhW+VwZlgvjmP5qF3+59atA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-ia32-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.1.1.tgz", + "integrity": "sha512-CNQqlQT9MwuCsg1Vd/oKXiuH+TcsSPJmlAFc5frFyX/KkOh0UpBLEj7aoY656d5UKZQMQFP7vJNa1DNUNORvug==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-x64-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.1.1.tgz", + "integrity": "sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.0.tgz", + "integrity": "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + } + }, + "node_modules/@npmcli/agent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-4.0.0.tgz", + "integrity": "sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==", + "dev": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^11.2.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/agent/node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@npmcli/fs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-5.0.0.tgz", + "integrity": "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==", + "dev": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/git": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-7.0.1.tgz", + "integrity": "sha512-+XTFxK2jJF/EJJ5SoAzXk3qwIDfvFc5/g+bD274LZ7uY7LE8sTfG6Z8rOanPl2ZEvZWqNvmEdtXC25cE54VcoA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^9.0.0", + "ini": "^6.0.0", + "lru-cache": "^11.2.1", + "npm-pick-manifest": "^11.0.1", + "proc-log": "^6.0.0", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/git/node_modules/@npmcli/promise-spawn": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz", + "integrity": "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "which": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/git/node_modules/ini": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", + "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/git/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/@npmcli/git/node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@npmcli/git/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/git/node_modules/which": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", + "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-3.0.0.tgz", + "integrity": "sha512-fkxoPuFGvxyrH+OQzyTkX2LUEamrF4jZSmxjAtPPHHGO0dqsQ8tTKjnIS8SAnPHdk2I03BDtSMR5K/4loKg79Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-5.0.0.tgz", + "integrity": "sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/package-json": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-7.0.4.tgz", + "integrity": "sha512-0wInJG3j/K40OJt/33ax47WfWMzZTm6OQxB9cDhTt5huCP2a9g2GnlsxmfN+PulItNPIpPrZ+kfwwUil7eHcZQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^7.0.0", + "glob": "^13.0.0", + "hosted-git-info": "^9.0.0", + "json-parse-even-better-errors": "^5.0.0", + "proc-log": "^6.0.0", + "semver": "^7.5.3", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/package-json/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-8.0.3.tgz", + "integrity": "sha512-Yb00SWaL4F8w+K8YGhQ55+xE4RUNdMHV43WZGsiTM92gS+lC0mGsn7I4hLug7pbao035S6bj3Y3w0cUNGLfmkg==", + "dev": true, + "license": "ISC", + "dependencies": { + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/redact": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-4.0.0.tgz", + "integrity": "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-10.0.3.tgz", + "integrity": "sha512-ER2N6itRkzWbbtVmZ9WKaWxVlKlOeBFF1/7xx+KA5J1xKa4JjUwBdb6tDpk0v1qA+d+VDwHI9qmLcXSWcmi+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^5.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "node-gyp": "^12.1.0", + "proc-log": "^6.0.0", + "which": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/@npmcli/promise-spawn": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz", + "integrity": "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "which": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/@npmcli/run-script/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/which": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", + "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.96.0.tgz", + "integrity": "sha512-r/xkmoXA0xEpU6UGtn18CNVjXH6erU3KCpCDbpLmbVxBFor1U9MqN5Z2uMmCHJuXjJzlnDR+hWY+yPoLo8oHDw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/@parcel/watcher/node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-beta.47.tgz", + "integrity": "sha512-vPP9/MZzESh9QtmvQYojXP/midjgkkc1E4AdnPPAzQXo668ncHJcVLKjJKzoBdsQmaIvNjrMdsCwES8vTQHRQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-beta.47.tgz", + "integrity": "sha512-Lc3nrkxeaDVCVl8qR3qoxh6ltDZfkQ98j5vwIr5ALPkgjZtDK4BGCrrBoLpGVMg+csWcaqUbwbKwH5yvVa0oOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-beta.47.tgz", + "integrity": "sha512-eBYxQDwP0O33plqNVqOtUHqRiSYVneAknviM5XMawke3mwMuVlAsohtOqEjbCEl/Loi/FWdVeks5WkqAkzkYWQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-beta.47.tgz", + "integrity": "sha512-Ns+kgp2+1Iq/44bY/Z30DETUSiHY7ZuqaOgD5bHVW++8vme9rdiWsN4yG4rRPXkdgzjvQ9TDHmZZKfY4/G11AA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-beta.47.tgz", + "integrity": "sha512-4PecgWCJhTA2EFOlptYJiNyVP2MrVP4cWdndpOu3WmXqWqZUmSubhb4YUAIxAxnXATlGjC1WjxNPhV7ZllNgdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-beta.47.tgz", + "integrity": "sha512-CyIunZ6D9U9Xg94roQI1INt/bLkOpPsZjZZkiaAZ0r6uccQdICmC99M9RUPlMLw/qg4yEWLlQhG73W/mG437NA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-beta.47.tgz", + "integrity": "sha512-doozc/Goe7qRCSnzfJbFINTHsMktqmZQmweull6hsZZ9sjNWQ6BWQnbvOlfZJe4xE5NxM1NhPnY5Giqnl3ZrYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-beta.47.tgz", + "integrity": "sha512-fodvSMf6Aqwa0wEUSTPewmmZOD44rc5Tpr5p9NkwQ6W1SSpUKzD3SwpJIgANDOhwiYhDuiIaYPGB7Ujkx1q0UQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-beta.47.tgz", + "integrity": "sha512-Rxm5hYc0mGjwLh5sjlGmMygxAaV2gnsx7CNm2lsb47oyt5UQyPDZf3GP/ct8BEcwuikdqzsrrlIp8+kCSvMFNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-beta.47.tgz", + "integrity": "sha512-YakuVe+Gc87jjxazBL34hbr8RJpRuFBhun7NEqoChVDlH5FLhLXjAPHqZd990TVGVNkemourf817Z8u2fONS8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-beta.47.tgz", + "integrity": "sha512-ak2GvTFQz3UAOw8cuQq8pWE+TNygQB6O47rMhvevvTzETh7VkHRFtRUwJynX5hwzFvQMP6G0az5JrBGuwaMwYQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.0.7" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-beta.47.tgz", + "integrity": "sha512-o5BpmBnXU+Cj+9+ndMcdKjhZlPb79dVPBZnWwMnI4RlNSSq5yOvFZqvfPYbyacvnW03Na4n5XXQAPhu3RydZ0w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-ia32-msvc": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.0.0-beta.47.tgz", + "integrity": "sha512-FVOmfyYehNE92IfC9Kgs913UerDog2M1m+FADJypKz0gmRg3UyTt4o1cZMCAl7MiR89JpM9jegNO1nXuP1w1vw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-beta.47.tgz", + "integrity": "sha512-by/70F13IUE101Bat0oeH8miwWX5mhMFPk1yjCdxoTNHTyTdLgb0THNaebRM6AP7Kz+O3O2qx87sruYuF5UxHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.47.tgz", + "integrity": "sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.5.tgz", + "integrity": "sha512-iDGS/h7D8t7tvZ1t6+WPK04KD0MwzLZrG0se1hzBjSi5fyxlsiggoJHwh18PCFNn7tG43OWb6pdZ6Y+rMlmyNQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.5.tgz", + "integrity": "sha512-wrSAViWvZHBMMlWk6EJhvg8/rjxzyEhEdgfMMjREHEq11EtJ6IP6yfcCH57YAEca2Oe3FNCE9DSTgU70EIGmVw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.5.tgz", + "integrity": "sha512-S87zZPBmRO6u1YXQLwpveZm4JfPpAa6oHBX7/ghSiGH3rz/KDgAu1rKdGutV+WUI6tKDMbaBJomhnT30Y2t4VQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.5.tgz", + "integrity": "sha512-YTbnsAaHo6VrAczISxgpTva8EkfQus0VPEVJCEaboHtZRIb6h6j0BNxRBOwnDciFTZLDPW5r+ZBmhL/+YpTZgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.5.tgz", + "integrity": "sha512-1T8eY2J8rKJWzaznV7zedfdhD1BqVs1iqILhmHDq/bqCUZsrMt+j8VCTHhP0vdfbHK3e1IQ7VYx3jlKqwlf+vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.5.tgz", + "integrity": "sha512-sHTiuXyBJApxRn+VFMaw1U+Qsz4kcNlxQ742snICYPrY+DDL8/ZbaC4DVIB7vgZmp3jiDaKA0WpBdP0aqPJoBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.5.tgz", + "integrity": "sha512-dV3T9MyAf0w8zPVLVBptVlzaXxka6xg1f16VAQmjg+4KMSTWDvhimI/Y6mp8oHwNrmnmVl9XxJ/w/mO4uIQONA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.5.tgz", + "integrity": "sha512-wIGYC1x/hyjP+KAu9+ewDI+fi5XSNiUi9Bvg6KGAh2TsNMA3tSEs+Sh6jJ/r4BV/bx/CyWu2ue9kDnIdRyafcQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.5.tgz", + "integrity": "sha512-Y+qVA0D9d0y2FRNiG9oM3Hut/DgODZbU9I8pLLPwAsU0tUKZ49cyV1tzmB/qRbSzGvY8lpgGkJuMyuhH7Ma+Vg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.5.tgz", + "integrity": "sha512-juaC4bEgJsyFVfqhtGLz8mbopaWD+WeSOYr5E16y+1of6KQjc0BpwZLuxkClqY1i8sco+MdyoXPNiCkQou09+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.5.tgz", + "integrity": "sha512-rIEC0hZ17A42iXtHX+EPJVL/CakHo+tT7W0pbzdAGuWOt2jxDFh7A/lRhsNHBcqL4T36+UiAgwO8pbmn3dE8wA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.5.tgz", + "integrity": "sha512-T7l409NhUE552RcAOcmJHj3xyZ2h7vMWzcwQI0hvn5tqHh3oSoclf9WgTl+0QqffWFG8MEVZZP1/OBglKZx52Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.5.tgz", + "integrity": "sha512-7OK5/GhxbnrMcxIFoYfhV/TkknarkYC1hqUw1wU2xUN3TVRLNT5FmBv4KkheSG2xZ6IEbRAhTooTV2+R5Tk0lQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.5.tgz", + "integrity": "sha512-GwuDBE/PsXaTa76lO5eLJTyr2k8QkPipAyOrs4V/KJufHCZBJ495VCGJol35grx9xryk4V+2zd3Ri+3v7NPh+w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.5.tgz", + "integrity": "sha512-IAE1Ziyr1qNfnmiQLHBURAD+eh/zH1pIeJjeShleII7Vj8kyEm2PF77o+lf3WTHDpNJcu4IXJxNO0Zluro8bOw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.5.tgz", + "integrity": "sha512-Pg6E+oP7GvZ4XwgRJBuSXZjcqpIW3yCBhK4BcsANvb47qMvAbCjR6E+1a/U2WXz1JJxp9/4Dno3/iSJLcm5auw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.5.tgz", + "integrity": "sha512-txGtluxDKTxaMDzUduGP0wdfng24y1rygUMnmlUJ88fzCCULCLn7oE5kb2+tRB+MWq1QDZT6ObT5RrR8HFRKqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.5.tgz", + "integrity": "sha512-3DFiLPnTxiOQV993fMc+KO8zXHTcIjgaInrqlG8zDp1TlhYl6WgrOHuJkJQ6M8zHEcntSJsUp1XFZSY8C1DYbg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.5.tgz", + "integrity": "sha512-nggc/wPpNTgjGg75hu+Q/3i32R00Lq1B6N1DO7MCU340MRKL3WZJMjA9U4K4gzy3dkZPXm9E1Nc81FItBVGRlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.5.tgz", + "integrity": "sha512-U/54pTbdQpPLBdEzCT6NBCFAfSZMvmjr0twhnD9f4EIvlm9wy3jjQ38yQj1AGznrNO65EWQMgm/QUjuIVrYF9w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.5.tgz", + "integrity": "sha512-2NqKgZSuLH9SXBBV2dWNRCZmocgSOx8OJSdpRaEcRlIfX8YrKxUT6z0F1NpvDVhOsl190UFTRh2F2WDWWCYp3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.5.tgz", + "integrity": "sha512-JRpZUhCfhZ4keB5v0fe02gQJy05GqboPOaxvjugW04RLSYYoB/9t2lx2u/tMs/Na/1NXfY8QYjgRljRpN+MjTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@schematics/angular": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-21.0.3.tgz", + "integrity": "sha512-XYOI2WOz8B+ydJ8iUHRXrUyjTx+YGdCQ8b2FlXnU46ksIctVU+zt4Zgu6462xeaPwOFYw6+r+TvaBAZ14a82Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "21.0.3", + "@angular-devkit/schematics": "21.0.3", + "jsonc-parser": "3.3.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@sigstore/bundle": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-4.0.0.tgz", + "integrity": "sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.5.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/core": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-3.0.0.tgz", + "integrity": "sha512-NgbJ+aW9gQl/25+GIEGYcCyi8M+ng2/5X04BMuIgoDfgvp18vDcoNHOQjQsG9418HGNYRxG3vfEXaR1ayD37gg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/protobuf-specs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.5.0.tgz", + "integrity": "sha512-MM8XIwUjN2bwvCg1QvrMtbBmpcSHrkhFSCu1D11NyPvDQ25HEc4oG5/OcQfd/Tlf/OxmKWERDj0zGE23jQaMwA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@sigstore/sign": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-4.0.1.tgz", + "integrity": "sha512-KFNGy01gx9Y3IBPG/CergxR9RZpN43N+lt3EozEfeoyqm8vEiLxwRl3ZO5sPx3Obv1ix/p7FWOlPc2Jgwfp9PA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.0.0", + "@sigstore/protobuf-specs": "^0.5.0", + "make-fetch-happen": "^15.0.2", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/tuf": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.0.tgz", + "integrity": "sha512-0QFuWDHOQmz7t66gfpfNO6aEjoFrdhkJaej/AOqb4kqWZVbPWFZifXZzkxyQBB1OwTbkhdT3LNpMFxwkTvf+2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.5.0", + "tuf-js": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/verify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-3.0.0.tgz", + "integrity": "sha512-moXtHH33AobOhTZF8xcX1MpOFqdvfCk7v6+teJL8zymBiDXwEsQH6XG9HGx2VIxnJZNm4cNSzflTLDnQLmIdmw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.0.0", + "@sigstore/protobuf-specs": "^0.5.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", + "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-4.0.0.tgz", + "integrity": "sha512-h5x5ga/hh82COe+GoD4+gKUeV4T3iaYOxqLt41GRKApinPI7DMidhCmNVTjKfhCWFJIGXaFJee07XczdT4jdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@tufjs/models/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-basic-ssl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.1.0.tgz", + "integrity": "sha512-dOxxrhgyDIEUADhb/8OlV9JIqYLgos03YorAueTIeOUskLJSEsfwCByjbu98ctXitUN3znXKp0bYD/WHSudCeA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0" + } + }, + "node_modules/@vitest/expect": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.16.tgz", + "integrity": "sha512-eshqULT2It7McaJkQGLkPjPjNph+uevROGuIMJdG3V+0BSR2w9u6J9Lwu+E8cK5TETlfou8GRijhafIMhXsimA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.16", + "@vitest/utils": "4.0.16", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.16.tgz", + "integrity": "sha512-yb6k4AZxJTB+q9ycAvsoxGn+j/po0UaPgajllBgt1PzoMAAmJGYFdDk0uCcRcxb3BrME34I6u8gHZTQlkqSZpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.16", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/mocker/node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.16.tgz", + "integrity": "sha512-eNCYNsSty9xJKi/UdVD8Ou16alu7AYiS2fCPRs0b1OdhJiV89buAXQLpTbe+X8V9L6qrs9CqyvU7OaAopJYPsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.16.tgz", + "integrity": "sha512-VWEDm5Wv9xEo80ctjORcTQRJ539EGPB3Pb9ApvVRAY1U/WkHXmmYISqU5E79uCwcW7xYUV38gwZD+RV755fu3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.16", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.16.tgz", + "integrity": "sha512-sf6NcrYhYBsSYefxnry+DR8n3UV4xWZwWxYbCJUt2YdvtqzSPR7VfGrY0zsv090DAbjFZsi7ZaMi1KnSRyK1XA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.16", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot/node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/@vitest/spy": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.16.tgz", + "integrity": "sha512-4jIOWjKP0ZUaEmJm00E0cOBLU+5WE0BpeNr3XN6TEF05ltro6NJqHWxXD0kA8/Zc8Nh23AT8WQxwNG+WeROupw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.16.tgz", + "integrity": "sha512-h8z9yYhV3e1LEfaQ3zdypIrnAg/9hguReGZoS7Gl0aBG5xgA410zBqECqmaF/+RkTggRsfnzc1XaAHA6bmUufA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.16", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/abbrev": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/algoliasearch": { + "version": "5.40.1", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.40.1.tgz", + "integrity": "sha512-iUNxcXUNg9085TJx0HJLjqtDE0r1RZ0GOGrt8KNQqQT5ugu8lZsHuMUYW/e0lHhq6xBvmktU9Bw4CXP9VQeKrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/abtesting": "1.6.1", + "@algolia/client-abtesting": "5.40.1", + "@algolia/client-analytics": "5.40.1", + "@algolia/client-common": "5.40.1", + "@algolia/client-insights": "5.40.1", + "@algolia/client-personalization": "5.40.1", + "@algolia/client-query-suggestions": "5.40.1", + "@algolia/client-search": "5.40.1", + "@algolia/ingestion": "1.40.1", + "@algolia/monitoring": "1.40.1", + "@algolia/recommend": "5.40.1", + "@algolia/requester-browser-xhr": "5.40.1", + "@algolia/requester-fetch": "5.40.1", + "@algolia/requester-node-http": "5.40.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/ansi-escapes": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz", + "integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "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/baseline-browser-mapping": { + "version": "2.9.9", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.9.tgz", + "integrity": "sha512-V8fbOCSeOFvlDj7LLChUcqbZrdKD9RU/VR260piF1790vT0mfLSwGc/Qzxv3IqiTukOpNtItePa0HBpMAj7MDg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/beasties": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/beasties/-/beasties-0.3.5.tgz", + "integrity": "sha512-NaWu+f4YrJxEttJSm16AzMIFtVldCvaJ68b1L098KpqXmxt9xOLtKoLkKxb8ekhOrLqEJAbvT6n6SEvB/sac7A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "css-select": "^6.0.0", + "css-what": "^7.0.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "htmlparser2": "^10.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.49", + "postcss-media-query-parser": "^0.2.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/body-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", + "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.3.tgz", + "integrity": "sha512-3pUp4e8hv07k1QlijZu6Kn7c9+ZpWWk4j3F8N3xPuCExULobqJydKYOTj1FTq58srkJsXvO7LbGAH4C0ZU3WGw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^5.0.0", + "fs-minipass": "^3.0.0", + "glob": "^13.0.0", + "lru-cache": "^11.1.0", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^13.0.0", + "unique-filename": "^5.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/cacache/node_modules/ssri": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.0.tgz", + "integrity": "sha512-yizwGBpbCn4YomB2lzhZqrHLJoqFGXihNbib3ozhqF/cIp5ue+xSmOQrjNasEE62hFxsCcg/V/z23t4n8jMEng==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001760", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz", + "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chai": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.1.tgz", + "integrity": "sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.3.0.tgz", + "integrity": "sha512-/+40ljC3ONVnYIttjMWrlL51nItDAbBrq2upN8BPyvGU/2n5Oxw3tbNwORCaNuNqLJnxGqOfjUuhsv7l5Q4IsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.1.1.tgz", + "integrity": "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^7.1.0", + "string-width": "^8.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "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/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-6.0.0.tgz", + "integrity": "sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^7.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "nth-check": "^2.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-7.0.0.tgz", + "integrity": "sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssstyle": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.5.tgz", + "integrity": "sha512-GlsEptulso7Jg0VaOZ8BXQi3AkYM5BOJKEO/rjMidSCq70FkIC5y0eawrCXeYzxgt3OCf4Ls+eoxN+/05vN0Ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^4.1.1", + "@csstools/css-syntax-patches-for-csstree": "^1.0.21", + "css-tree": "^3.1.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/data-urls": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-6.0.0.tgz", + "integrity": "sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.267", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.26.0.tgz", + "integrity": "sha512-3Hq7jri+tRrVWha+ZeIVhl4qJRha/XjRNSopvTsOaCvfPHrflTYTcUFcEjMKdxofsXXsdc4zjg5NOTnL4Gl57Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.26.0", + "@esbuild/android-arm": "0.26.0", + "@esbuild/android-arm64": "0.26.0", + "@esbuild/android-x64": "0.26.0", + "@esbuild/darwin-arm64": "0.26.0", + "@esbuild/darwin-x64": "0.26.0", + "@esbuild/freebsd-arm64": "0.26.0", + "@esbuild/freebsd-x64": "0.26.0", + "@esbuild/linux-arm": "0.26.0", + "@esbuild/linux-arm64": "0.26.0", + "@esbuild/linux-ia32": "0.26.0", + "@esbuild/linux-loong64": "0.26.0", + "@esbuild/linux-mips64el": "0.26.0", + "@esbuild/linux-ppc64": "0.26.0", + "@esbuild/linux-riscv64": "0.26.0", + "@esbuild/linux-s390x": "0.26.0", + "@esbuild/linux-x64": "0.26.0", + "@esbuild/netbsd-arm64": "0.26.0", + "@esbuild/netbsd-x64": "0.26.0", + "@esbuild/openbsd-arm64": "0.26.0", + "@esbuild/openbsd-x64": "0.26.0", + "@esbuild/openharmony-arm64": "0.26.0", + "@esbuild/sunos-x64": "0.26.0", + "@esbuild/win32-arm64": "0.26.0", + "@esbuild/win32-ia32": "0.26.0", + "@esbuild/win32-x64": "0.26.0" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true, + "license": "MIT" + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", + "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-minipass": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", + "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "path-scurry": "^2.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", + "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/htmlparser2": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", + "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.1", + "entities": "^6.0.0" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", + "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ignore-walk": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-8.0.0.tgz", + "integrity": "sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minimatch": "^10.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/immutable": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz", + "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jose": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", + "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdom": { + "version": "27.3.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.3.0.tgz", + "integrity": "sha512-GtldT42B8+jefDUC4yUKAvsaOrH7PDHmZxZXNgF2xMmymjUbRYJvpAybZAKEmXDGTM0mCsz8duOa4vTm5AY2Kg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@acemir/cssom": "^0.9.28", + "@asamuzakjp/dom-selector": "^6.7.6", + "cssstyle": "^5.3.4", + "data-urls": "^6.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "parse5": "^8.0.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.1.0", + "ws": "^8.18.3", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-5.0.0.tgz", + "integrity": "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/listr2": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", + "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^5.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/listr2/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/lmdb": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.4.3.tgz", + "integrity": "sha512-GWV1kVi6uhrXWqe+3NXWO73OYe8fto6q8JMo0HOpk1vf8nEyFWgo4CSNJpIFzsOxOrysVUlcO48qRbQfmKd1gA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "msgpackr": "^1.11.2", + "node-addon-api": "^6.1.0", + "node-gyp-build-optional-packages": "5.2.2", + "ordered-binary": "^1.5.3", + "weak-lru-cache": "^1.2.2" + }, + "bin": { + "download-lmdb-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@lmdb/lmdb-darwin-arm64": "3.4.3", + "@lmdb/lmdb-darwin-x64": "3.4.3", + "@lmdb/lmdb-linux-arm": "3.4.3", + "@lmdb/lmdb-linux-arm64": "3.4.3", + "@lmdb/lmdb-linux-x64": "3.4.3", + "@lmdb/lmdb-win32-arm64": "3.4.3", + "@lmdb/lmdb-win32-x64": "3.4.3" + } + }, + "node_modules/log-symbols": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz", + "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-angular": { + "version": "0.561.0", + "resolved": "https://registry.npmjs.org/lucide-angular/-/lucide-angular-0.561.0.tgz", + "integrity": "sha512-/DVDEVDJM08h8bGarLH65+4jfLcjaGhHcG1TdDXvo0hX1CXczG0NzEIEs6CJdddfEdO+wzv3KLGpAz1QPw7sSg==", + "license": "ISC", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": "13.x - 21.x", + "@angular/core": "13.x - 21.x" + } + }, + "node_modules/magic-string": { + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/make-fetch-happen": { + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.3.tgz", + "integrity": "sha512-iyyEpDty1mwW3dGlYXAJqC/azFn5PPvgKVwXayOGBSmKLxhKZ9fg4qIan2ePpp1vJIwfFiO34LAPZgq9SZW9Aw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^4.0.0", + "cacache": "^20.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^5.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^6.0.0", + "promise-retry": "^2.0.1", + "ssri": "^13.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/make-fetch-happen/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/make-fetch-happen/node_modules/ssri": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.0.tgz", + "integrity": "sha512-yizwGBpbCn4YomB2lzhZqrHLJoqFGXihNbib3ozhqF/cIp5ue+xSmOQrjNasEE62hFxsCcg/V/z23t4n8jMEng==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-collect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-fetch": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-5.0.0.tgz", + "integrity": "sha512-fiCdUALipqgPWrOVTz9fw0XhcazULXOSU6ie40DDbX1F49p1dBrSRBuswndTx1x3vEb/g0FT7vC4c4C2u/mh3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/msgpackr": { + "version": "1.11.8", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.8.tgz", + "integrity": "sha512-bC4UGzHhVvgDNS7kn9tV8fAucIYUBuGojcaLiz7v+P63Lmtm0Xeji8B/8tYKddALXxJLpwIeBmUN3u64C4YkRA==", + "dev": true, + "license": "MIT", + "optional": true, + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + } + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-gyp": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.1.0.tgz", + "integrity": "sha512-W+RYA8jBnhSr2vrTtlPYPc1K+CSjGpVDRZxcqJcERZ8ND3A1ThWPHRwctTx3qC3oW99jt726jhdz3Y6ky87J4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^15.0.0", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "tar": "^7.5.2", + "tinyglobby": "^0.2.12", + "which": "^6.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/node-gyp/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", + "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^4.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-bundled": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-4.0.0.tgz", + "integrity": "sha512-IxaQZDMsqfQ2Lz37VvyyEtKLe8FsRZuysmedy/N06TU1RyVppYKXrO4xIhR0F+7ubIBox6Q7nir6fQI3ej39iA==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-install-checks": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-8.0.0.tgz", + "integrity": "sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-package-arg": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-13.0.1.tgz", + "integrity": "sha512-6zqls5xFvJbgFjB1B2U6yITtyGBjDBORB7suI4zA4T/sZ1OmkMFlaQSNB/4K0LtXNA1t4OprAFxPisadK5O2ag==", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^9.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-packlist": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.3.tgz", + "integrity": "sha512-zPukTwJMOu5X5uvm0fztwS5Zxyvmk38H/LfidkOMt3gbZVCyro2cD/ETzwzVPcWZA3JOyPznfUN/nkyFiyUbxg==", + "dev": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^8.0.0", + "proc-log": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-packlist/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-pick-manifest": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz", + "integrity": "sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^8.0.0", + "npm-normalize-package-bin": "^5.0.0", + "npm-package-arg": "^13.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-pick-manifest/node_modules/npm-normalize-package-bin": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-5.0.0.tgz", + "integrity": "sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-registry-fetch": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-19.1.1.tgz", + "integrity": "sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^4.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^15.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^5.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^13.0.0", + "proc-log": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-9.0.0.tgz", + "integrity": "sha512-m0pg2zscbYgWbqRR6ABga5c3sZdEon7bSgjnlXC64kxtxLOyjRcbbUkLj7HFyy/FTD+P2xdBWu8snGhYI0jc4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.6.2", + "cli-cursor": "^5.0.0", + "cli-spinners": "^3.2.0", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.1.0", + "log-symbols": "^7.0.1", + "stdin-discarder": "^0.2.2", + "string-width": "^8.1.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ordered-binary": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.6.0.tgz", + "integrity": "sha512-IQh2aMfMIDbPjI/8a3Edr+PiOpcsB7yo8NdW7aHWVaoR/pcDldunMvnnwbk/auPGqmKeAdxtZl7MHX/QmPwhvQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/p-map": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pacote": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-21.0.3.tgz", + "integrity": "sha512-itdFlanxO0nmQv4ORsvA9K1wv40IPfB9OmWqfaJWvoJ30VKyHsqNgDVeG+TVhI7Gk7XW8slUy7cA9r6dF5qohw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^7.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^10.0.0", + "cacache": "^20.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^13.0.0", + "npm-packlist": "^10.0.1", + "npm-pick-manifest": "^11.0.1", + "npm-registry-fetch": "^19.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^4.0.0", + "ssri": "^12.0.0", + "tar": "^7.4.3" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-html-rewriting-stream": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-8.0.0.tgz", + "integrity": "sha512-wzh11mj8KKkno1pZEu+l2EVeWsuKDfR5KNWZOTsslfUX8lPDZx77m9T0kIoAVkFtD1nx6YF8oh4BnPHvxMtNMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0", + "parse5": "^8.0.0", + "parse5-sax-parser": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-html-rewriting-stream/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parse5-sax-parser": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-8.0.0.tgz", + "integrity": "sha512-/dQ8UzHZwnrzs3EvDj6IkKrD/jIZyTlB+8XrHJvcjNgRdmWruNdN9i9RK/JtxakmlUdPwKubKPTCqvbTgzGhrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/piscina": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-5.1.3.tgz", + "integrity": "sha512-0u3N7H4+hbr40KjuVn2uNhOcthu/9usKhnw5vT3J7ply79v3D3M8naI00el9Klcy16x557VsEkkUQaHCWFXC/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.x" + }, + "optionalDependencies": { + "@napi-rs/nice": "^1.0.4" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", + "dev": true, + "license": "MIT" + }, + "node_modules/proc-log": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", + "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rolldown": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-beta.47.tgz", + "integrity": "sha512-Mid74GckX1OeFAOYz9KuXeWYhq3xkXbMziYIC+ULVdUzPTG9y70OBSBQDQn9hQP8u/AfhuYw1R0BSg15nBI4Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.96.0", + "@rolldown/pluginutils": "1.0.0-beta.47" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-beta.47", + "@rolldown/binding-darwin-arm64": "1.0.0-beta.47", + "@rolldown/binding-darwin-x64": "1.0.0-beta.47", + "@rolldown/binding-freebsd-x64": "1.0.0-beta.47", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.47", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.47", + "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.47", + "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.47", + "@rolldown/binding-linux-x64-musl": "1.0.0-beta.47", + "@rolldown/binding-openharmony-arm64": "1.0.0-beta.47", + "@rolldown/binding-wasm32-wasi": "1.0.0-beta.47", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.47", + "@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.47", + "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.47" + } + }, + "node_modules/rollup": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.5.tgz", + "integrity": "sha512-iTNAbFSlRpcHeeWu73ywU/8KuU/LZmNCSxp6fjQkJBD3ivUb8tpDrXhIxEzA05HlYMEwmtaUnb3RP+YNv162OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.5", + "@rollup/rollup-android-arm64": "4.53.5", + "@rollup/rollup-darwin-arm64": "4.53.5", + "@rollup/rollup-darwin-x64": "4.53.5", + "@rollup/rollup-freebsd-arm64": "4.53.5", + "@rollup/rollup-freebsd-x64": "4.53.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.5", + "@rollup/rollup-linux-arm-musleabihf": "4.53.5", + "@rollup/rollup-linux-arm64-gnu": "4.53.5", + "@rollup/rollup-linux-arm64-musl": "4.53.5", + "@rollup/rollup-linux-loong64-gnu": "4.53.5", + "@rollup/rollup-linux-ppc64-gnu": "4.53.5", + "@rollup/rollup-linux-riscv64-gnu": "4.53.5", + "@rollup/rollup-linux-riscv64-musl": "4.53.5", + "@rollup/rollup-linux-s390x-gnu": "4.53.5", + "@rollup/rollup-linux-x64-gnu": "4.53.5", + "@rollup/rollup-linux-x64-musl": "4.53.5", + "@rollup/rollup-openharmony-arm64": "4.53.5", + "@rollup/rollup-win32-arm64-msvc": "4.53.5", + "@rollup/rollup-win32-ia32-msvc": "4.53.5", + "@rollup/rollup-win32-x64-gnu": "4.53.5", + "@rollup/rollup-win32-x64-msvc": "4.53.5", + "fsevents": "~2.3.2" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.2.tgz", + "integrity": "sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sigstore": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-4.0.0.tgz", + "integrity": "sha512-Gw/FgHtrLM9WP8P5lLcSGh9OQcrTruWCELAiS48ik1QbL0cH+dfjomiRTUE9zzz+D1N6rOLkwXUvVmXZAsNE0Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.0.0", + "@sigstore/protobuf-specs": "^0.5.0", + "@sigstore/sign": "^4.0.0", + "@sigstore/tuf": "^4.0.0", + "@sigstore/verify": "^3.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/ssri": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-12.0.0.tgz", + "integrity": "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", + "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tar": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", + "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "7.0.19", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.19.tgz", + "integrity": "sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.19" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.19", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.19.tgz", + "integrity": "sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==", + "dev": true, + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz", + "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "peer": true + }, + "node_modules/tuf-js": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-4.0.0.tgz", + "integrity": "sha512-Lq7ieeGvXDXwpoSmOSgLWVdsGGV9J4a77oDTAPe/Ltrqnnm/ETaRlBAQTH5JatEh8KXuE6sddf9qAv1Q2282Hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "4.0.0", + "debug": "^4.4.1", + "make-fetch-happen": "^15.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.16.0.tgz", + "integrity": "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/unique-filename": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-5.0.0.tgz", + "integrity": "sha512-2RaJTAvAb4owyjllTfXzFClJ7WsGxlykkPvCr9pA//LD9goVq+m4PPAeBgNodGZ7nSrntT/auWpJ6Y5IFXcfjg==", + "dev": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/unique-slug": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-6.0.0.tgz", + "integrity": "sha512-4Lup7Ezn8W3d52/xBhZBVdx323ckxa7DEvd9kPQHppTkLoJXw6ltrBCyj5pnrxj0qKDxYMJ56CoxNuFCscdTiw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-6.0.2.tgz", + "integrity": "sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz", + "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/vitest": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.16.tgz", + "integrity": "sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@vitest/expect": "4.0.16", + "@vitest/mocker": "4.0.16", + "@vitest/pretty-format": "4.0.16", + "@vitest/runner": "4.0.16", + "@vitest/snapshot": "4.0.16", + "@vitest/spy": "4.0.16", + "@vitest/utils": "4.0.16", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.16", + "@vitest/browser-preview": "4.0.16", + "@vitest/browser-webdriverio": "4.0.16", + "@vitest/ui": "4.0.16", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/watchpack": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/weak-lru-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", + "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/webidl-conversions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.0.tgz", + "integrity": "sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz", + "integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "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/wrap-ansi/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/wrap-ansi/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/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" + }, + "engines": { + "node": ">=8" + } + }, + "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/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", + "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^7.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz", + "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz", + "integrity": "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "zod": "^3.25 || ^4" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..73e93ae --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,45 @@ +{ + "name": "supervision-app", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test" + }, + "prettier": { + "printWidth": 100, + "singleQuote": true, + "overrides": [ + { + "files": "*.html", + "options": { + "parser": "angular" + } + } + ] + }, + "private": true, + "packageManager": "npm@11.6.2", + "dependencies": { + "@angular/cdk": "^21.0.0", + "@angular/common": "^21.0.0", + "@angular/compiler": "^21.0.0", + "@angular/core": "^21.0.0", + "@angular/forms": "^21.0.0", + "@angular/platform-browser": "^21.0.0", + "@angular/router": "^21.0.0", + "lucide-angular": "^0.561.0", + "rxjs": "~7.8.0", + "tslib": "^2.3.0" + }, + "devDependencies": { + "@angular/build": "^21.0.3", + "@angular/cli": "^21.0.3", + "@angular/compiler-cli": "^21.0.0", + "jsdom": "^27.1.0", + "typescript": "~5.9.2", + "vitest": "^4.0.8" + } +} diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico new file mode 100644 index 0000000..889b323 Binary files /dev/null and b/frontend/public/favicon.ico differ diff --git a/frontend/public/logo.png b/frontend/public/logo.png new file mode 100644 index 0000000..ddefe70 Binary files /dev/null and b/frontend/public/logo.png differ diff --git a/frontend/src/app/app.config.ts b/frontend/src/app/app.config.ts new file mode 100644 index 0000000..d05f055 --- /dev/null +++ b/frontend/src/app/app.config.ts @@ -0,0 +1,19 @@ +import { ApplicationConfig, provideBrowserGlobalErrorListeners, LOCALE_ID } from '@angular/core'; +import { registerLocaleData } from '@angular/common'; +import localeEs from '@angular/common/locales/es'; +import { provideRouter } from '@angular/router'; +import { provideHttpClient, withInterceptors } from '@angular/common/http'; +import { authInterceptor } from './interceptors/auth-interceptor'; + +registerLocaleData(localeEs); + +import { routes } from './app.routes'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideBrowserGlobalErrorListeners(), + provideRouter(routes), + provideHttpClient(withInterceptors([authInterceptor])), + { provide: LOCALE_ID, useValue: 'es' } + ] +}; diff --git a/frontend/src/app/app.html b/frontend/src/app/app.html new file mode 100644 index 0000000..0680b43 --- /dev/null +++ b/frontend/src/app/app.html @@ -0,0 +1 @@ + diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts new file mode 100644 index 0000000..8536001 --- /dev/null +++ b/frontend/src/app/app.routes.ts @@ -0,0 +1,38 @@ +import { Routes } from '@angular/router'; +import { LoginComponent } from './components/login/login'; +import { LayoutComponent } from './components/layout/layout'; +import { DashboardComponent } from './components/dashboard/dashboard'; +import { ActivityListComponent } from './components/activity-list/activity-list'; +import { ActivityFormComponent } from './components/activity-form/activity-form'; +import { ProjectListComponent } from './components/project-list/project-list'; +import { ProjectFormComponent } from './components/project-form/project-form'; +import { NonConformityListComponent } from './components/non-conformity-list/non-conformity-list'; +import { NonConformityFormComponent } from './components/non-conformity-form/non-conformity-form'; +import { ContractorListComponent } from './components/contractor-list/contractor-list'; +import { ContractorFormComponent } from './components/contractor-form/contractor-form'; +import { authGuard } from './guards/auth'; + +export const routes: Routes = [ + { path: 'login', component: LoginComponent }, + { + path: '', + component: LayoutComponent, + canActivate: [authGuard], + children: [ + { path: 'dashboard', component: DashboardComponent }, + { path: 'activities', component: ActivityListComponent }, + { path: 'activities/new', component: ActivityFormComponent }, + { path: 'activities/edit/:id', component: ActivityFormComponent }, + { path: 'projects', component: ProjectListComponent }, + { path: 'projects/new', component: ProjectFormComponent }, + { path: 'projects/edit/:id', component: ProjectFormComponent }, + { path: 'non-conformities', component: NonConformityListComponent }, + { path: 'non-conformities/edit/:id', component: NonConformityFormComponent }, + { path: 'contractors', component: ContractorListComponent }, + { path: 'contractors/new', component: ContractorFormComponent }, + { path: 'contractors/edit/:id', component: ContractorFormComponent }, + { path: '', redirectTo: 'dashboard', pathMatch: 'full' } + ] + }, + { path: '**', redirectTo: 'login' } +]; diff --git a/frontend/src/app/app.scss b/frontend/src/app/app.scss new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/app/app.spec.ts b/frontend/src/app/app.spec.ts new file mode 100644 index 0000000..121ea05 --- /dev/null +++ b/frontend/src/app/app.spec.ts @@ -0,0 +1,23 @@ +import { TestBed } from '@angular/core/testing'; +import { App } from './app'; + +describe('App', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [App], + }).compileComponents(); + }); + + it('should create the app', () => { + const fixture = TestBed.createComponent(App); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + + it('should render title', async () => { + const fixture = TestBed.createComponent(App); + await fixture.whenStable(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('h1')?.textContent).toContain('Hello, supervision-app'); + }); +}); diff --git a/frontend/src/app/app.ts b/frontend/src/app/app.ts new file mode 100644 index 0000000..11ee44b --- /dev/null +++ b/frontend/src/app/app.ts @@ -0,0 +1,19 @@ +import { Component, inject, OnInit } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; +import { AuthService } from './services/auth'; + +@Component({ + selector: 'app-root', + imports: [RouterOutlet], + templateUrl: './app.html', + styleUrl: './app.scss' +}) +export class App implements OnInit { + private authService = inject(AuthService); + + ngOnInit() { + if (this.authService.token()) { + this.authService.fetchCurrentUser().subscribe(); + } + } +} diff --git a/frontend/src/app/components/activity-calendar/activity-calendar.html b/frontend/src/app/components/activity-calendar/activity-calendar.html new file mode 100644 index 0000000..69f4796 --- /dev/null +++ b/frontend/src/app/components/activity-calendar/activity-calendar.html @@ -0,0 +1,60 @@ +
+ +
+ + +
+

{{ currentDate() | date:'MMMM yyyy' | titlecase }}

+
+ +
+ Semana del {{ weekDays()[0] | date:'shortDate' }} al {{ weekDays()[6] | + date:'shortDate' }} +
+
+ + +
+
+
+ {{ day | date:'EEE' | uppercase }} + {{ day | date:'dd' }} +
+ +
+
+
+ +
+ + {{ act.date | date:'HH:mm' }} +
+ +

{{ act.area }}

+

{{ act.description | slice:0:40 }}{{ act.description.length > 40 ? '...' : + '' }}

+ + +
+ +
+ +
+
+
+
+
+
\ No newline at end of file diff --git a/frontend/src/app/components/activity-calendar/activity-calendar.scss b/frontend/src/app/components/activity-calendar/activity-calendar.scss new file mode 100644 index 0000000..db5a8ed --- /dev/null +++ b/frontend/src/app/components/activity-calendar/activity-calendar.scss @@ -0,0 +1,277 @@ +.calendar-wrapper { + background: var(--bg-surface); + border-radius: 20px; + padding: 24px; + box-shadow: var(--shadow-md); + border: 1px solid var(--border-color); +} + +.calendar-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 24px; + flex-wrap: wrap; + gap: 16px; + + .nav-controls { + display: flex; + align-items: center; + gap: 8px; + background: var(--bg-main); + padding: 4px; + border-radius: 12px; + border: 1px solid var(--border-color); + + .nav-btn { + width: 36px; + height: 36px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 8px; + color: var(--text-muted); + transition: all 0.2s; + + &:hover { + background: white; + color: var(--brand-primary); + } + } + + .today-btn { + padding: 0 16px; + height: 36px; + font-size: 0.85rem; + font-weight: 700; + color: var(--brand-secondary); + + &:hover { + color: var(--brand-primary); + } + } + } + + .current-month h2 { + font-size: 1.25rem; + margin: 0; + color: var(--brand-secondary); + font-weight: 800; + text-transform: capitalize; + } + + .week-label { + font-size: 0.85rem; + color: var(--text-muted); + font-weight: 600; + } +} + +.week-grid { + display: grid; + grid-template-columns: repeat(7, 1fr); + gap: 2px; + background: var(--border-color); + border: 1px solid var(--border-color); + border-radius: 16px; + overflow: hidden; + + @media (max-width: 1024px) { + display: flex; + flex-direction: column; + gap: 12px; + background: transparent; + border: none; + overflow: visible; + } +} + +.day-column { + background: var(--bg-surface); + min-height: 500px; + display: flex; + flex-direction: column; + + &.is-today { + background: rgba(180, 83, 9, 0.02); + + .day-header { + .day-number { + background: var(--brand-primary); + color: white; + } + + .day-name { + color: var(--brand-primary); + } + } + } + + @media (max-width: 1024px) { + min-height: auto; + border: 1px solid var(--border-color); + border-radius: 16px; + } +} + +.day-header { + padding: 16px; + text-align: center; + border-bottom: 1px solid var(--border-color); + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + + .day-name { + font-size: 0.7rem; + font-weight: 800; + color: var(--text-muted); + letter-spacing: 0.05em; + } + + .day-number { + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + font-size: 1rem; + font-weight: 700; + color: var(--brand-secondary); + } +} + +.day-body { + flex: 1; + padding: 12px; + background: rgba(248, 250, 252, 0.5); + + .activity-cards { + display: flex; + flex-direction: column; + gap: 8px; + } +} + +.activity-card { + padding: 12px; + border-radius: 10px; + font-size: 0.85rem; + cursor: pointer; + transition: all 0.2s; + border-left: 4px solid #cbd5e1; + background: white; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.02); + + &:hover { + transform: scale(1.02); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); + } + + .time { + display: flex; + align-items: center; + gap: 4px; + font-size: 0.7rem; + font-weight: 700; + color: var(--text-muted); + margin-bottom: 4px; + } + + h4 { + margin: 0; + font-size: 0.85rem; + font-weight: 800; + color: var(--brand-secondary); + margin-bottom: 2px; + } + + p { + margin: 0; + font-size: 0.75rem; + color: var(--text-muted); + line-height: 1.2; + } + + .card-footer { + margin-top: 8px; + + .type-badge { + font-size: 0.6rem; + text-transform: uppercase; + font-weight: 800; + padding: 2px 6px; + border-radius: 4px; + background: #f1f5f9; + } + } + + // Color variants + &.type-inspection { + border-left-color: #3b82f6; + + .time { + color: #3b82f6; + } + } + + &.type-meeting { + border-left-color: #10b981; + + .time { + color: #10b981; + } + } + + &.type-virtual { + border-left-color: #8b5cf6; + + .time { + color: #8b5cf6; + } + } + + &.type-coordination { + border-left-color: #f59e0b; + + .time { + color: #f59e0b; + } + } + + &.type-test { + border-left-color: #ef4444; + + .time { + color: #ef4444; + } + } + + &.type-other { + border-left-color: #64748b; + } +} + +.add-slot { + height: 32px; + display: flex; + align-items: center; + justify-content: center; + border: 1px dashed var(--border-color); + border-radius: 8px; + color: var(--text-muted); + opacity: 0; + transition: opacity 0.2s; + + &:hover { + background: white; + color: var(--brand-primary); + border-color: var(--brand-primary); + } +} + +.day-column:hover .add-slot { + opacity: 1; +} \ No newline at end of file diff --git a/frontend/src/app/components/activity-calendar/activity-calendar.ts b/frontend/src/app/components/activity-calendar/activity-calendar.ts new file mode 100644 index 0000000..fc409d9 --- /dev/null +++ b/frontend/src/app/components/activity-calendar/activity-calendar.ts @@ -0,0 +1,109 @@ +import { Component, inject, signal, OnInit, computed, Input } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { LucideAngularModule, ChevronLeft, ChevronRight, Calendar as CalendarIcon, Clock, MoreHorizontal } from 'lucide-angular'; +import { ActivityService } from '../../services/activity'; +import { RouterModule } from '@angular/router'; + +@Component({ + selector: 'app-activity-calendar', + standalone: true, + imports: [CommonModule, LucideAngularModule, RouterModule], + templateUrl: './activity-calendar.html', + styleUrl: './activity-calendar.scss' +}) +export class ActivityCalendarComponent implements OnInit { + readonly ChevronLeft = ChevronLeft; + readonly ChevronRight = ChevronRight; + readonly CalendarIcon = CalendarIcon; + readonly Clock = Clock; + readonly MoreHorizontal = MoreHorizontal; + + private activityService = inject(ActivityService); + + @Input() selectedProjectId: number | null = null; + + activities = signal([]); + currentDate = signal(new Date()); + weekDays = signal([]); + + loading = signal(false); + + ngOnInit() { + this.generateWeekDays(); + this.loadActivities(); + } + + generateWeekDays() { + const curr = new Date(this.currentDate()); + const first = curr.getDate() - curr.getDay() + 1; // Monday as first day + const days = []; + for (let i = 0; i < 7; i++) { + const d = new Date(curr.setDate(first + i)); + days.push(new Date(d)); + } + this.weekDays.set(days); + } + + loadActivities() { + this.loading.set(true); + this.activityService.getActivities(this.selectedProjectId || undefined).subscribe({ + next: (acts) => { + this.activities.set(acts); + this.loading.set(false); + }, + error: () => this.loading.set(false) + }); + } + + nextWeek() { + const d = new Date(this.currentDate()); + d.setDate(d.getDate() + 7); + this.currentDate.set(d); + this.generateWeekDays(); + } + + prevWeek() { + const d = new Date(this.currentDate()); + d.setDate(d.getDate() - 7); + this.currentDate.set(d); + this.generateWeekDays(); + } + + goToToday() { + this.currentDate.set(new Date()); + this.generateWeekDays(); + } + + getActivitiesForDay(day: Date) { + return this.activities().filter(a => { + const actDate = new Date(a.date); + return actDate.getDate() === day.getDate() && + actDate.getMonth() === day.getMonth() && + actDate.getFullYear() === day.getFullYear(); + }); + } + + getActivityClass(type: string) { + const classes: any = { + 'inspection': 'type-inspection', + 'meeting': 'type-meeting', + 'virtual_meeting': 'type-virtual', + 'coordination': 'type-coordination', + 'test': 'type-test', + 'other': 'type-other' + }; + return classes[type] || 'type-other'; + } + + getActivityTypeName(type: string) { + const names: any = { + 'inspection': 'Supervisión', + 'meeting': 'Reunión Presencial', + 'virtual_meeting': 'Reunión Virtual', + 'coordination': 'Coordinación', + 'test': 'Pruebas/Ensayos', + 'other': 'Otro' + }; + return names[type] || type; + } +} diff --git a/frontend/src/app/components/activity-form/activity-form.html b/frontend/src/app/components/activity-form/activity-form.html new file mode 100644 index 0000000..d46c3e2 --- /dev/null +++ b/frontend/src/app/components/activity-form/activity-form.html @@ -0,0 +1,285 @@ + +
+ + +
+
+ + + +
+
+ +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ +
+
+

Evidencias

+

Gestione los registros técnicos vinculados a esta actividad.

+
+ + +
+
+

Archivos Registrados ({{ existingEvidences.length }})

+ +
+ +
+
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ {{ ev.media_type.split('/')[1] }} + +
+ + + +
+
+ + {{ ev.transcription_status === 'completed' ? 'Transcrito' : + ev.transcription_status === 'error' ? 'Error' : 'Procesando' }} +
+ +

+ "{{ ev.transcription }}" +

+
+
+
+
+
+ +
+
+ +
+ Cargar archivos + Fotos y Videos +
+
+
+ + +
+ + +
+ +
+
+
+ +
+ + Video +
+
+ + Audio +
+
+
+
+ {{ item.file.name }} + +
+ +
+
+
+
+ + +
+
+

Hallazgos de No Conformidad

+

Registre desviaciones o incumplimientos detectados.

+
+ +
+
+
+
+ + Hallazgo #{{ i + 1 }} +
+ +
+ +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+ + +
+ +
+ + +
+
+ +
+ + + +
+
+
\ No newline at end of file diff --git a/frontend/src/app/components/activity-form/activity-form.scss b/frontend/src/app/components/activity-form/activity-form.scss new file mode 100644 index 0000000..7ecaa6a --- /dev/null +++ b/frontend/src/app/components/activity-form/activity-form.scss @@ -0,0 +1,1074 @@ +// Transcription Status +.transcription-status { + margin-top: 0.5rem; + + .status-badge { + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.2rem 0.6rem; + border-radius: 99rem; + font-size: 0.75rem; + font-weight: 500; + + &.pending { + background: rgba(100, 116, 139, 0.1); + color: #64748b; + } + + &.processing { + background: rgba(59, 130, 246, 0.1); + color: #3b82f6; + animation: pulse 2s infinite; + } + + &.completed { + background: rgba(34, 197, 94, 0.1); + color: #22c55e; + } + + &.error { + background: rgba(239, 68, 68, 0.1); + color: #ef4444; + } + } + + .retry-btn { + background: #fffbeb; + border: 1px solid #fde68a; + color: #b45309; + font-size: 0.7rem; + font-weight: 700; + padding: 4px 8px; + border-radius: 6px; + text-transform: uppercase; + margin-top: 4px; + cursor: pointer; + + &:hover { + background: #fef3c7; + } + } + + .transcription-text { + margin-top: 0.4rem; + font-size: 0.85rem; + color: #475569; + font-style: italic; + padding-left: 0.5rem; + border-left: 2px solid #e2e8f0; + } +} + +.page-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + margin-bottom: 24px; + position: sticky; + top: -24px; + background: var(--bg-main); + z-index: 100; + padding: 16px 0; + border-bottom: 2px solid transparent; + transition: all 0.3s ease; + + &.sticky-header { + background: var(--bg-main); + margin-left: -24px; + margin-right: -24px; + padding-left: 24px; + padding-right: 24px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05); + border-bottom-color: var(--border-color); + } + + .header-left { + display: flex; + align-items: center; + gap: 12px; + + .back-btn { + background: var(--bg-surface); + border: 1px solid var(--border-color); + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + color: var(--text-muted); + + &:hover { + border-color: var(--brand-primary); + color: var(--brand-primary); + } + } + + .title-meta { + display: flex; + flex-direction: column; + + h1 { + font-size: 1.25rem; + margin: 0; + color: var(--brand-secondary); + } + + .badge { + font-size: 0.7rem; + font-weight: 700; + color: var(--text-muted); + letter-spacing: 0.05em; + } + } + } + + .header-actions { + display: flex; + align-items: center; + gap: 8px; + + .cancel-btn { + background: transparent; + color: var(--text-muted); + font-weight: 700; + padding: 10px 16px; + font-size: 0.85rem; + text-transform: uppercase; + + &:hover { + color: var(--brand-secondary); + } + } + + .gold-button { + padding: 10px 20px; + font-size: 0.85rem; + text-transform: uppercase; + letter-spacing: 0.02em; + } + } + + @media (max-width: 640px) { + flex-direction: column; + align-items: stretch; + gap: 12px; + padding: 12px 16px; + top: -16px; + margin-left: -16px; + margin-right: -16px; + + .header-actions { + justify-content: space-between; + + .gold-button, + .cancel-btn { + flex: 1; + text-align: center; + } + } + } +} + +.form-container { + max-width: 840px; + margin: 0 auto; + + + .status-selector-card { + margin-bottom: 24px; + padding: 20px; + background: var(--bg-surface); + border-radius: 16px; + box-shadow: var(--shadow-sm); + border: 1px solid var(--border-color); + + .status-options { + display: flex; + gap: 16px; + + @media (max-width: 480px) { + flex-direction: column; + } + + .status-btn { + flex: 1; + padding: 12px 16px; + border-radius: 12px; + font-size: 0.85rem; + font-weight: 700; + border: 1px solid var(--border-color); + background: var(--bg-main); + color: var(--text-muted); + transition: all 0.2s; + text-transform: uppercase; + cursor: pointer; + + &.active { + color: white; + + &.pending { + background: #64748b; + border-color: #475569; + } + + &.in-progress { + background: #b45309; + border-color: #92400e; + } + + &.done { + background: #059669; + border-color: #047857; + } + } + + &:hover:not(.active) { + border-color: var(--brand-primary); + color: var(--brand-primary); + } + } + } + } + + .form-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 24px; + margin-bottom: 32px; + + @media (max-width: 640px) { + grid-template-columns: 1fr; + } + + .full-width { + grid-column: span 2; + + @media (max-width: 640px) { + grid-column: span 1; + } + } + } + + .form-group { + display: flex; + flex-direction: column; + gap: 8px; + + label { + font-size: 0.85rem; + color: var(--text-muted); + font-weight: 700; + text-transform: uppercase; + } + + input, + select, + textarea { + background: var(--bg-main); + border: 1px solid var(--border-color); + color: var(--text-main); + padding: 12px 14px; + border-radius: 10px; + font-size: 0.95rem; + transition: all 0.2s; + + &:focus { + outline: none; + border-color: var(--brand-primary); + background: var(--bg-surface); + } + } + + textarea { + min-height: 100px; + resize: vertical; + } + } + + .evidence-section { + border-top: 1px solid var(--border-color); + padding-top: 32px; + margin-bottom: 32px; + + .section-header { + margin-bottom: 20px; + + h3 { + font-size: 1.1rem; + margin-bottom: 4px; + } + + p { + color: var(--text-muted); + font-size: 0.9rem; + } + } + + .section-sub-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; + + h4 { + font-size: 0.8rem; + color: var(--text-muted); + font-weight: 800; + text-transform: uppercase; + margin: 0; + } + + .refresh-btn { + background: none; + border: none; + color: #64748b; + padding: 6px; + border-radius: 50%; + cursor: pointer; + + &:hover { + background: rgba(0, 0, 0, 0.05); + color: #1e293b; + } + } + } + } + + // Existing Evidences Grid + .evidence-grid-dynamic { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); + gap: 16px; + margin-bottom: 24px; + + @media (max-width: 480px) { + grid-template-columns: 1fr; + } + } + + .evidence-card-editable { + background: white; + border: 1px solid var(--border-color); + border-radius: 16px; + overflow: hidden; + display: flex; + flex-direction: column; + transition: all 0.2s; + box-shadow: var(--shadow-sm); + + &:hover { + transform: translateY(-2px); + box-shadow: var(--shadow-md); + + .card-media .overlay-play { + opacity: 1; + } + } + + .card-media { + height: 140px; + background: #f8fafc; + position: relative; + cursor: pointer; + overflow: hidden; + + img { + width: 100%; + height: 100%; + object-fit: cover; + } + + .placeholder { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + color: #94a3b8; + + &.audio { + background: #eff6ff; + color: #3b82f6; + } + + &.video { + background: #fef2f2; + color: #ef4444; + } + } + + .overlay-play { + position: absolute; + inset: 0; + background: rgba(0, 0, 0, 0.3); + display: flex; + align-items: center; + justify-content: center; + color: white; + opacity: 0; + transition: opacity 0.2s; + } + } + + .card-content { + padding: 12px; + display: flex; + flex-direction: column; + gap: 10px; + + .card-header { + display: flex; + justify-content: space-between; + align-items: center; + + .type-tag { + font-size: 0.65rem; + text-transform: uppercase; + font-weight: 800; + color: var(--text-muted); + background: #f1f5f9; + padding: 2px 6px; + border-radius: 4px; + } + + .delete-btn { + color: #94a3b8; + padding: 4px; + cursor: pointer; + + &:hover { + color: #ef4444; + } + } + } + + textarea { + width: 100%; + border: 1px solid transparent; + background: #f8fafc; + border-radius: 8px; + padding: 6px 10px; + font-size: 0.85rem; + resize: none; + height: 60px; + transition: all 0.2s; + + &:focus { + background: white; + border-color: var(--brand-primary); + outline: none; + } + } + } + } + + // Upload Area + .upload-area { + display: flex; + align-items: center; + gap: 12px; + flex-wrap: wrap; + margin-top: 16px; + + .upload-main { + flex: 1; + min-width: 200px; + border: 2px dashed #e2e8f0; + border-radius: 16px; + padding: 16px; + display: flex; + align-items: center; + gap: 12px; + cursor: pointer; + transition: all 0.2s; + background: var(--bg-main); + + &:hover { + border-color: var(--brand-primary); + background: #fffbeb; + } + + .upload-icon { + color: #94a3b8; + } + + .upload-text { + display: flex; + flex-direction: column; + + strong { + font-size: 0.9rem; + color: #1e293b; + } + + span { + font-size: 0.75rem; + color: #64748b; + } + } + } + + .action-buttons-group { + display: flex; + gap: 8px; + + .camera-btn, + .mic-btn { + height: 52px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 16px; + transition: all 0.2s; + } + + .camera-btn { + background: #f1f5f9; + color: #475569; + padding: 0 16px; + gap: 8px; + font-weight: 700; + font-size: 0.85rem; + border: 1px solid #e2e8f0; + + &:hover { + background: #e2e8f0; + } + } + + .mic-btn { + width: 52px; + background: #f1f5f9; + color: #475569; + border: 1px solid #e2e8f0; + cursor: pointer; + + &.recording { + background: #fee2e2; + color: #ef4444; + border-color: #fecaca; + animation: pulse-red 1.5s infinite; + } + + &:hover:not(.recording) { + background: #e2e8f0; + } + } + } + } + + // Preview for new files + .preview-list { + margin-top: 24px; + display: grid; + gap: 16px; + } + + .preview-item-expanded { + background: var(--bg-surface); + border: 1px solid var(--border-color); + border-radius: 16px; + display: flex; + overflow: hidden; + box-shadow: var(--shadow-sm); + + @media (max-width: 480px) { + flex-direction: column; + } + + .preview-media { + width: 120px; + height: 120px; + background: var(--bg-main); + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + + @media (max-width: 480px) { + width: 100%; + height: 200px; + } + + img { + width: 100%; + height: 100%; + object-fit: cover; + } + + .video-placeholder, + .audio-placeholder { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + color: var(--text-muted); + + span { + font-size: 0.7rem; + font-weight: 600; + text-transform: uppercase; + } + } + } + + .preview-details { + flex: 1; + padding: 16px; + display: flex; + flex-direction: column; + gap: 12px; + + .preview-header { + display: flex; + align-items: center; + justify-content: space-between; + + .file-name { + font-size: 0.85rem; + font-weight: 600; + color: var(--brand-secondary); + max-width: 200px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .remove-btn-small { + background: #fef2f2; + color: #ef4444; + width: 28px; + height: 28px; + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + + &:hover { + background: #fee2e2; + } + } + } + + textarea { + background: var(--bg-main); + border: 1px solid var(--border-color); + border-radius: 8px; + padding: 10px; + font-size: 0.85rem; + min-height: 50px; + resize: none; + + &:focus { + border-color: var(--brand-primary); + background: var(--bg-surface); + } + } + } + } + + // NC Section + .nc-list { + display: flex; + flex-direction: column; + gap: 24px; + margin-top: 24px; + } + + .nc-item, + .premium-card { + padding: 24px; + border: 1px solid var(--border-color); + background: white; + border-radius: 20px; + box-shadow: var(--shadow-sm); + transition: all 0.2s ease; + + &:hover { + box-shadow: var(--shadow-md); + transform: translateY(-2px); + } + + .nc-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 24px; + padding-bottom: 16px; + border-bottom: 1px solid var(--border-color); + + .nc-title { + display: flex; + align-items: center; + gap: 12px; + font-weight: 800; + color: var(--brand-secondary); + font-size: 1rem; + text-transform: uppercase; + letter-spacing: 0.02em; + + lucide-icon { + &.critical { + color: #ef4444; + } + + &.major { + color: #f59e0b; + } + + &.minor { + color: #3b82f6; + } + } + } + + .remove-nc-btn { + background: #fef2f2; + color: #ef4444; + width: 36px; + height: 36px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.2s; + + &:hover { + background: #fee2e2; + transform: rotate(90deg); + } + } + } + + .nc-fields { + display: grid; + grid-template-columns: 200px 1fr; + gap: 24px; + + @media (max-width: 768px) { + grid-template-columns: 1fr; + } + + .field-group { + display: flex; + flex-direction: column; + gap: 10px; + + label { + font-size: 0.75rem; + font-weight: 800; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 0.05em; + } + + select, + textarea { + background: var(--bg-main); + border: 1.5px solid var(--border-color); + border-radius: 12px; + padding: 12px 14px; + font-size: 0.95rem; + color: var(--text-main); + transition: all 0.2s; + + &:focus { + outline: none; + border-color: var(--brand-primary); + background: white; + box-shadow: 0 0 0 4px rgba(180, 83, 9, 0.05); + } + } + + textarea { + min-height: 120px; + resize: vertical; + } + } + } + } + + .add-nc-btn { + width: 100%; + margin-top: 24px; + padding: 20px; + border: 2px dashed var(--brand-primary); + border-radius: 20px; + background: rgba(180, 83, 9, 0.02); + color: var(--brand-primary); + display: flex; + align-items: center; + justify-content: center; + gap: 12px; + font-weight: 800; + font-size: 0.95rem; + text-transform: uppercase; + letter-spacing: 0.05em; + cursor: pointer; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &:hover { + background: rgba(180, 83, 9, 0.08); + transform: translateY(-4px); + border-style: solid; + box-shadow: 0 8px 24px rgba(180, 83, 9, 0.15); + } + + lucide-icon { + transition: transform 0.3s; + } + + &:hover lucide-icon { + transform: rotate(90deg) scale(1.2); + } + } +} + +// Global UI Elements (Camera, Modals) +.camera-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.95); + z-index: 9999; + display: flex; + align-items: center; + justify-content: center; + padding: 20px; + + .camera-container { + width: 100%; + max-width: 640px; + position: relative; + border-radius: 24px; + overflow: hidden; + background: black; + + video { + width: 100%; + height: auto; + display: block; + } + + .camera-actions { + position: absolute; + bottom: 32px; + left: 0; + right: 0; + display: flex; + justify-content: center; + gap: 16px; + + .action-btn { + padding: 12px 28px; + border-radius: 50px; + font-weight: 700; + text-transform: uppercase; + font-size: 0.75rem; + transition: all 0.2s; + cursor: pointer; + + &.photo { + background: white; + color: black; + } + + &.video { + background: #ef4444; + color: white; + + &.recording { + animation: pulse-red 1.5s infinite; + } + } + + &.cancel { + background: rgba(255, 255, 255, 0.2); + color: white; + backdrop-filter: blur(8px); + } + + &:hover { + transform: scale(1.05); + } + } + } + } +} + +.preview-modal-overlay { + position: fixed; + inset: 0; + background: rgb(15 23 42 / 18%); + backdrop-filter: blur(3px); + z-index: 9999; + display: flex; + align-items: center; + justify-content: center; + padding: 40px; + + @media (max-width: 640px) { + padding: 0; + } + + .modal-content { + background: white; + border-radius: 24px; + width: 100%; + max-width: 900px; + max-height: 85vh; + position: relative; + display: flex; + flex-direction: column; + overflow: hidden; + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); + + @media (max-width: 640px) { + height: 100vh; + max-height: 100vh; + border-radius: 0; + } + + .close-modal { + position: absolute; + top: 20px; + right: 20px; + z-index: 10; + background: white; + border-radius: 50%; + width: 44px; + height: 44px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + color: #64748b; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + + &:hover { + color: #1e293b; + transform: rotate(90deg) scale(1.1); + background: #f8fafc; + } + } + + .modal-media { + flex: 1; + //background: var(--brand-primary); + display: flex; + align-items: center; + justify-content: center; + min-height: 0; + padding: 1rem; + + img { + max-width: 100%; + max-height: 100%; + object-fit: contain; + } + + video { + width: 100%; + height: 100%; + max-height: 100%; + } + + audio { + width: 90%; + max-width: 500px; + //filter: invert(1) hue-rotate(180deg); + } + } + + .modal-footer { + padding: 24px; + background: white; + display: flex; + justify-content: space-between; + align-items: center; + gap: 20px; + border-top: 1px solid #f1f5f9; + + p { + font-size: 1rem; + color: #1e293b; + font-weight: 600; + margin: 0; + } + + .download-link { + display: flex; + align-items: center; + gap: 8px; + font-size: 0.9rem; + color: var(--brand-primary); + font-weight: 700; + text-decoration: none; + padding: 8px 16px; + background: rgba(180, 83, 9, 0.05); + border-radius: 8px; + + &:hover { + background: rgba(180, 83, 9, 0.1); + transform: translateY(-1px); + } + } + + @media (max-width: 480px) { + flex-direction: column; + align-items: flex-start; + padding: 16px; + + .download-link { + width: 100%; + justify-content: center; + } + } + } + } +} + +@keyframes pulse { + 0% { + opacity: 1; + } + + 50% { + opacity: 0.6; + } + + 100% { + opacity: 1; + } +} + +@keyframes pulse-red { + 0% { + transform: scale(0.95); + box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7); + } + + 70% { + transform: scale(1); + box-shadow: 0 0 0 10px rgba(239, 68, 68, 0); + } + + 100% { + transform: scale(0.95); + box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); + } +} + +@keyframes spin { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } +} \ No newline at end of file diff --git a/frontend/src/app/components/activity-form/activity-form.spec.ts b/frontend/src/app/components/activity-form/activity-form.spec.ts new file mode 100644 index 0000000..4a08e45 --- /dev/null +++ b/frontend/src/app/components/activity-form/activity-form.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ActivityForm } from './activity-form'; + +describe('ActivityForm', () => { + let component: ActivityForm; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ActivityForm] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ActivityForm); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/activity-form/activity-form.ts b/frontend/src/app/components/activity-form/activity-form.ts new file mode 100644 index 0000000..637e758 --- /dev/null +++ b/frontend/src/app/components/activity-form/activity-form.ts @@ -0,0 +1,426 @@ +import { Component, inject, signal, OnInit } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule, Router, ActivatedRoute } from '@angular/router'; +import { ReactiveFormsModule, FormBuilder, FormGroup, Validators, FormsModule } from '@angular/forms'; +import { ActivityService } from '../../services/activity'; +import { ProjectService } from '../../services/project'; +import { TranscriptionService } from '../../services/transcription'; +import { forkJoin } from 'rxjs'; +import { + LucideAngularModule, ArrowLeft, Upload, X, Shield, Mic, Square, Loader, + Camera, TriangleAlert, Plus, FileVolume, RefreshCw, CircleCheck, Clock, + Trash2, Maximize2, Play, ExternalLink +} from 'lucide-angular'; + +import { NonConformityService } from '../../services/non-conformity'; + +export enum ActivityType { + INSPECTION = 'inspection', + MEETING = 'meeting', + VIRTUAL_MEETING = 'virtual_meeting', + COORDINATION = 'coordination', + TEST = 'test', + OTHER = 'other' +} + +interface EvidenceItem { + file: File; + description: string; + capturedAt: string; + previewUrl: string; +} + +interface NCEntry { + id?: number; + level: 'critical' | 'major' | 'minor'; + description: string; +} + +@Component({ + selector: 'app-activity-form', + standalone: true, + imports: [CommonModule, RouterModule, ReactiveFormsModule, FormsModule, LucideAngularModule], + templateUrl: './activity-form.html', + styleUrl: './activity-form.scss' +}) +export class ActivityFormComponent implements OnInit { + readonly ArrowLeft = ArrowLeft; + readonly Upload = Upload; + readonly X = X; + readonly Shield = Shield; + readonly Mic = Mic; + readonly Square = Square; + readonly Loader = Loader; + readonly Camera = Camera; + readonly AlertTriangle = TriangleAlert; + readonly Plus = Plus; + readonly FileAudio = FileVolume; + readonly RefreshCw = RefreshCw; + readonly CheckCircle = CircleCheck; + readonly Clock = Clock; + readonly Trash2 = Trash2; + readonly Maximize2 = Maximize2; + readonly Play = Play; + readonly ExternalLink = ExternalLink; + + private fb = inject(FormBuilder); + private activityService = inject(ActivityService); + private projectService = inject(ProjectService); + private transcriptionService = inject(TranscriptionService); + private ncService = inject(NonConformityService); + private route = inject(ActivatedRoute); + private router = inject(Router); + + activityForm: FormGroup; + projects = signal([]); + selectedFiles: EvidenceItem[] = []; + existingEvidences: any[] = []; + ncEntries = signal([]); + loading = signal(false); + editMode = signal(false); + activityId: number | null = null; + + // Audio Recording + isRecording = signal(false); + transcriptionLoading = signal(false); + private mediaRecorder: MediaRecorder | null = null; + private audioChunks: Blob[] = []; + + // Camera Management + showCamera = signal(false); + isRecordingVideo = signal(false); + private videoStream: MediaStream | null = null; + private cameraRecorder: MediaRecorder | null = null; + private videoChunks: Blob[] = []; + + // Preview Modal + previewModal = signal<{ type: string, url: string, description: string } | null>(null); + + constructor() { + this.activityForm = this.fb.group({ + project_id: [null, Validators.required], + specialty_id: [1, Validators.required], + area: ['', Validators.required], + description: ['', Validators.required], + status: ['pending'], + type: [ActivityType.INSPECTION], + date: [null, Validators.required], + end_date: [null] + }); + } + + ngOnInit() { + this.loadProjects(); + + // Check for edit mode + this.activityId = Number(this.route.snapshot.paramMap.get('id')); + if (this.activityId) { + this.editMode.set(true); + this.loadActivity(this.activityId); + } else { + // Set default date to now + const now = new Date(); + now.setMinutes(0); // Round to hour + this.activityForm.patchValue({ date: now.toISOString().slice(0, 16) }); + } + } + + loadProjects() { + this.projectService.getProjects().subscribe(projs => { + this.projects.set(projs); + if (projs.length > 0 && !this.editMode()) { + this.activityForm.patchValue({ project_id: projs[0].id }); + } + }); + } + + loadActivity(id: number) { + this.activityService.getActivity(id).subscribe(act => { + this.activityForm.patchValue({ + project_id: act.project_id, + specialty_id: act.specialty_id, + area: act.area, + description: act.description, + status: act.status, + type: act.type, + date: act.date ? new Date(act.date).toISOString().slice(0, 16) : null, + end_date: act.end_date ? new Date(act.end_date).toISOString().slice(0, 16) : null + }); + this.existingEvidences = act.evidences || []; + + // Load existing NCs + if (act.non_conformities && act.non_conformities.length > 0) { + this.ncEntries.set(act.non_conformities.map((nc: any) => ({ + id: nc.id, + level: nc.level, + description: nc.description + }))); + } + }); + } + + async toggleRecording() { + if (this.isRecording()) { + this.stopRecording(); + } else { + await this.startRecording(); + } + } + + async startRecording() { + try { + const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); + this.mediaRecorder = new MediaRecorder(stream); + this.audioChunks = []; + + this.mediaRecorder.ondataavailable = (event) => { + this.audioChunks.push(event.data); + }; + + this.mediaRecorder.onstop = async () => { + const audioBlob = new Blob(this.audioChunks, { type: 'audio/wav' }); + const file = new File([audioBlob], `audio_${Date.now()}.wav`, { type: 'audio/wav' }); + this.addFile(file); + stream.getTracks().forEach(track => track.stop()); + }; + + this.mediaRecorder.start(); + this.isRecording.set(true); + } catch (err) { + console.error('No se pudo acceder al micrófono:', err); + alert('Error: No se pudo acceder al micrófono. Verifique los permisos.'); + } + } + + stopRecording() { + if (this.mediaRecorder && this.isRecording()) { + this.mediaRecorder.stop(); + this.isRecording.set(false); + } + } + + refreshStatus() { + if (this.activityId) { + this.loadActivity(this.activityId); + } + } + + retryTranscription(evidenceId: number) { + this.activityService.retryTranscription(evidenceId).subscribe({ + next: () => { + this.refreshStatus(); + }, + error: (err) => { + console.error('Error al reintentar transcripción:', err); + alert('No se pudo reintentar la transcripción.'); + } + }); + } + + deleteExistingEvidence(evidenceId: number) { + if (confirm('¿Está seguro de eliminar esta evidencia?')) { + this.activityService.deleteEvidence(evidenceId).subscribe({ + next: () => { + this.existingEvidences = this.existingEvidences.filter(e => e.id !== evidenceId); + }, + error: (err) => { + console.error('Error al eliminar evidencia:', err); + alert('No se pudo eliminar la evidencia.'); + } + }); + } + } + + updateEvidenceDescription(evidence: any) { + this.activityService.updateEvidence(evidence.id, { description: evidence.description }).subscribe({ + error: (err) => { + console.error('Error al actualizar descripción:', err); + } + }); + } + + openPreview(ev: any) { + const baseUrl = 'http://192.168.1.74:8000/'; + this.previewModal.set({ + type: ev.media_type, + url: baseUrl + ev.file_path, + description: ev.description || '' + }); + } + + closePreview() { + this.previewModal.set(null); + } + + async startCamera() { + try { + this.videoStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' }, audio: true }); + this.showCamera.set(true); + setTimeout(() => { + const videoElement = document.querySelector('video#cameraPreview') as HTMLVideoElement; + if (videoElement) videoElement.srcObject = this.videoStream; + }, 100); + } catch (err) { + console.error('Error al acceder a la cámara:', err); + alert('No se pudo acceder a la cámara.'); + } + } + + stopCamera() { + if (this.videoStream) { + this.videoStream.getTracks().forEach(track => track.stop()); + this.videoStream = null; + } + this.showCamera.set(false); + this.isRecordingVideo.set(false); + } + + takePhoto() { + const video = document.querySelector('video#cameraPreview') as HTMLVideoElement; + if (!video) return; + + const canvas = document.createElement('canvas'); + canvas.width = video.videoWidth; + canvas.height = video.videoHeight; + const ctx = canvas.getContext('2d'); + ctx?.drawImage(video, 0, 0); + + canvas.toBlob((blob) => { + if (blob) { + const file = new File([blob], `photo_${Date.now()}.jpg`, { type: 'image/jpeg' }); + this.addFile(file); + this.stopCamera(); + } + }, 'image/jpeg', 0.8); + } + + toggleVideoRecording() { + if (this.isRecordingVideo()) { + this.cameraRecorder?.stop(); + this.isRecordingVideo.set(false); + } else { + this.startVideoRecording(); + } + } + + private startVideoRecording() { + if (!this.videoStream) return; + this.videoChunks = []; + this.cameraRecorder = new MediaRecorder(this.videoStream); + this.cameraRecorder.ondataavailable = (e) => this.videoChunks.push(e.data); + this.cameraRecorder.onstop = () => { + const blob = new Blob(this.videoChunks, { type: 'video/mp4' }); + const file = new File([blob], `video_${Date.now()}.mp4`, { type: 'video/mp4' }); + this.addFile(file); + this.stopCamera(); + }; + this.cameraRecorder.start(); + this.isRecordingVideo.set(true); + } + + onFileSelected(event: any) { + const files = event.target.files; + if (files) { + for (let i = 0; i < files.length; i++) { + this.addFile(files[i]); + } + } + } + + private addFile(file: File) { + const reader = new FileReader(); + reader.onload = (e: any) => { + this.selectedFiles.push({ + file, + description: '', + capturedAt: new Date().toISOString(), + previewUrl: e.target.result + }); + }; + reader.readAsDataURL(file); + } + + removeFile(index: number) { + this.selectedFiles.splice(index, 1); + } + + addNC() { + this.ncEntries.update(list => [...list, { level: 'minor', description: '' }]); + } + + removeNC(index: number) { + this.ncEntries.update(list => list.filter((_, i) => i !== index)); + } + + onSubmit() { + if (this.activityForm.invalid) return; + + this.loading.set(true); + const activityData = { + ...this.activityForm.value, + project_id: Number(this.activityForm.value.project_id), + specialty_id: Number(this.activityForm.value.specialty_id) + }; + + if (this.editMode() && this.activityId) { + this.activityService.updateActivity(this.activityId, activityData).subscribe({ + next: (res) => this.processPostSave(res.id), + error: () => this.loading.set(false) + }); + } else { + this.activityService.createActivity(activityData).subscribe({ + next: (res) => this.processPostSave(res.id), + error: () => this.loading.set(false) + }); + } + } + + private processPostSave(activityId: number) { + const tasks = []; + + // Upload Evidences + if (this.selectedFiles.length > 0) { + tasks.push(...this.selectedFiles.map(item => + this.activityService.uploadEvidence(activityId, item.file, item.description, item.capturedAt) + )); + } + + // Sync NCs + if (this.ncEntries().length > 0) { + tasks.push(...this.ncEntries().map(nc => { + if (nc.id) { + // Update existing NC + return this.ncService.updateNC(nc.id, { + level: nc.level, + description: nc.description + }); + } else { + // Create new NC + return this.ncService.createNC({ + ...nc, + activity_id: activityId, + status: 'open' + }); + } + })); + } + + if (tasks.length > 0) { + forkJoin(tasks).subscribe({ + next: () => this.afterSubmit(), + error: () => this.afterSubmit() + }); + } else { + this.afterSubmit(); + } + } + + private handleUploads(activityId: number) { + // Deprecated for processPostSave + } + + afterSubmit() { + this.loading.set(false); + this.router.navigate(['/activities']); + } +} diff --git a/frontend/src/app/components/activity-list/activity-list.html b/frontend/src/app/components/activity-list/activity-list.html new file mode 100644 index 0000000..1aedb56 --- /dev/null +++ b/frontend/src/app/components/activity-list/activity-list.html @@ -0,0 +1,169 @@ +
+ + +
+
+ + +
+
+
+
+
+ +
+
+ +
+
+
+ +

Pendiente

+
+ {{ pending().length }} +
+ +
+ +
+
+ + {{ getSpecialtyName(act.specialty_id) }} + + + + +
+
+ {{ act.project?.name || 'S/P' }} +

{{ act.area }}

+

{{ act.description | slice:0:80 }}{{ act.description.length > 80 ? '...' : '' }}

+
+ +
+ +
+

No hay pendientes

+
+
+
+ + +
+
+
+ +

En Proceso

+
+ {{ inProgress().length }} +
+ +
+ +
+
+ + {{ getSpecialtyName(act.specialty_id) }} + + + + +
+
+ {{ act.project?.name || 'S/P' }} +

{{ act.area }}

+

{{ act.description | slice:0:80 }}{{ act.description.length > 80 ? '...' : '' }}

+
+ +
+ +
+

Mueva algo aquí

+
+
+
+ + +
+
+
+ +

Finalizado

+
+ {{ done().length }} +
+ +
+ +
+
+ + {{ getSpecialtyName(act.specialty_id) }} + + + + +
+
+ {{ act.project?.name || 'S/P' }} +

{{ act.area }}

+

{{ act.description | slice:0:80 }}{{ act.description.length > 80 ? '...' : '' }}

+
+ +
+ +
+

Sin finalizadas

+
+
+
+
+
+ +
+ +
+
\ No newline at end of file diff --git a/frontend/src/app/components/activity-list/activity-list.scss b/frontend/src/app/components/activity-list/activity-list.scss new file mode 100644 index 0000000..e35073d --- /dev/null +++ b/frontend/src/app/components/activity-list/activity-list.scss @@ -0,0 +1,293 @@ +.activity-container { + padding: 24px; + max-width: 1400px; + margin: 0 auto; +} + +.page-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 32px; + + .title-area { + // h1 { + // font-size: 1.75rem; + // color: var(--brand-secondary); + // margin-bottom: 4px; + // } + + p { + color: var(--text-muted); + font-size: 0.95rem; + } + } + + @media (max-width: 640px) { + flex-direction: column; + gap: 16px; + } +} + +.filters-card { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px 24px; + margin-bottom: 32px; + background: var(--bg-surface); + border-radius: 16px; + box-shadow: var(--shadow-sm); + + .filter-group { + display: flex; + align-items: center; + gap: 12px; + + label { + font-weight: 700; + font-size: 0.8rem; + color: var(--text-muted); + text-transform: uppercase; + } + + select { + background: var(--bg-main); + border: 1px solid var(--border-color); + padding: 8px 16px; + border-radius: 10px; + font-size: 0.9rem; + color: var(--text-main); + + &:focus { + border-color: var(--brand-primary); + outline: none; + } + } + } +} + +.kanban-board { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 24px; + align-items: flex-start; + + @media (max-width: 1024px) { + grid-template-columns: 1fr; + + .kanban-column { + min-height: fit-content; + } + + .column-body { + min-height: fit-content; + } + } +} + +.kanban-column { + background: #f8fafc; + border-radius: 20px; + display: flex; + flex-direction: column; + min-height: calc(100vh - 350px); + border: 1px solid #e2e8f0; + + .column-header { + padding: 20px; + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid #e2e8f0; + + .title-icon { + display: flex; + align-items: center; + gap: 10px; + + h2 { + font-size: 1rem; + font-weight: 700; + margin: 0; + } + + &.pending { + color: #64748b; + } + + &.in-progress { + color: #b45309; + } + + &.done { + color: #059669; + } + } + + .count { + background: rgba(0, 0, 0, 0.05); + padding: 2px 10px; + border-radius: 50px; + font-size: 0.75rem; + font-weight: 700; + color: var(--text-muted); + } + } + + .column-body { + flex: 1; + padding: 12px; + display: flex; + flex-direction: column; + gap: 12px; + min-height: 100px; + } +} + +.kanban-card { + background: white; + border-radius: 14px; + padding: 16px; + border: 1px solid #e2e8f0; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.02); + cursor: grab; + transition: all 0.2s; + + &:active { + cursor: grabbing; + } + + &:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); + border-color: var(--brand-primary); + } + + .card-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 12px; + + .specialty-badge { + font-size: 0.65rem; + font-weight: 800; + padding: 3px 8px; + border-radius: 6px; + text-transform: uppercase; + + &.spec-1 { + background: #eff6ff; + color: #1e40af; + } + + &.spec-2 { + background: #fef2f2; + color: #991b1b; + } + + &.spec-3 { + background: #f0fdf4; + color: #166534; + } + + &.spec-4 { + background: #fffbeb; + color: #92400e; + } + } + + .edit-btn { + color: var(--text-muted); + + &:hover { + color: var(--brand-primary); + } + } + } + + .card-body { + margin-bottom: 16px; + + .project-tag { + display: block; + font-size: 0.7rem; + color: var(--brand-primary); + font-weight: 700; + text-transform: uppercase; + margin-bottom: 4px; + opacity: 0.8; + } + + h3 { + font-size: 0.95rem; + font-weight: 700; + margin-bottom: 6px; + color: var(--brand-secondary); + } + + p { + font-size: 0.85rem; + color: var(--text-muted); + line-height: 1.4; + } + } + + .card-footer { + display: flex; + justify-content: space-between; + align-items: center; + padding-top: 12px; + border-top: 1px solid #f1f5f9; + + .date { + font-size: 0.75rem; + color: var(--text-muted); + } + + .evidence-tag { + display: flex; + align-items: center; + gap: 4px; + color: var(--text-muted); + font-size: 0.75rem; + font-weight: 600; + } + } +} + +// CDK Drag & Drop Styles +.cdk-drag-preview { + box-sizing: border-box; + border-radius: 14px; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); +} + +.cdk-drag-placeholder { + opacity: 0.3; + border: 2px dashed var(--brand-primary); + border-radius: 14px; +} + +.cdk-drag-animating { + transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); +} + +.column-body.cdk-drop-list-dragging .kanban-card:not(.cdk-drag-placeholder) { + transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); +} + +.empty-column-msg { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + color: #cbd5e1; + font-size: 0.85rem; + font-weight: 500; + border: 2px dashed #e2e8f0; + border-radius: 14px; + margin: 8px; + max-height: 14rem; +} \ No newline at end of file diff --git a/frontend/src/app/components/activity-list/activity-list.spec.ts b/frontend/src/app/components/activity-list/activity-list.spec.ts new file mode 100644 index 0000000..f323ac6 --- /dev/null +++ b/frontend/src/app/components/activity-list/activity-list.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ActivityList } from './activity-list'; + +describe('ActivityList', () => { + let component: ActivityList; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ActivityList] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ActivityList); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/activity-list/activity-list.ts b/frontend/src/app/components/activity-list/activity-list.ts new file mode 100644 index 0000000..927ac32 --- /dev/null +++ b/frontend/src/app/components/activity-list/activity-list.ts @@ -0,0 +1,107 @@ +import { Component, inject, signal, OnInit, computed } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { FormsModule } from '@angular/forms'; +import { ActivityService } from '../../services/activity'; +import { ProjectService } from '../../services/project'; +import { LucideAngularModule, Plus, Image, Inbox, SquarePen, Clock, Play, CircleCheck, Calendar as CalendarIcon, LayoutDashboard } from 'lucide-angular'; +import { ActivityCalendarComponent } from '../activity-calendar/activity-calendar'; +import { + DragDropModule, + CdkDragDrop +} from '@angular/cdk/drag-drop'; + +@Component({ + selector: 'app-activity-list', + standalone: true, + imports: [CommonModule, RouterModule, FormsModule, LucideAngularModule, DragDropModule, ActivityCalendarComponent], + templateUrl: './activity-list.html', + styleUrl: './activity-list.scss' +}) +export class ActivityListComponent implements OnInit { + readonly Plus = Plus; + readonly Image = Image; + readonly Inbox = Inbox; + readonly SquarePen = SquarePen; + readonly Clock = Clock; + readonly Play = Play; + readonly CircleCheck = CircleCheck; + readonly CalendarIcon = CalendarIcon; + readonly LayoutDashboard = LayoutDashboard; + + currentView = signal<'board' | 'calendar'>('board'); + + private activityService = inject(ActivityService); + private projectService = inject(ProjectService); + + activities = signal([]); + projects = signal([]); + selectedProject: number | null = null; + loading = signal(false); + + // Kanban Columns + pending = computed(() => this.activities().filter(a => a.status === 'pending' || a.status === 'completed')); // Defaulting 'completed' to some column for now or renaming statuses + inProgress = computed(() => this.activities().filter(a => a.status === 'in-progress')); + done = computed(() => this.activities().filter(a => a.status === 'done')); + + // Define valid statuses + readonly STATUSES = ['pending', 'in-progress', 'done']; + + ngOnInit() { + this.loadProjects(); + this.loadActivities(); + } + + loadProjects() { + this.projectService.getProjects().subscribe(projs => this.projects.set(projs)); + } + + loadActivities() { + this.loading.set(true); + this.activityService.getActivities(this.selectedProject || undefined).subscribe({ + next: (acts) => { + // Map old status 'completed' to 'done' or 'pending' if needed + const mappedActs = acts.map(a => ({ + ...a, + status: a.status === 'completed' ? 'done' : a.status + })); + this.activities.set(mappedActs); + this.loading.set(false); + }, + error: () => this.loading.set(false) + }); + } + + drop(event: CdkDragDrop, newStatus: string) { + if (event.previousContainer === event.container) { + // Reordering in same column (local optimization not strictly needed for MVP but good for UX) + // Actually signal-based computed columns make this tricky without local state management + } else { + const item = event.item.data; + const oldStatus = item.status; + + // Update locally for immediate feedback + const updatedActs = this.activities().map(a => + a.id === item.id ? { ...a, status: newStatus } : a + ); + this.activities.set(updatedActs); + + // Update backend + this.activityService.updateActivity(item.id, { status: newStatus }).subscribe({ + error: () => { + // Revert on error + const revertedActs = this.activities().map(a => + a.id === item.id ? { ...a, status: oldStatus } : a + ); + this.activities.set(revertedActs); + alert('Error al actualizar el estado de la actividad.'); + } + }); + } + } + + getSpecialtyName(id: number): string { + const names: any = { 1: 'Civil', 2: 'Mecánica', 3: 'Eléctrica', 4: 'SSOMA' }; + return names[id] || 'Desconocida'; + } +} diff --git a/frontend/src/app/components/contractor-form/contractor-form.html b/frontend/src/app/components/contractor-form/contractor-form.html new file mode 100644 index 0000000..204a399 --- /dev/null +++ b/frontend/src/app/components/contractor-form/contractor-form.html @@ -0,0 +1,154 @@ +
+ + +
+
+ +
+ +
+
+

+ + Identificación de Empresa +

+
+ +
+ + +
+ +
+
+ + +
+
+ + +
+
+ +
+ +
+ + +
+
+
+ + +
+
+

+ + Información de Contacto +

+
+ +
+ +
+ + +
+
+ +
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ +
+ + +
+
+
+
+ + +
+
+
+

+ + Subcontratistas Asociados +

+

Empresas que trabajan bajo el mando de este contratista.

+
+
+ +
+ + + +
+ +
+
+
+ {{ sub.name }} + RUC: {{ sub.ruc }} +
+ +
+
+ Aún no hay subcontratistas registrados para esta empresa. +
+
+
+ +
+
+ + +
+
+

Cargando información del contratista...

+
+
+
\ No newline at end of file diff --git a/frontend/src/app/components/contractor-form/contractor-form.scss b/frontend/src/app/components/contractor-form/contractor-form.scss new file mode 100644 index 0000000..021c03d --- /dev/null +++ b/frontend/src/app/components/contractor-form/contractor-form.scss @@ -0,0 +1,311 @@ +.page-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + margin-bottom: 24px; + position: sticky; + top: -24px; + background: rgba(248, 250, 252, 0.8); + backdrop-filter: blur(12px); + z-index: 1000; + padding: 20px 0; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &.sticky-header { + margin-left: -24px; + margin-right: -24px; + padding-left: 24px; + padding-right: 24px; + border-bottom: 1px solid var(--border-color); + } + + .header-left { + display: flex; + align-items: center; + gap: 16px; + + .back-btn { + background: white; + border: 1px solid var(--border-color); + width: 44px; + height: 44px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 14px; + color: var(--text-muted); + box-shadow: var(--shadow-sm); + transition: all 0.2s; + + &:hover { + border-color: var(--brand-primary); + color: var(--brand-primary); + transform: translateX(-2px); + } + } + + .title-meta { + h1 { + font-size: 1.5rem; + margin: 0; + color: var(--brand-secondary); + } + + .badge { + font-size: 0.75rem; + font-weight: 700; + color: var(--brand-primary); + background: rgba(180, 83, 9, 0.1); + padding: 2px 8px; + border-radius: 6px; + letter-spacing: 0.05em; + } + } + } + + .header-actions { + display: flex; + align-items: center; + gap: 12px; + + .cancel-btn { + background: white; + color: var(--text-muted); + font-weight: 700; + padding: 12px 20px; + font-size: 0.85rem; + border: 1px solid var(--border-color); + border-radius: var(--radius-md); + } + } +} + +.form-container { + max-width: 1200px; + margin: 0 auto; + padding-bottom: 80px; +} + +.contractor-edit-form { + display: flex; + flex-direction: column; + gap: 32px; +} + +.premium-card { + background: white; + padding: 32px; + border-radius: 24px; + border: 1px solid var(--border-color); + box-shadow: 0 10px 30px -5px rgba(0, 0, 0, 0.02); + + .section-header { + margin-bottom: 24px; + + &.flex-between { + display: flex; + justify-content: space-between; + align-items: flex-start; + } + + h3 { + font-size: 1.25rem; + margin: 0; + display: flex; + align-items: center; + gap: 10px; + } + + p { + margin: 6px 0 0; + font-size: 0.95rem; + color: var(--text-muted); + } + } + + label { + display: block; + font-size: 0.75rem; + font-weight: 800; + text-transform: uppercase; + color: var(--text-muted); + margin-bottom: 10px; + } +} + +.form-row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 24px; + + &.split { + gap: 32px; + + @media (max-width: 950px) { + grid-template-columns: 1fr; + } + } +} + +.input-with-icon { + position: relative; + + lucide-icon { + position: absolute; + left: 14px; + top: 50%; + transform: translateY(-50%); + color: var(--text-muted); + } + + input { + padding-left: 42px !important; + } + + &:focus-within lucide-icon { + color: var(--brand-primary); + } +} + +input, +select { + width: 100%; + background: var(--bg-main); + border: 2px solid transparent; + padding: 14px 16px; + border-radius: 14px; + font-size: 0.95rem; + font-weight: 500; + transition: all 0.2s ease; + + &:focus { + outline: none; + background: white; + border-color: var(--brand-primary); + box-shadow: 0 0 0 4px rgba(180, 83, 9, 0.1); + } +} + +.status-toggle { + display: flex; + align-items: center; + gap: 12px; + + input[type="checkbox"] { + width: auto; + cursor: pointer; + } + + label { + margin: 0; + cursor: pointer; + text-transform: none; + font-size: 0.95rem; + color: var(--text-main); + } +} + +.add-sub-form { + display: grid; + grid-template-columns: 2fr 1fr auto; + gap: 16px; + margin-bottom: 24px; + background: var(--bg-main); + padding: 20px; + border-radius: 16px; + + .add-btn { + background: white; + color: var(--brand-primary); + border: 1px solid var(--border-color); + padding: 0 24px; + border-radius: 12px; + display: flex; + align-items: center; + gap: 8px; + + &:hover { + border-color: var(--brand-primary); + transform: translateY(-1px); + } + } +} + +.subs-list { + display: flex; + flex-direction: column; + gap: 12px; + + .sub-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px 20px; + background: white; + border: 1px solid var(--border-color); + border-radius: 16px; + transition: all 0.2s; + + &:hover { + border-color: var(--brand-primary); + box-shadow: var(--shadow-sm); + } + + .sub-name { + font-weight: 700; + color: var(--brand-secondary); + } + + .sub-ruc { + font-size: 0.8rem; + color: var(--text-muted); + margin-left: 12px; + } + + .remove-btn { + color: #94a3b8; + padding: 8px; + border-radius: 10px; + + &:hover { + background: #fee2e2; + color: #ef4444; + } + } + } + + .empty-subs { + text-align: center; + padding: 40px; + color: var(--text-muted); + background: #f8fafc; + border-radius: 16px; + border: 2px dashed var(--border-color); + } +} + +.loading-state { + text-align: center; + padding: 100px 0; + + .spinner { + width: 50px; + height: 50px; + border: 4px solid #f3f3f3; + border-top: 4px solid var(--brand-primary); + border-radius: 50%; + animation: spin 1s linear infinite; + margin: 0 auto 20px; + } +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } +} \ No newline at end of file diff --git a/frontend/src/app/components/contractor-form/contractor-form.ts b/frontend/src/app/components/contractor-form/contractor-form.ts new file mode 100644 index 0000000..07a1476 --- /dev/null +++ b/frontend/src/app/components/contractor-form/contractor-form.ts @@ -0,0 +1,161 @@ +import { Component, inject, signal, OnInit } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule, ActivatedRoute, Router } from '@angular/router'; +import { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { ContractorService } from '../../services/contractor'; +import { SpecialtyService } from '../../services/specialty'; +import { LucideAngularModule, ArrowLeft, Save, Building2, Phone, Mail, MapPin, User, Shield, Plus, X, Trash2 } from 'lucide-angular'; +import { forkJoin } from 'rxjs'; + +@Component({ + selector: 'app-contractor-form', + standalone: true, + imports: [CommonModule, RouterModule, FormsModule, ReactiveFormsModule, LucideAngularModule], + templateUrl: './contractor-form.html', + styleUrl: './contractor-form.scss' +}) +export class ContractorFormComponent implements OnInit { + readonly ArrowLeft = ArrowLeft; + readonly Save = Save; + readonly Building = Building2; + readonly Phone = Phone; + readonly Mail = Mail; + readonly MapPin = MapPin; + readonly User = User; + readonly Shield = Shield; + readonly Plus = Plus; + readonly X = X; + readonly Trash2 = Trash2; + + private fb = inject(FormBuilder); + private contractorService = inject(ContractorService); + private specialtyService = inject(SpecialtyService); + private route = inject(ActivatedRoute); + private router = inject(Router); + + contractorId: number | null = null; + loading = signal(false); + saving = signal(false); + contractorForm: FormGroup; + + specialties = signal([]); + allContractors = signal([]); + subcontractors = signal([]); + + newSubName = ''; + newSubRuc = ''; + + constructor() { + this.contractorForm = this.fb.group({ + name: ['', Validators.required], + ruc: [''], + contact_name: [''], + email: ['', [Validators.email]], + phone: [''], + address: [''], + specialty_id: [null], + parent_id: [null], + is_active: [true] + }); + } + + ngOnInit() { + this.loadSpecialties(); + + this.contractorId = Number(this.route.snapshot.paramMap.get('id')); + if (this.contractorId) { + this.loadContractor(this.contractorId); + } + } + + loadSpecialties() { + this.specialtyService.getSpecialties().subscribe(data => this.specialties.set(data)); + } + + loadContractor(id: number) { + this.loading.set(true); + this.contractorService.getContractor(id).subscribe({ + next: (data) => { + this.contractorForm.patchValue(data); + this.subcontractors.set(data.subcontractors || []); + this.loading.set(false); + }, + error: () => this.loading.set(false) + }); + } + + addSubcontractor() { + if (!this.newSubName) return; + + const sub = { + name: this.newSubName, + ruc: this.newSubRuc, + is_active: true, + parent_id: this.contractorId + }; + + if (this.contractorId) { + // Save immediately if we are in edit mode + this.contractorService.createContractor(sub).subscribe(newSub => { + this.subcontractors.update(list => [...list, newSub]); + this.newSubName = ''; + this.newSubRuc = ''; + }); + } else { + // Temporary add if creating new parent + this.subcontractors.update(list => [...list, sub]); + this.newSubName = ''; + this.newSubRuc = ''; + } + } + + removeSubcontractor(index: number) { + const sub = this.subcontractors()[index]; + if (sub.id) { + if (confirm('¿Eliminar subcontratista?')) { + this.contractorService.deleteContractor(sub.id).subscribe(() => { + this.subcontractors.update(list => list.filter((_, i) => i !== index)); + }); + } + } else { + this.subcontractors.update(list => list.filter((_, i) => i !== index)); + } + } + + onSubmit() { + if (this.contractorForm.invalid) return; + + this.saving.set(true); + const data = this.contractorForm.value; + + if (this.contractorId) { + this.contractorService.updateContractor(this.contractorId, data).subscribe({ + next: () => this.afterSave(), + error: () => this.saving.set(false) + }); + } else { + this.contractorService.createContractor(data).subscribe({ + next: (newParent) => { + // If there were temporary subs, update them with the new parent_id + if (this.subcontractors().length > 0) { + const subTasks = this.subcontractors().map(sub => + this.contractorService.createContractor({ ...sub, parent_id: newParent.id }) + ); + forkJoin(subTasks).subscribe({ + next: () => this.afterSave(), + error: () => this.afterSave() + }); + } else { + this.afterSave(); + } + }, + error: () => this.saving.set(false) + }); + } + } + + afterSave() { + this.saving.set(false); + this.router.navigate(['/contractors']); + } +} diff --git a/frontend/src/app/components/contractor-list/contractor-list.html b/frontend/src/app/components/contractor-list/contractor-list.html new file mode 100644 index 0000000..5c458c2 --- /dev/null +++ b/frontend/src/app/components/contractor-list/contractor-list.html @@ -0,0 +1,87 @@ +
+ + + +
+ +
+ + +
+
+ +
+
+
+ +
+
+

{{ contractor.name }}

+ RUC: {{ contractor.ruc || 'N/A' }} +
+
+
+ {{ contractor.is_active ? 'Activo' : 'Inactivo' }} +
+
+ +
+
+
+ + {{ contractor.contact_name || 'Sin contacto' }} +
+
+ + {{ contractor.phone }} +
+
+ + {{ contractor.email }} +
+
+
+ + +
+ + +
+ +

No se encontraron contratistas

+

Crea un nuevo contratista para comenzar la gestión.

+
+
+ + +
+
+
+
+
\ No newline at end of file diff --git a/frontend/src/app/components/contractor-list/contractor-list.scss b/frontend/src/app/components/contractor-list/contractor-list.scss new file mode 100644 index 0000000..d044270 --- /dev/null +++ b/frontend/src/app/components/contractor-list/contractor-list.scss @@ -0,0 +1,254 @@ +.page-container { + padding: 24px; + max-width: 1400px; + margin: 0 auto; +} + +.page-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 32px; + + .header-left { + h1 { + font-size: 2rem; + margin-bottom: 4px; + } + + .subtitle { + color: var(--text-muted); + font-size: 1rem; + } + } +} + +.filters-bar { + margin-bottom: 32px; + padding: 16px 24px !important; + + .search-box { + display: flex; + align-items: center; + gap: 12px; + background: var(--bg-main); + padding: 12px 18px; + border-radius: 14px; + border: 1px solid var(--border-color); + transition: all 0.2s; + + &:focus-within { + border-color: var(--brand-primary); + background: white; + box-shadow: 0 0 0 4px rgba(180, 83, 9, 0.1); + } + + lucide-icon { + color: var(--text-muted); + } + + input { + border: none; + background: transparent; + width: 100%; + font-size: 0.95rem; + font-weight: 500; + color: var(--text-main); + + &:focus { + outline: none; + } + + &::placeholder { + color: #94a3b8; + } + } + } +} + +.contractors-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); + gap: 24px; +} + +.contractor-card { + display: flex; + flex-direction: column; + padding: 0 !important; + overflow: hidden; + height: 100%; + cursor: pointer; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &:hover { + transform: translateY(-4px); + box-shadow: 0 20px 40px -8px rgba(0, 0, 0, 0.08); + } + + .card-header { + padding: 24px; + display: flex; + justify-content: space-between; + align-items: flex-start; + border-bottom: 1px solid var(--bg-main); + + .company-brand { + display: flex; + gap: 16px; + + .logo-placeholder { + width: 52px; + height: 52px; + background: linear-gradient(135deg, var(--brand-secondary) 0%, #334155 100%); + border-radius: 14px; + display: flex; + align-items: center; + justify-content: center; + color: white; + } + + .company-info { + h3 { + font-size: 1.1rem; + margin: 0; + color: var(--brand-secondary); + } + + .ruc { + font-size: 0.75rem; + color: var(--text-muted); + font-weight: 600; + } + } + } + + .status-badge { + font-size: 0.7rem; + font-weight: 800; + text-transform: uppercase; + padding: 4px 10px; + border-radius: 20px; + background: #f1f5f9; + color: #64748b; + + &.active { + background: #ecfdf5; + color: #10b981; + } + } + } + + .card-body { + padding: 24px; + flex: 1; + + .contact-details { + display: flex; + flex-direction: column; + gap: 12px; + + .detail-item { + display: flex; + align-items: center; + gap: 10px; + color: var(--text-main); + font-size: 0.9rem; + font-weight: 500; + + lucide-icon { + color: var(--text-muted); + } + + .truncate { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + } + } + + .card-footer { + padding: 16px 24px; + background: var(--bg-main); + display: flex; + justify-content: space-between; + align-items: center; + + .sub-count { + display: flex; + align-items: center; + gap: 6px; + font-size: 0.8rem; + font-weight: 700; + color: var(--brand-primary); + } + + .actions { + .icon-btn { + width: 36px; + height: 36px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; + background: transparent; + + &.delete { + color: #94a3b8; + + &:hover { + background: #fee2e2; + color: #ef4444; + } + } + } + } + } +} + +.empty-state { + grid-column: 1 / -1; + text-align: center; + padding: 60px 0; + color: var(--text-muted); + + lucide-icon { + margin-bottom: 20px; + color: var(--border-color); + } + + h3 { + color: var(--brand-secondary); + margin-bottom: 8px; + } +} + +.loading-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); + gap: 24px; + + .skeleton-card { + height: 250px; + background: #e2e8f0; + border-radius: 24px; + animation: pulse 1.5s infinite; + } +} + +@keyframes pulse { + 0% { + opacity: 0.6; + } + + 50% { + opacity: 0.3; + } + + 100% { + opacity: 0.6; + } +} \ No newline at end of file diff --git a/frontend/src/app/components/contractor-list/contractor-list.ts b/frontend/src/app/components/contractor-list/contractor-list.ts new file mode 100644 index 0000000..58a66b4 --- /dev/null +++ b/frontend/src/app/components/contractor-list/contractor-list.ts @@ -0,0 +1,76 @@ +import { Component, inject, signal, OnInit } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { ContractorService } from '../../services/contractor'; +import { LucideAngularModule, Plus, Search, Building2, Phone, Mail, MapPin, MoreVertical, Trash2, Edit, ChevronRight, Users } from 'lucide-angular'; +import { FormsModule } from '@angular/forms'; + +@Component({ + selector: 'app-contractor-list', + standalone: true, + imports: [CommonModule, RouterModule, LucideAngularModule, FormsModule], + templateUrl: './contractor-list.html', + styleUrl: './contractor-list.scss' +}) +export class ContractorListComponent implements OnInit { + readonly Plus = Plus; + readonly Search = Search; + readonly Building = Building2; + readonly Phone = Phone; + readonly Mail = Mail; + readonly MapPin = MapPin; + readonly MoreVertical = MoreVertical; + readonly Trash2 = Trash2; + readonly Edit = Edit; + readonly ChevronRight = ChevronRight; + readonly Users = Users; + + private contractorService = inject(ContractorService); + + contractors = signal([]); + filteredContractors = signal([]); + loading = signal(false); + searchTerm = signal(''); + + ngOnInit() { + this.loadContractors(); + } + + loadContractors() { + this.loading.set(true); + // Load only parent contractors by default for the main list + this.contractorService.getContractors(null, undefined, true).subscribe({ + next: (data) => { + this.contractors.set(data); + this.applyFilter(); + this.loading.set(false); + }, + error: () => this.loading.set(false) + }); + } + + applyFilter() { + const term = this.searchTerm().toLowerCase(); + if (!term) { + this.filteredContractors.set(this.contractors()); + return; + } + + this.filteredContractors.set( + this.contractors().filter(c => + c.name.toLowerCase().includes(term) || + c.ruc?.toLowerCase().includes(term) || + c.contact_name?.toLowerCase().includes(term) + ) + ); + } + + deleteContractor(id: number, event: Event) { + event.stopPropagation(); + if (confirm('¿Está seguro de eliminar este contratista? Se eliminarán también sus subcontratistas.')) { + this.contractorService.deleteContractor(id).subscribe(() => { + this.loadContractors(); + }); + } + } +} diff --git a/frontend/src/app/components/dashboard/dashboard.html b/frontend/src/app/components/dashboard/dashboard.html new file mode 100644 index 0000000..8788235 --- /dev/null +++ b/frontend/src/app/components/dashboard/dashboard.html @@ -0,0 +1,66 @@ +
+
+

Panel de Control

+

Bienvenido de nuevo al sistema de supervisión de FRITOS FRESH.

+
+ +
+
+
+ +
+
+

Proyectos Activos

+ {{ projectsCount().toString().padStart(2, '0') }} +
+
+ +
+
+ +
+
+

Actividades Hoy

+ {{ activitiesToday().toString().padStart(2, '0') }} +
+
+ +
+
+ +
+
+

No Conformidades

+ {{ nonConformitiesCount().toString().padStart(2, '0') }} +
+
+
+ +
+
+
+

Actividades Recientes

+ +
+ +
+
+
+
+
+ {{ act.area }} + {{ getTimeAgo(act.date) }} +
+

{{ act.project?.name || 'S/P' }} - {{ act.description | slice:0:100 }}{{ + act.description.length > 100 ? '...' : '' }}

+ ID Actividad: #{{ act.id }} +
+
+ +
+

No hay actividades registradas aún.

+
+
+
+
+
\ No newline at end of file diff --git a/frontend/src/app/components/dashboard/dashboard.scss b/frontend/src/app/components/dashboard/dashboard.scss new file mode 100644 index 0000000..8e55b2a --- /dev/null +++ b/frontend/src/app/components/dashboard/dashboard.scss @@ -0,0 +1,162 @@ +.dashboard { + max-width: 1200px; + margin: 0 auto; + + .welcome-section { + margin-bottom: 32px; + + h1 { + font-size: 1.8rem; + margin-bottom: 4px; + } + + p { + color: var(--text-muted); + font-size: 1rem; + } + } +} + +.stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + gap: 20px; + margin-bottom: 32px; +} + +.stat-card { + display: flex; + align-items: center; + gap: 16px; + padding: 20px; + + .stat-icon { + width: 48px; + height: 48px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + + &.gold { + background: rgba(180, 83, 9, 0.1); + color: var(--brand-primary); + } + + &.blue { + background: rgba(59, 130, 246, 0.1); + color: var(--brand-accent); + } + + &.red { + background: rgba(239, 68, 68, 0.1); + color: #ef4444; + } + } + + .stat-info { + h3 { + font-size: 0.85rem; + color: var(--text-muted); + font-weight: 500; + margin-bottom: 2px; + } + + .value { + font-size: 1.5rem; + font-weight: 700; + color: var(--brand-secondary); + } + } +} + +.activity-feed { + .card-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 24px; + + h2 { + font-size: 1.1rem; + } + + .text-btn { + background: none; + color: var(--brand-primary); + font-size: 0.85rem; + font-weight: 600; + } + } +} + +.feed-list { + display: flex; + flex-direction: column; + gap: 20px; +} + +.feed-item { + display: flex; + gap: 16px; + + .dot { + width: 10px; + height: 10px; + background: var(--border-color); + border-radius: 50%; + margin-top: 6px; + flex-shrink: 0; + + &.active { + background: var(--brand-primary); + box-shadow: 0 0 0 4px rgba(180, 83, 9, 0.1); + } + } + + .item-content { + flex: 1; + + .header { + display: flex; + justify-content: space-between; + margin-bottom: 4px; + + strong { + font-size: 0.95rem; + color: var(--brand-secondary); + } + + .time { + font-size: 0.75rem; + color: var(--text-muted); + } + } + + p { + font-size: 0.9rem; + color: var(--text-main); + margin-bottom: 4px; + } + + .author { + font-size: 0.8rem; + color: var(--text-muted); + font-weight: 500; + } + } +} + +.empty-feed { + padding: 32px; + text-align: center; + background: var(--bg-main); + border-radius: 12px; + border: 2px dashed var(--border-color); + + p { + color: var(--text-muted); + font-size: 0.9rem; + font-weight: 500; + } +} \ No newline at end of file diff --git a/frontend/src/app/components/dashboard/dashboard.spec.ts b/frontend/src/app/components/dashboard/dashboard.spec.ts new file mode 100644 index 0000000..3cbcb8a --- /dev/null +++ b/frontend/src/app/components/dashboard/dashboard.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Dashboard } from './dashboard'; + +describe('Dashboard', () => { + let component: Dashboard; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Dashboard] + }) + .compileComponents(); + + fixture = TestBed.createComponent(Dashboard); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/dashboard/dashboard.ts b/frontend/src/app/components/dashboard/dashboard.ts new file mode 100644 index 0000000..3a1b676 --- /dev/null +++ b/frontend/src/app/components/dashboard/dashboard.ts @@ -0,0 +1,68 @@ +import { Component, inject, signal, OnInit, computed } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { LucideAngularModule, HardHat, ClipboardList, AlertTriangle } from 'lucide-angular'; +import { ActivityService } from '../../services/activity'; +import { ProjectService } from '../../services/project'; + +@Component({ + selector: 'app-dashboard', + standalone: true, + imports: [CommonModule, LucideAngularModule, RouterModule], + templateUrl: './dashboard.html', + styleUrl: './dashboard.scss', +}) +export class DashboardComponent implements OnInit { + readonly HardHat = HardHat; + readonly ClipboardList = ClipboardList; + readonly AlertTriangle = AlertTriangle; + + private activityService = inject(ActivityService); + private projectService = inject(ProjectService); + + activities = signal([]); + projectsCount = signal(0); + + // Stats + activitiesToday = computed(() => { + const today = new Date().toDateString(); + return this.activities().filter(a => new Date(a.date).toDateString() === today).length; + }); + + nonConformitiesCount = computed(() => { + // Total NCs across all activities + return this.activities().reduce((acc, a) => acc + (a.non_conformities?.length || 0), 0); + }); + + recentActivities = computed(() => { + return this.activities() + .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()) + .slice(0, 5); + }); + + ngOnInit() { + this.loadData(); + } + + loadData() { + this.projectService.getProjects().subscribe(projs => { + this.projectsCount.set(projs.length); + }); + + this.activityService.getActivities().subscribe(acts => { + this.activities.set(acts); + }); + } + + getTimeAgo(dateStr: string): string { + const date = new Date(dateStr); + const now = new Date(); + const diffMs = now.getTime() - date.getTime(); + const diffMin = Math.round(diffMs / 60000); + + if (diffMin < 60) return `Hace ${diffMin}m`; + const diffHours = Math.round(diffMin / 60); + if (diffHours < 24) return `Hace ${diffHours}h`; + return date.toLocaleDateString(); + } +} diff --git a/frontend/src/app/components/header/header.html b/frontend/src/app/components/header/header.html new file mode 100644 index 0000000..176cf15 --- /dev/null +++ b/frontend/src/app/components/header/header.html @@ -0,0 +1,21 @@ +
+
+ + +
+ + +
\ No newline at end of file diff --git a/frontend/src/app/components/header/header.scss b/frontend/src/app/components/header/header.scss new file mode 100644 index 0000000..a93506d --- /dev/null +++ b/frontend/src/app/components/header/header.scss @@ -0,0 +1,122 @@ +.header { + height: var(--header-height); + background: var(--bg-surface); + border-bottom: 1px solid var(--border-color); + padding: 0 24px; + display: flex; + align-items: center; + justify-content: space-between; + position: sticky; + top: 0; + z-index: 90; + + @media (max-width: 768px) { + padding: 0 16px; + } +} + +.left-section { + display: flex; + align-items: center; + gap: 16px; + flex: 1; +} + +.menu-toggle { + background: transparent; + color: var(--text-main); + display: flex; + align-items: center; + justify-content: center; + padding: 8px; + border-radius: 8px; + + &:hover { + background: var(--bg-main); + } +} + +.search-bar { + background: var(--bg-main); + border-radius: 10px; + padding: 8px 16px; + display: flex; + align-items: center; + gap: 12px; + width: 100%; + max-width: 400px; + border: 1px solid transparent; + transition: all 0.2s; + + &:focus-within { + background: var(--bg-surface); + border-color: var(--brand-primary); + box-shadow: 0 0 0 2px rgba(180, 83, 9, 0.1); + } + + lucide-icon { + color: var(--text-muted); + } + + input { + background: transparent; + border: none; + color: var(--text-main); + font-size: 0.9rem; + width: 100%; + + &:focus { + outline: none; + } + } + + @media (max-width: 768px) { + display: none; // Hide search on mobile to save space + } +} + +.user-info { + display: flex; + align-items: center; + gap: 12px; + padding: 05px 30px; + background-color: #f0eae6; + border-radius: 0.7rem; + cursor: pointer; + + + .user-details { + text-align: right; + display: flex; + flex-direction: column; + + .name { + font-weight: 600; + font-size: 0.9rem; + } + + .role { + font-size: 0.75rem; + color: var(--text-muted); + text-transform: uppercase; + font-weight: 500; + } + + @media (max-width: 640px) { + display: none; + } + } + + .user-avatar { + width: 38px; + height: 38px; + background: var(--brand-secondary); + color: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-weight: 700; + font-size: 0.9rem; + } +} \ No newline at end of file diff --git a/frontend/src/app/components/header/header.spec.ts b/frontend/src/app/components/header/header.spec.ts new file mode 100644 index 0000000..f6d7d05 --- /dev/null +++ b/frontend/src/app/components/header/header.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Header } from './header'; + +describe('Header', () => { + let component: Header; + let fixture: ComponentFixture
; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Header] + }) + .compileComponents(); + + fixture = TestBed.createComponent(Header); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/header/header.ts b/frontend/src/app/components/header/header.ts new file mode 100644 index 0000000..837c089 --- /dev/null +++ b/frontend/src/app/components/header/header.ts @@ -0,0 +1,21 @@ +import { Component, inject, Output, EventEmitter } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { AuthService } from '../../services/auth'; +import { LucideAngularModule, Search, Menu } from 'lucide-angular'; + +@Component({ + selector: 'app-header', + standalone: true, + imports: [CommonModule, LucideAngularModule], + templateUrl: './header.html', + styleUrl: './header.scss' +}) +export class HeaderComponent { + readonly Search = Search; + readonly Menu = Menu; + + @Output() toggleSidebar = new EventEmitter(); + + private authService = inject(AuthService); + user = this.authService.currentUser; +} diff --git a/frontend/src/app/components/layout/layout.html b/frontend/src/app/components/layout/layout.html new file mode 100644 index 0000000..8f9ea33 --- /dev/null +++ b/frontend/src/app/components/layout/layout.html @@ -0,0 +1,14 @@ +
+ + +
+ + +
+ +
+
+ + + +
\ No newline at end of file diff --git a/frontend/src/app/components/layout/layout.scss b/frontend/src/app/components/layout/layout.scss new file mode 100644 index 0000000..3324dc5 --- /dev/null +++ b/frontend/src/app/components/layout/layout.scss @@ -0,0 +1,66 @@ +.layout-wrapper { + display: flex; + height: 100vh; + position: relative; + overflow: hidden; + + &.sidebar-collapsed { + app-sidebar { + width: var(--nav-width-collapsed); + } + } +} + +.main-container { + flex: 1; + display: flex; + flex-direction: column; + min-width: 0; // Prevent flex overflow + height: 100vh; + overflow: hidden; +} + +.content-area { + padding: 24px; + flex: 1; + overflow-y: auto; + + @media (max-width: 768px) { + padding: 16px; + } +} + +.sidebar-overlay { + background: rgba(0, 0, 0, 0.4); + position: fixed; + inset: 0; + z-index: 1000; + display: none; // Hidden by default + + @media (max-width: 1024px) { + display: block; + } +} + +app-sidebar { + display: block; + width: var(--nav-width); + height: 100vh; + position: sticky; + top: 0; + z-index: 1001; + transition: width 0.3s ease; + + @media (max-width: 1024px) { + position: fixed; + left: 0; + top: 0; + bottom: 0; + transform: translateX(-100%); + box-shadow: 20px 0 50px rgba(0, 0, 0, 0.1); + + &.open { + transform: translateX(0); + } + } +} \ No newline at end of file diff --git a/frontend/src/app/components/layout/layout.spec.ts b/frontend/src/app/components/layout/layout.spec.ts new file mode 100644 index 0000000..d6ba188 --- /dev/null +++ b/frontend/src/app/components/layout/layout.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Layout } from './layout'; + +describe('Layout', () => { + let component: Layout; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Layout] + }) + .compileComponents(); + + fixture = TestBed.createComponent(Layout); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/layout/layout.ts b/frontend/src/app/components/layout/layout.ts new file mode 100644 index 0000000..efd378e --- /dev/null +++ b/frontend/src/app/components/layout/layout.ts @@ -0,0 +1,20 @@ +import { Component, signal } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { SidebarComponent } from '../sidebar/sidebar'; +import { HeaderComponent } from '../header/header'; + +@Component({ + selector: 'app-layout', + standalone: true, + imports: [CommonModule, RouterModule, SidebarComponent, HeaderComponent], + templateUrl: './layout.html', + styleUrl: './layout.scss' +}) +export class LayoutComponent { + isSidebarOpen = signal(true); + + toggleSidebar() { + this.isSidebarOpen.update(v => !v); + } +} diff --git a/frontend/src/app/components/login/login.html b/frontend/src/app/components/login/login.html new file mode 100644 index 0000000..df61c8e --- /dev/null +++ b/frontend/src/app/components/login/login.html @@ -0,0 +1,42 @@ + \ No newline at end of file diff --git a/frontend/src/app/components/login/login.scss b/frontend/src/app/components/login/login.scss new file mode 100644 index 0000000..9416926 --- /dev/null +++ b/frontend/src/app/components/login/login.scss @@ -0,0 +1,107 @@ +.login-wrapper { + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + background: var(--bg-main); + padding: 24px; +} + +.login-card { + width: 100%; + max-width: 440px; + padding: 48px; + border-radius: 24px; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.04); + + @media (max-width: 480px) { + padding: 32px 24px; + } +} + +.login-header { + text-align: center; + margin-bottom: 40px; + + .logo-wrapper { + width: 80px; + height: 80px; + background: #fffbeb; + border-radius: 24px; + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 20px; + color: var(--brand-primary); + } + + h1 { + font-size: 1.5rem; + letter-spacing: 0.1em; + margin-bottom: 8px; + } + + p { + color: var(--text-muted); + font-size: 0.95rem; + font-weight: 500; + } +} + +.login-form { + display: flex; + flex-direction: column; + gap: 24px; + + .form-group { + display: flex; + flex-direction: column; + gap: 8px; + + label { + font-size: 0.75rem; + color: var(--text-muted); + font-weight: 700; + text-transform: uppercase; + padding-left: 4px; + } + + input { + width: 100%; + background: var(--bg-main); + border: 1px solid var(--border-color); + padding: 14px 16px; + border-radius: 12px; + font-size: 1rem; + transition: all 0.2s; + + &:focus { + outline: none; + border-color: var(--brand-primary); + background: var(--bg-surface); + box-shadow: 0 0 0 4px rgba(180, 83, 9, 0.05); + } + } + } + + .error-msg { + background: #fef2f2; + color: #ef4444; + padding: 12px; + border-radius: 10px; + font-size: 0.85rem; + font-weight: 600; + text-align: center; + border: 1px solid #fee2e2; + } +} + +.login-footer { + margin-top: 40px; + text-align: center; + + p { + font-size: 0.75rem; + color: var(--text-muted); + } +} \ No newline at end of file diff --git a/frontend/src/app/components/login/login.spec.ts b/frontend/src/app/components/login/login.spec.ts new file mode 100644 index 0000000..8d4c63e --- /dev/null +++ b/frontend/src/app/components/login/login.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Login } from './login'; + +describe('Login', () => { + let component: Login; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Login] + }) + .compileComponents(); + + fixture = TestBed.createComponent(Login); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/login/login.ts b/frontend/src/app/components/login/login.ts new file mode 100644 index 0000000..c372a19 --- /dev/null +++ b/frontend/src/app/components/login/login.ts @@ -0,0 +1,41 @@ +import { Component, inject, signal } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { AuthService } from '../../services/auth'; +import { Router } from '@angular/router'; +import { LucideAngularModule, Shield } from 'lucide-angular'; + +@Component({ + selector: 'app-login', + standalone: true, + imports: [CommonModule, FormsModule, LucideAngularModule], + templateUrl: './login.html', + styleUrl: './login.scss' +}) +export class LoginComponent { + readonly Shield = Shield; + private authService = inject(AuthService); + private router = inject(Router); + + username = 'admin@fritosfresh.com'; + password = 'secret'; + loading = signal(false); + error = signal(null); + + onSubmit(event: Event) { + event.preventDefault(); + this.loading.set(true); + this.error.set(null); + + this.authService.login({ username: this.username, password: this.password }).subscribe({ + next: () => { + this.router.navigate(['/dashboard']); + }, + error: (_err: any) => { + console.log(_err); + this.loading.set(false); + this.error.set('Credenciales incorrectas. Intente nuevamente.'); + } + }); + } +} diff --git a/frontend/src/app/components/non-conformity-form/non-conformity-form.html b/frontend/src/app/components/non-conformity-form/non-conformity-form.html new file mode 100644 index 0000000..56c3262 --- /dev/null +++ b/frontend/src/app/components/non-conformity-form/non-conformity-form.html @@ -0,0 +1,211 @@ + + +
+
+ +

Cargando detalles...

+
+ +
+ + +
+
+ + +
+
+ +
+ + + +
+
+
+ + +
+
+ + +
+ +
+
+ + +
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+

Lista de Acciones Correctivas

+

Pasos necesarios para solventar el hallazgo.

+
+ +
+ + +
+ +
+
+
+ + +
+ {{ item.text }} + +
+
+ No hay acciones programadas. +
+
+
+ + +
+
+

Evidencias Fotográficas

+

Registro visual del hallazgo y su resolución.

+
+ +
+
+
+ +
+ +
+
+
+ + +
+
+
+ +
+
+ + Subir archivos +
+ + + +
+ +
+
+ + + +
+
+
+
+
+ + +
+ +
+ + +
+
+ +
+ + +
+
+
\ No newline at end of file diff --git a/frontend/src/app/components/non-conformity-form/non-conformity-form.scss b/frontend/src/app/components/non-conformity-form/non-conformity-form.scss new file mode 100644 index 0000000..73af928 --- /dev/null +++ b/frontend/src/app/components/non-conformity-form/non-conformity-form.scss @@ -0,0 +1,525 @@ +.page-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + margin-bottom: 24px; + position: sticky; + top: -24px; + background: rgba(248, 250, 252, 0.8); + backdrop-filter: blur(12px); + z-index: 1000; + padding: 20px 0; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &.sticky-header { + margin-left: -24px; + margin-right: -24px; + padding-left: 24px; + padding-right: 24px; + border-bottom: 1px solid var(--border-color); + } + + .header-left { + display: flex; + align-items: center; + gap: 16px; + + .back-btn { + background: white; + border: 1px solid var(--border-color); + width: 44px; + height: 44px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 14px; + color: var(--text-muted); + box-shadow: var(--shadow-sm); + transition: all 0.2s; + + &:hover { + border-color: var(--brand-primary); + color: var(--brand-primary); + transform: translateX(-2px); + box-shadow: 0 4px 12px rgba(180, 83, 9, 0.1); + } + } + + .title-meta { + h1 { + font-size: 1.5rem; + margin: 0; + color: var(--brand-secondary); + background: linear-gradient(135deg, var(--brand-secondary) 0%, #334155 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } + + .badge { + font-size: 0.75rem; + font-weight: 700; + color: var(--brand-primary); + background: rgba(180, 83, 9, 0.1); + padding: 2px 8px; + border-radius: 6px; + letter-spacing: 0.05em; + } + } + } + + .header-actions { + display: flex; + align-items: center; + gap: 12px; + + .cancel-btn { + background: white; + color: var(--text-muted); + font-weight: 700; + padding: 12px 20px; + font-size: 0.85rem; + text-transform: uppercase; + border: 1px solid var(--border-color); + border-radius: var(--radius-md); + + &:hover { + color: var(--brand-secondary); + border-color: var(--text-muted); + } + } + + .gold-button { + height: 46px; + padding: 0 24px; + font-size: 0.85rem; + text-transform: uppercase; + letter-spacing: 0.05em; + border-radius: var(--radius-md); + background: linear-gradient(135deg, var(--brand-primary) 0%, #92400e 100%); + } + } +} + +.form-container { + max-width: 1000px; + margin: 0 auto; + padding-bottom: 80px; +} + +.nc-edit-form { + display: flex; + flex-direction: column; + gap: 32px; +} + +.premium-card { + background: white; + padding: 32px; + border-radius: 24px; + border: 1px solid var(--border-color); + box-shadow: 0 10px 30px -5px rgba(0, 0, 0, 0.02); + transition: all 0.3s ease; + + &:hover { + box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.05); + border-color: var(--border-hover); + } + + .section-header { + margin-bottom: 24px; + + h3 { + font-size: 1.25rem; + margin: 0; + display: flex; + align-items: center; + gap: 10px; + } + + p { + margin: 6px 0 0; + font-size: 0.95rem; + color: var(--text-muted); + } + } + + label { + display: block; + font-size: 0.75rem; + font-weight: 800; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--text-muted); + margin-bottom: 10px; + } +} + +.form-row.split { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 32px; + + @media (max-width: 850px) { + grid-template-columns: 1fr; + } +} + +.input-with-icon { + position: relative; + + lucide-icon { + position: absolute; + left: 14px; + top: 50%; + transform: translateY(-50%); + color: var(--text-muted); + transition: color 0.2s; + } + + input, + select { + padding-left: 42px !important; + } + + &:focus-within lucide-icon { + color: var(--brand-primary); + } +} + +input, +select, +textarea { + width: 100%; + background: var(--bg-main); + border: 2px solid transparent; + padding: 14px 16px; + border-radius: 14px; + font-size: 0.95rem; + font-weight: 500; + color: var(--text-main); + transition: all 0.2s ease; + + &::placeholder { + color: #94a3b8; + } + + &:focus { + outline: none; + background: white; + border-color: var(--brand-primary); + box-shadow: 0 0 0 4px rgba(180, 83, 9, 0.1); + } +} + +.status-select { + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; + + &.open { + color: #ef4444; + background: rgba(239, 68, 68, 0.05); + } + + &.in-progress { + color: #3b82f6; + background: rgba(59, 130, 246, 0.05); + } + + &.re-inspect { + color: #f59e0b; + background: rgba(245, 158, 11, 0.05); + } + + &.resolved { + color: #10b981; + background: rgba(16, 185, 129, 0.05); + } + + &.closed { + color: #64748b; + background: rgba(100, 116, 139, 0.05); + } +} + +.level-selector { + display: flex; + gap: 12px; + + .level-btn { + flex: 1; + padding: 12px; + border-radius: 12px; + font-size: 0.85rem; + font-weight: 700; + border: 2px solid var(--border-color); + background: transparent; + color: var(--text-muted); + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + + &:hover { + border-color: var(--text-muted); + } + + &.active { + color: white; + transform: translateY(-2px); + + &.minor { + background: #10b981; + border-color: #10b981; + box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3); + } + + &.major { + background: #f59e0b; + border-color: #f59e0b; + box-shadow: 0 4px 12px rgba(245, 158, 11, 0.3); + } + + &.critical { + background: #ef4444; + border-color: #ef4444; + box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3); + } + } + } +} + +.checklist-items { + padding-top: 16px; + + .empty-state { + padding: 16px; + border-radius: 16px; + border: 2px dashed var(--border-color); + } + + .checklist-item { + display: flex; + align-items: center; + gap: 16px; + padding: 16px; + background: var(--bg-main); + border-radius: 16px; + margin-bottom: 12px; + transition: transform 0.2s ease; + + &:hover { + transform: translateX(4px); + background: #f1f5f9; + } + + .check-toggle { + cursor: pointer; + width: 24px; + height: 24px; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + color: var(--text-muted); + transition: all 0.2s; + background: white; + border: 2px solid var(--border-color); + + &.completed { + background: #10b981; + border-color: #10b981; + color: white; + } + } + + .action-text { + flex: 1; + font-size: 0.95rem; + font-weight: 500; + color: var(--text-main); + + &.completed { + text-decoration: line-through; + color: var(--text-muted); + } + } + + .remove-btn { + color: #ef4444; + opacity: 0; + transition: opacity 0.2s; + padding: 4px; + border-radius: 8px; + + &:hover { + background: rgba(239, 68, 68, 0.1); + } + } + + &:hover .remove-btn { + opacity: 0.6; + } + } +} + +.evidence-card { + .existing-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); + gap: 20px; + margin-bottom: 32px; + + .evidence-thumb { + border-radius: 16px; + overflow: hidden; + background: white; + border: 1px solid var(--border-color); + transition: all 0.3s ease; + + &:hover { + transform: translateY(-4px); + box-shadow: var(--shadow-md); + } + + .thumb-media { + height: 140px; + position: relative; + cursor: pointer; + + img { + width: 100%; + height: 100%; + object-fit: cover; + } + + .overlay { + position: absolute; + inset: 0; + background: rgba(15, 23, 42, 0.4); + display: flex; + align-items: center; + justify-content: center; + color: white; + opacity: 0; + transition: opacity 0.2s; + backdrop-filter: blur(2px); + } + + &:hover .overlay { + opacity: 1; + } + } + + .thumb-actions { + padding: 12px; + display: flex; + flex-direction: column; + gap: 8px; + + textarea { + font-size: 0.8rem; + padding: 8px; + border-radius: 10px; + border: 1px solid var(--border-color); + background: var(--bg-main); + } + + .del-btn { + color: #ef4444; + font-size: 0.75rem; + font-weight: 700; + align-self: flex-end; + display: flex; + align-items: center; + gap: 4px; + padding: 4px 8px; + border-radius: 6px; + + &:hover { + background: rgba(239, 68, 68, 0.05); + } + } + } + } + } + + .upload-area { + display: flex; + gap: 16px; + + .upload-btn, + .camera-btn { + flex: 1; + height: 80px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + background: var(--bg-main); + border: 2px dashed var(--border-color); + border-radius: 20px; + color: var(--text-muted); + cursor: pointer; + transition: all 0.2s; + + span { + font-size: 0.85rem; + font-weight: 700; + } + + &:hover { + color: var(--brand-primary); + border-color: var(--brand-primary); + background: white; + transform: scale(1.02); + } + } + } +} + +.preview-modal-overlay { + backdrop-filter: blur(16px); + + .modal-content { + border-radius: 32px; + box-shadow: 0 40px 100px -20px rgba(0, 0, 0, 0.5); + } +} + +.camera-overlay { + .camera-actions { + .photo { + width: 80px; + height: 80px; + border-radius: 50%; + padding: 0; + border: 6px solid rgba(255, 255, 255, 0.3); + background: white; + + &:active { + transform: scale(0.9); + } + } + } +} + +.add-action { + display: flex; + gap: 8px; + + .add-action-btn { + background: white; + color: var(--text-muted); + font-weight: 700; + padding: 0 20px; + font-size: 0.85rem; + text-transform: uppercase; + border: 1px solid var(--border-color); + border-radius: var(--radius-md); + + &:hover { + color: var(--brand-secondary); + border-color: var(--text-muted); + } + } +} \ No newline at end of file diff --git a/frontend/src/app/components/non-conformity-form/non-conformity-form.ts b/frontend/src/app/components/non-conformity-form/non-conformity-form.ts new file mode 100644 index 0000000..b95a7ff --- /dev/null +++ b/frontend/src/app/components/non-conformity-form/non-conformity-form.ts @@ -0,0 +1,254 @@ +import { Component, inject, signal, OnInit } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule, ActivatedRoute, Router } from '@angular/router'; +import { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { NonConformityService } from '../../services/non-conformity'; +import { ActivityService } from '../../services/activity'; +import { forkJoin } from 'rxjs'; +import { + LucideAngularModule, ArrowLeft, TriangleAlert, CircleCheck, Clock, ListPlus, X, + Save, Camera, Trash2, Calendar, User, FileText, Maximize2, ExternalLink, RefreshCw +} from 'lucide-angular'; + +@Component({ + selector: 'app-non-conformity-form', + standalone: true, + imports: [CommonModule, RouterModule, FormsModule, ReactiveFormsModule, LucideAngularModule], + templateUrl: './non-conformity-form.html', + styleUrl: './non-conformity-form.scss' +}) +export class NonConformityFormComponent implements OnInit { + // Icons + readonly ArrowLeft = ArrowLeft; + readonly AlertTriangle = TriangleAlert; + readonly CheckCircle = CircleCheck; + readonly Clock = Clock; + readonly Plus = ListPlus; + readonly X = X; + readonly Save = Save; + readonly Camera = Camera; + readonly Trash2 = Trash2; + readonly Calendar = Calendar; + readonly User = User; + readonly FileText = FileText; + readonly Maximize2 = Maximize2; + readonly ExternalLink = ExternalLink; + readonly RefreshCw = RefreshCw; + + private fb = inject(FormBuilder); + private ncService = inject(NonConformityService); + private activityService = inject(ActivityService); + private route = inject(ActivatedRoute); + private router = inject(Router); + + ncId: number | null = null; + loading = signal(false); + ncForm: FormGroup; + + // Dynamic collections + checklist = signal([]); + newActionText = ''; + + existingEvidences = signal([]); + selectedFiles = signal([]); // { file, previewUrl, description, capturedAt } + + // Meta options + ncTypes = [ + 'Errores humanos', + 'Fallas en los procesos', + 'Problemas de diseño', + 'Cambios no controlados', + 'Falta de comunicación' + ]; + + // Camera Support + showCamera = signal(false); + private videoStream: MediaStream | null = null; + + // Preview Modal + previewModal = signal<{ type: string, url: string, description: string } | null>(null); + + constructor() { + this.ncForm = this.fb.group({ + level: ['minor', Validators.required], + description: ['', Validators.required], + status: ['open', Validators.required], + due_date: [null], + responsible_person: [''], + nc_type: [null], + impact_description: [''], + closure_description: [''] + }); + } + + ngOnInit() { + this.ncId = Number(this.route.snapshot.paramMap.get('id')); + if (this.ncId) { + this.loadNC(this.ncId); + } + } + + loadNC(id: number) { + this.loading.set(true); + this.ncService.getNC(id).subscribe({ + next: (nc) => { + this.ncForm.patchValue({ + level: nc.level, + description: nc.description, + status: nc.status, + due_date: nc.due_date ? new Date(nc.due_date).toISOString().slice(0, 16) : null, + responsible_person: nc.responsible_person, + nc_type: nc.nc_type, + impact_description: nc.impact_description, + closure_description: nc.closure_description + }); + this.checklist.set(nc.action_checklist || []); + this.existingEvidences.set(nc.evidences || []); + this.loading.set(false); + }, + error: () => this.loading.set(false) + }); + } + + // Checklist Helpers + addAction() { + if (!this.newActionText.trim()) return; + this.checklist.update(list => [...list, { text: this.newActionText.trim(), completed: false }]); + this.newActionText = ''; + } + + removeAction(index: number) { + this.checklist.update(list => list.filter((_, i) => i !== index)); + } + + toggleAction(index: number) { + this.checklist.update(list => { + const newList = [...list]; + newList[index].completed = !newList[index].completed; + return newList; + }); + } + + // Evidence Helpers + onFileSelected(event: any) { + const files: FileList = event.target.files; + for (let i = 0; i < files.length; i++) { + this.addFile(files[i]); + } + } + + addFile(file: File) { + const reader = new FileReader(); + reader.onload = (e: any) => { + this.selectedFiles.update(prev => [...prev, { + file: file, + previewUrl: e.target.result, + description: '', + capturedAt: new Date().toISOString() + }]); + }; + reader.readAsDataURL(file); + } + + removeSelectedFile(index: number) { + this.selectedFiles.update(prev => prev.filter((_, i) => i !== index)); + } + + deleteExistingEvidence(evId: number) { + if (confirm('¿Eliminar esta evidencia?')) { + this.activityService.deleteEvidence(evId).subscribe(() => { + this.existingEvidences.update(list => list.filter(e => e.id !== evId)); + }); + } + } + + updateEvidenceDescription(ev: any) { + this.activityService.updateEvidence(ev.id, { description: ev.description }).subscribe(); + } + + // Camera Support + async startCamera() { + try { + this.videoStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } }); + this.showCamera.set(true); + setTimeout(() => { + const video = document.getElementById('ncCameraPreview') as HTMLVideoElement; + if (video) video.srcObject = this.videoStream; + }, 100); + } catch (err) { + alert('No se pudo acceder a la cámara'); + } + } + + stopCamera() { + if (this.videoStream) { + this.videoStream.getTracks().forEach(t => t.stop()); + this.videoStream = null; + } + this.showCamera.set(false); + } + + takePhoto() { + const video = document.getElementById('ncCameraPreview') as HTMLVideoElement; + if (!video) return; + + const canvas = document.createElement('canvas'); + canvas.width = video.videoWidth; + canvas.height = video.videoHeight; + canvas.getContext('2d')?.drawImage(video, 0, 0); + + canvas.toBlob(blob => { + if (blob) { + const file = new File([blob], `nc_photo_${Date.now()}.jpg`, { type: 'image/jpeg' }); + this.addFile(file); + this.stopCamera(); + } + }, 'image/jpeg'); + } + + // Preview Modal + openPreview(ev: any) { + const baseUrl = 'http://192.168.1.74:8000/'; + this.previewModal.set({ + type: ev.media_type || 'image/jpeg', + url: ev.file_path.startsWith('http') ? ev.file_path : baseUrl + ev.file_path, + description: ev.description || '' + }); + } + + closePreview() { + this.previewModal.set(null); + } + + onSubmit() { + if (this.ncForm.invalid || !this.ncId) return; + + this.loading.set(true); + const data = { + ...this.ncForm.value, + action_checklist: this.checklist() + }; + + this.ncService.updateNC(this.ncId, data).subscribe({ + next: () => { + if (this.selectedFiles().length > 0) { + const uploads = this.selectedFiles().map(f => + this.ncService.uploadEvidence(this.ncId!, f.file, f.description, f.capturedAt) + ); + forkJoin(uploads).subscribe({ + next: () => this.afterSave(), + error: () => this.afterSave() + }); + } else { + this.afterSave(); + } + }, + error: () => this.loading.set(false) + }); + } + + afterSave() { + this.loading.set(false); + this.router.navigate(['/non-conformities']); + } +} diff --git a/frontend/src/app/components/non-conformity-list/non-conformity-list.html b/frontend/src/app/components/non-conformity-list/non-conformity-list.html new file mode 100644 index 0000000..9a41fcc --- /dev/null +++ b/frontend/src/app/components/non-conformity-list/non-conformity-list.html @@ -0,0 +1,90 @@ +
+ + +
+ + +
+
+
+ +

{{ col.title }}

+
+ {{ col.data.length }} +
+ +
+ +
+
+ + {{ getLevelLabel(nc.level) }} + + +
+ +
+

{{ nc.description }}

+ +
+
+ + {{ nc.responsible_person }} +
+
+ + {{ nc.due_date | date:'shortDate' + }} +
+
+ + {{ nc.nc_type }} +
+
+ +
+
+
+
+ {{ getChecklistProgress(nc) }}% +
+
+ + +
+ +
+

Sin hallazgos en esta etapa

+
+
+
+
+
+
\ No newline at end of file diff --git a/frontend/src/app/components/non-conformity-list/non-conformity-list.scss b/frontend/src/app/components/non-conformity-list/non-conformity-list.scss new file mode 100644 index 0000000..e068c18 --- /dev/null +++ b/frontend/src/app/components/non-conformity-list/non-conformity-list.scss @@ -0,0 +1,543 @@ +@import '../activity-list/activity-list.scss'; + +.nc-container { + @extend .activity-container; +} + +.level-badge { + @extend .specialty-badge; + + &.critical { + background: #fee2e2; + color: #991b1b; + } + + &.major { + background: #fff7ed; + color: #9a3412; + } + + &.minor { + background: #eff6ff; + color: #1e40af; + } + + &.closed { + background: #f0fdf4; + color: #166534; + } +} + +// Kanban Enhancements +.kanban-card { + &.overdue { + border: 2px solid #ef4444; + box-shadow: 0 0 10px rgba(239, 68, 68, 0.2); + + .card-header::after { + content: 'VENCIDA'; + font-size: 0.65rem; + font-weight: 800; + color: #ef4444; + background: #fef2f2; + padding: 2px 8px; + border-radius: 4px; + } + } + + .nc-desc { + font-size: 0.9rem; + color: var(--text-main); + margin-bottom: 12px; + line-height: 1.5; + } + + .nc-meta { + display: flex; + flex-direction: column; + gap: 6px; + margin-bottom: 12px; + + .meta-item { + display: flex; + align-items: center; + gap: 6px; + font-size: 0.75rem; + color: var(--text-muted); + + lucide-icon { + color: var(--brand-secondary); + } + + .text-danger { + color: #ef4444; + font-weight: 700; + } + } + } + + .progress-mini { + display: flex; + align-items: center; + gap: 8px; + margin-top: 12px; + + .progress-bar { + flex: 1; + height: 4px; + background: var(--bg-main); + border-radius: 10px; + overflow: hidden; + + .fill { + height: 100%; + background: var(--brand-primary); + transition: width 0.3s ease; + } + } + + span { + font-size: 0.7rem; + font-weight: 700; + color: var(--text-muted); + min-width: 30px; + } + } + + .card-actions { + display: flex; + gap: 8px; + + .action-btn { + width: 28px; + height: 28px; + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; + background: var(--bg-main); + color: var(--text-muted); + transition: all 0.2s; + + &:hover { + color: var(--brand-primary); + background: #fdf2f2; + } + + &.edit:hover { + background: #f0f9ff; + color: #0ea5e9; + } + } + } +} + +// Modal Styles +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.4); + backdrop-filter: blur(8px); + z-index: 10000; + display: flex; + align-items: center; + justify-content: center; + padding: 24px; +} + +.modal-content { + width: 100%; + max-width: 800px; + max-height: 90vh; + background: white; + border-radius: 24px; + display: flex; + flex-direction: column; + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); +} + +.modal-header { + padding: 24px; + border-bottom: 1px solid var(--border-color); + display: flex; + justify-content: space-between; + align-items: center; + + .modal-title { + display: flex; + align-items: center; + gap: 12px; + color: var(--brand-secondary); + + h2 { + font-size: 1.25rem; + margin: 0; + } + } + + .close-btn { + background: var(--bg-main); + border: none; + width: 36px; + height: 36px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + color: var(--text-muted); + cursor: pointer; + + &:hover { + background: #fef2f2; + color: #ef4444; + } + } +} + +.modal-body { + padding: 24px; + overflow-y: auto; + flex: 1; + + .form-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; + + @media (max-width: 640px) { + grid-template-columns: 1fr; + } + + .full-width { + grid-column: span 2; + + @media (max-width: 640px) { + grid-column: span 1; + } + } + } + + .form-group { + display: flex; + flex-direction: column; + gap: 8px; + + label { + font-size: 0.75rem; + font-weight: 800; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 0.05em; + } + + input, + select, + textarea { + background: var(--bg-main); + border: 1px solid var(--border-color); + border-radius: 12px; + padding: 12px; + font-size: 0.95rem; + color: var(--text-main); + + &:focus { + border-color: var(--brand-primary); + outline: none; + background: white; + } + } + + textarea { + min-height: 80px; + resize: vertical; + } + + .input-with-icon { + position: relative; + + lucide-icon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + color: var(--text-muted); + } + + input { + padding-left: 40px; + width: 100%; + border-radius: 12px; + } + } + } + + .form-divider { + border: none; + height: 1px; + background: var(--border-color); + margin: 20px 0; + } + + .form-section { + .section-header { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 16px; + color: var(--brand-secondary); + + h3 { + font-size: 0.95rem; + text-transform: uppercase; + letter-spacing: 0.05em; + margin: 0; + } + } + } + + // Checklist UI + .checklist-container { + display: flex; + flex-direction: column; + gap: 10px; + + .checklist-item { + display: flex; + align-items: center; + gap: 12px; + padding: 10px 14px; + background: var(--bg-main); + border-radius: 10px; + transition: all 0.2s; + + input[type="checkbox"] { + width: 18px; + height: 18px; + cursor: pointer; + } + + span { + flex: 1; + font-size: 0.9rem; + + &.completed { + text-decoration: line-through; + color: var(--text-muted); + } + } + + .remove-btn { + background: none; + border: none; + color: var(--text-muted); + cursor: pointer; + + &:hover { + color: #ef4444; + } + } + } + + .checklist-add { + display: flex; + gap: 10px; + margin-top: 8px; + + input { + flex: 1; + background: white; + border: 1.5px dashed var(--border-color); + } + + button { + background: var(--brand-primary); + color: white; + width: 40px; + height: 40px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + } + } + } + + // Evidences UI in Modal + .evidence-strip { + display: flex; + gap: 12px; + overflow-x: auto; + padding-bottom: 12px; + margin-bottom: 16px; + + .evidence-thumb { + width: 80px; + height: 80px; + border-radius: 10px; + overflow: hidden; + border: 1px solid var(--border-color); + flex-shrink: 0; + + img { + width: 100%; + height: 100%; + object-fit: cover; + } + } + } + + .upload-controls { + display: flex; + gap: 12px; + margin-bottom: 16px; + + button { + flex: 1; + padding: 10px; + border: 1px solid var(--border-color); + background: white; + border-radius: 10px; + font-size: 0.85rem; + font-weight: 700; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + cursor: pointer; + transition: all 0.2s; + + &:hover { + border-color: var(--brand-primary); + color: var(--brand-primary); + background: var(--bg-main); + } + } + } + + .camera-mini-overlay { + background: black; + border-radius: 16px; + overflow: hidden; + position: relative; + margin-bottom: 16px; + + video { + width: 100%; + display: block; + } + + .camera-mini-actions { + position: absolute; + bottom: 16px; + left: 0; + right: 0; + display: flex; + justify-content: center; + gap: 12px; + + button { + padding: 8px 16px; + border-radius: 50px; + font-weight: 700; + font-size: 0.75rem; + text-transform: uppercase; + + &.snap-btn { + background: white; + color: black; + } + + &.cancel-btn { + background: rgba(0, 0, 0, 0.5); + color: white; + border: 1px solid white; + } + } + } + } + + .temp-previews { + display: flex; + gap: 10px; + flex-wrap: wrap; + + .temp-item { + position: relative; + width: 60px; + height: 60px; + border-radius: 8px; + overflow: hidden; + border: 2px solid var(--brand-primary); + + img { + width: 100%; + height: 100%; + object-fit: cover; + } + + .remove-temp { + position: absolute; + top: 2px; + right: 2px; + background: #ef4444; + color: white; + width: 16px; + height: 16px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + } + } + } +} + +.modal-footer { + padding: 24px; + border-top: 1px solid var(--border-color); + display: flex; + justify-content: flex-end; + gap: 16px; + + button { + padding: 12px 24px; + border-radius: 12px; + font-weight: 700; + cursor: pointer; + transition: all 0.2s; + } + + .btn-cancel { + background: transparent; + color: var(--text-muted); + } + + .btn-save { + border: none; + display: flex; + align-items: center; + gap: 8px; + } +} + +.custom-scrollbar { + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-track { + background: transparent; + } + + &::-webkit-scrollbar-thumb { + background: var(--border-color); + border-radius: 10px; + } + + &:hover::-webkit-scrollbar-thumb { + background: var(--text-muted); + } +} \ No newline at end of file diff --git a/frontend/src/app/components/non-conformity-list/non-conformity-list.ts b/frontend/src/app/components/non-conformity-list/non-conformity-list.ts new file mode 100644 index 0000000..5a76f67 --- /dev/null +++ b/frontend/src/app/components/non-conformity-list/non-conformity-list.ts @@ -0,0 +1,119 @@ +import { Component, inject, signal, OnInit, computed, HostListener } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { NonConformityService } from '../../services/non-conformity'; +import { + LucideAngularModule, TriangleAlert, CircleCheck, Clock, Play, X, + ExternalLink, SquarePen, Trash2, Calendar, User, SquareCheck, Type, Activity, FileText, Camera, Plus, Save +} from 'lucide-angular'; +import { + DragDropModule, + CdkDragDrop +} from '@angular/cdk/drag-drop'; + +@Component({ + selector: 'app-non-conformity-list', + standalone: true, + imports: [CommonModule, RouterModule, FormsModule, ReactiveFormsModule, LucideAngularModule, DragDropModule], + templateUrl: './non-conformity-list.html', + styleUrl: './non-conformity-list.scss' +}) +export class NonConformityListComponent implements OnInit { + readonly AlertTriangle = TriangleAlert; + readonly CheckCircle = CircleCheck; + readonly Clock = Clock; + readonly Play = Play; + readonly X = X; + readonly ExternalLink = ExternalLink; + readonly Edit = SquarePen; + readonly Trash2 = Trash2; + readonly Calendar = Calendar; + readonly User = User; + readonly CheckSquare = SquareCheck; + readonly Type = Type; + readonly Activity = Activity; + readonly FileText = FileText; + readonly Camera = Camera; + readonly Plus = Plus; + readonly Save = Save; + + private ncService = inject(NonConformityService); + + ncs = signal([]); + loading = signal(false); + isMobile = signal(false); + + // Kanban Columns + open = computed(() => this.ncs().filter(nc => nc.status === 'open')); + inProgress = computed(() => this.ncs().filter(nc => nc.status === 'in-progress' || nc.status === 're-inspect')); + closed = computed(() => this.ncs().filter(nc => nc.status === 'closed' || nc.status === 'resolved')); + inChecking = computed(() => this.ncs().filter(nc => nc.status === 'in-checking')); + + @HostListener('window:resize') + onResize() { + this.checkMobile(); + } + + ngOnInit() { + this.checkMobile(); + this.loadNCs(); + } + + private checkMobile() { + this.isMobile.set(window.innerWidth < 768); + } + + loadNCs() { + this.loading.set(true); + this.ncService.getNCs().subscribe({ + next: (data) => { + this.ncs.set(data); + this.loading.set(false); + }, + error: () => this.loading.set(false) + }); + } + + drop(event: CdkDragDrop, newStatus: string) { + if (event.previousContainer !== event.container) { + const item = event.item.data; + const oldStatus = item.status; + + const updatedNCs = this.ncs().map(nc => + nc.id === item.id ? { ...nc, status: newStatus } : nc + ); + this.ncs.set(updatedNCs); + + this.ncService.updateNC(item.id, { status: newStatus }).subscribe({ + error: () => { + const revertedNCs = this.ncs().map(nc => + nc.id === item.id ? { ...nc, status: oldStatus } : nc + ); + this.ncs.set(revertedNCs); + alert('Error al actualizar el estado de la No Conformidad.'); + } + }); + } + } + + getLevelLabel(level: string): string { + const labels: any = { + critical: 'Crítica', + major: 'Grave', + minor: 'Leve' + }; + return labels[level] || level; + } + + isOverdue(nc: any): boolean { + if (!nc.due_date || nc.status === 'closed') return false; + return new Date(nc.due_date) < new Date(); + } + + getChecklistProgress(nc: any): number { + if (!nc.action_checklist || nc.action_checklist.length === 0) return 0; + const completed = nc.action_checklist.filter((a: any) => a.completed).length; + return Math.round((completed / nc.action_checklist.length) * 100); + } +} diff --git a/frontend/src/app/components/project-form/project-form.html b/frontend/src/app/components/project-form/project-form.html new file mode 100644 index 0000000..b94e544 --- /dev/null +++ b/frontend/src/app/components/project-form/project-form.html @@ -0,0 +1,92 @@ +
+ + +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+
+ +
+ + +
+
+
\ No newline at end of file diff --git a/frontend/src/app/components/project-form/project-form.scss b/frontend/src/app/components/project-form/project-form.scss new file mode 100644 index 0000000..702ab75 --- /dev/null +++ b/frontend/src/app/components/project-form/project-form.scss @@ -0,0 +1,247 @@ +.form-container { + max-width: 900px; + margin: 0 auto; + + .page-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + margin-bottom: 32px; + position: sticky; + top: -24px; + background: var(--bg-main); + z-index: 100; + padding: 16px 0; + border-bottom: 1px solid transparent; + + &.sticky-header { + background: var(--bg-main); + margin-left: -24px; + margin-right: -24px; + padding-left: 24px; + padding-right: 24px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.03); + border-bottom-color: var(--border-color); + } + + .header-left { + display: flex; + align-items: center; + gap: 16px; + } + + .header-actions { + display: flex; + align-items: center; + gap: 12px; + + .cancel-btn { + background: transparent; + color: var(--text-muted); + font-weight: 600; + padding: 10px 20px; + border: none; + cursor: pointer; + + &:hover { + color: var(--brand-secondary); + } + } + + .gold-button { + padding: 10px 24px; + min-width: 120px; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + } + } + + .back-btn { + background: var(--bg-surface); + border: 1px solid var(--border-color); + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + color: var(--text-muted); + + &:hover { + border-color: var(--brand-primary); + color: var(--brand-primary); + } + } + + h1 { + font-size: 1.5rem; + } + + .subproject-badge { + display: inline-flex; + align-items: center; + padding: 4px 12px; + background: rgba(180, 83, 9, 0.08); + color: var(--brand-primary); + border-radius: 6px; + font-size: 0.85rem; + font-weight: 600; + border: 1px solid rgba(180, 83, 9, 0.2); + + strong { + margin-left: 4px; + color: var(--brand-secondary); + } + } + } +} + +.form-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 24px; + margin-bottom: 32px; + + @media (max-width: 640px) { + grid-template-columns: 1fr; + } + + .full-width { + grid-column: span 2; + + @media (max-width: 640px) { + grid-column: span 1; + } + } +} + +.form-group { + display: flex; + flex-direction: column; + gap: 8px; + + label { + font-size: 0.85rem; + color: var(--text-muted); + font-weight: 700; + text-transform: uppercase; + } + + input, + select { + background: var(--bg-main); + border: 1px solid var(--border-color); + color: var(--text-main); + padding: 12px 14px; + border-radius: 10px; + font-size: 0.95rem; + transition: all 0.2s; + + &:focus { + outline: none; + border-color: var(--brand-primary); + background: var(--bg-surface); + } + } +} + +.selection-section { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 40px; + padding-top: 32px; + border-top: 1px solid var(--border-color); + margin-bottom: 40px; + + @media (max-width: 768px) { + grid-template-columns: 1fr; + gap: 32px; + } + + .section-column { + display: flex; + flex-direction: column; + gap: 16px; + + label { + font-size: 0.85rem; + color: var(--brand-secondary); + font-weight: 700; + text-transform: uppercase; + } + } +} + +.pill-grid { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.pill { + background: var(--bg-main); + border: 1px solid var(--border-color); + padding: 8px 16px; + border-radius: 20px; + font-size: 0.85rem; + font-weight: 600; + color: var(--text-muted); + transition: all 0.2s; + + &:hover { + border-color: var(--border-hover); + color: var(--text-main); + } + + &.selected { + background: rgba(180, 83, 9, 0.1); + border-color: var(--brand-primary); + color: var(--brand-primary); + } +} + +.form-actions { + display: flex; + justify-content: flex-end; + align-items: center; + gap: 16px; + padding-top: 24px; + border-top: 1px solid var(--border-color); + + .cancel-btn { + background: transparent; + color: var(--text-muted); + font-weight: 600; + padding: 10px 20px; + + &:hover { + color: var(--brand-secondary); + } + } + + .animate-spin { + animation: spin 1s linear infinite; + } + + @keyframes spin { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } + } + + @media (max-width: 480px) { + flex-direction: column-reverse; + + .gold-button, + .cancel-btn { + width: 100%; + } + } +} \ No newline at end of file diff --git a/frontend/src/app/components/project-form/project-form.ts b/frontend/src/app/components/project-form/project-form.ts new file mode 100644 index 0000000..23745eb --- /dev/null +++ b/frontend/src/app/components/project-form/project-form.ts @@ -0,0 +1,136 @@ +import { Component, inject, signal, OnInit } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms'; +import { ActivatedRoute, Router, RouterModule } from '@angular/router'; +import { ProjectService } from '../../services/project'; +import { SpecialtyService } from '../../services/specialty'; +import { ContractorService } from '../../services/contractor'; +import { LucideAngularModule, ArrowLeft, Save, Loader2 } from 'lucide-angular'; + +@Component({ + selector: 'app-project-form', + standalone: true, + imports: [CommonModule, ReactiveFormsModule, RouterModule, LucideAngularModule], + templateUrl: './project-form.html', + styleUrl: './project-form.scss' +}) +export class ProjectFormComponent implements OnInit { + readonly ArrowLeft = ArrowLeft; + readonly Save = Save; + readonly Loader2 = Loader2; + + private fb = inject(FormBuilder); + private projectService = inject(ProjectService); + private specialtyService = inject(SpecialtyService); + private contractorService = inject(ContractorService); + private router = inject(Router); + private route = inject(ActivatedRoute); + + projectForm: FormGroup; + isEditMode = signal(false); + loading = signal(false); + projectId = signal(null); + + specialties = signal([]); + contractors = signal([]); + + constructor() { + this.projectForm = this.fb.group({ + name: ['', Validators.required], + code: ['', Validators.required], + location: [''], + start_date: [''], + end_date: [''], + status: ['active'], + parent_id: [null], + specialty_ids: [[]], + contractor_ids: [[]] + }); + } + + ngOnInit() { + this.loadDependencies(); + const id = this.route.snapshot.params['id']; + if (id) { + this.isEditMode.set(true); + this.projectId.set(+id); + this.loadProject(+id); + } + + const parentId = this.route.snapshot.queryParams['parentId']; + if (parentId) { + this.projectForm.patchValue({ parent_id: +parentId }); + this.loadParentProject(+parentId); + } + } + + parentProjectName = signal(null); + + loadParentProject(id: number) { + this.projectService.getProject(id).subscribe(p => { + this.parentProjectName.set(p.name); + }); + } + + loadDependencies() { + this.specialtyService.getSpecialties().subscribe(data => this.specialties.set(data)); + this.contractorService.getContractors().subscribe(data => this.contractors.set(data)); + } + + loadProject(id: number) { + this.loading.set(true); + this.projectService.getProject(id).subscribe({ + next: (project) => { + this.projectForm.patchValue({ + ...project, + start_date: project.start_date ? project.start_date.split('T')[0] : '', + end_date: project.end_date ? project.end_date.split('T')[0] : '', + specialty_ids: project.specialties?.map((s: any) => s.id) || [], + contractor_ids: project.contractors?.map((c: any) => c.id) || [] + }); + this.loading.set(false); + }, + error: () => this.loading.set(false) + }); + } + + onSubmit() { + if (this.projectForm.invalid) return; + + this.loading.set(true); + const data = this.projectForm.value; + + // Ensure dates are sent correctly or null if empty + if (!data.start_date) delete data.start_date; + if (!data.end_date) delete data.end_date; + + const action = this.isEditMode() + ? this.projectService.updateProject(this.projectId()!, data) + : this.projectService.createProject(data); + + action.subscribe({ + next: () => { + this.router.navigate(['/projects']); + }, + error: (err) => { + console.error('Error saving project:', err); + this.loading.set(false); + alert('Error al guardar el proyecto. Verifique el código único.'); + } + }); + } + + toggleSelection(controlName: string, id: number) { + const control = this.projectForm.get(controlName); + const currentValues = control?.value as number[]; + if (currentValues.includes(id)) { + control?.setValue(currentValues.filter(v => v !== id)); + } else { + control?.setValue([...currentValues, id]); + } + } + + isSelected(controlName: string, id: number): boolean { + return (this.projectForm.get(controlName)?.value as number[]).includes(id); + } +} diff --git a/frontend/src/app/components/project-list/project-list.html b/frontend/src/app/components/project-list/project-list.html new file mode 100644 index 0000000..b8b3e73 --- /dev/null +++ b/frontend/src/app/components/project-list/project-list.html @@ -0,0 +1,91 @@ +
+ + +
+ +
+ + @if(loading()) { + +
Cargando proyectos...
+ + } @else { + +
+ + @for(p of projects(); track p.id) { + +
+
+
+ {{ p.code }} +

{{ p.name }}

+
+
+ {{ p.status | titlecase }} +
+
+ +
+
+ + {{ p.location || 'Sin ubicación' }} +
+
+ + {{ p.start_date | date:'shortDate' }} - {{ p.end_date | date:'shortDate' }} +
+
+ +
+
+ + {{ p.specialties?.length || 0 }} Especialidades +
+
+ + {{ p.contractors?.length || 0 }} Contratistas +
+
+ + +
+ + } + +
+ } +
+ +

No hay proyectos registrados aún.

+ +
+ +
\ No newline at end of file diff --git a/frontend/src/app/components/project-list/project-list.scss b/frontend/src/app/components/project-list/project-list.scss new file mode 100644 index 0000000..13ed9a9 --- /dev/null +++ b/frontend/src/app/components/project-list/project-list.scss @@ -0,0 +1,228 @@ +.project-container { + max-width: 1200px; + margin: 0 auto; + + .page-header { + display: flex; + justify-content: space-between; + align-items: center; + gap: 20px; + margin-bottom: 32px; + + @media (max-width: 640px) { + flex-direction: column; + align-items: flex-start; + } + + .title-area { + h1 { + font-size: 1.8rem; + margin-bottom: 4px; + } + + p { + color: var(--text-muted); + font-size: 0.95rem; + } + } + + .gold-button { + @media (max-width: 640px) { + width: 100%; + } + } + } +} + +.search-card { + margin-bottom: 32px; + padding: 12px 20px; +} + +.search-box { + display: flex; + align-items: center; + gap: 12px; + color: var(--text-muted); + + input { + border: none; + background: transparent; + flex: 1; + font-size: 0.95rem; + color: var(--text-main); + + &:focus { + outline: none; + } + } +} + +.project-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(360px, 1fr)); + gap: 24px; + + @media (max-width: 480px) { + grid-template-columns: 1fr; + } +} + +.project-card { + display: flex; + flex-direction: column; + padding: 24px; + transition: transform 0.2s, box-shadow 0.2s; + + &:hover { + transform: translateY(-4px); + box-shadow: 0 12px 30px rgba(0, 0, 0, 0.06); + } + + .card-top { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 20px; + + .project-info { + h3 { + font-size: 1.2rem; + color: var(--brand-secondary); + } + } + + .status-badge { + font-size: 0.7rem; + font-weight: 700; + padding: 4px 10px; + border-radius: 20px; + text-transform: uppercase; + + &.active { + background: #ecfdf5; + color: #047857; + } + + &.completed { + background: #eff6ff; + color: #1d4ed8; + } + + &.on_hold { + background: #fff7ed; + color: #d97706; + } + } + } + + .card-details { + display: flex; + flex-direction: column; + gap: 12px; + margin-bottom: 20px; + + .detail-item { + display: flex; + align-items: center; + gap: 10px; + color: var(--text-muted); + font-size: 0.9rem; + font-weight: 500; + } + } + + .card-stats { + display: flex; + gap: 16px; + margin-bottom: 24px; + padding: 12px; + background: var(--bg-main); + border-radius: 12px; + + + } + + .card-footer { + margin-top: auto; + display: flex; + justify-content: flex-end; + gap: 12px; + padding-top: 16px; + border-top: 1px solid var(--border-color); + + .action-btn { + width: 38px; + height: 38px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; + background: var(--bg-surface); + border: 1px solid var(--border-color); + + &.subproject { + color: var(--brand-secondary); + opacity: 0.8; + + &:hover { + border-color: var(--brand-secondary); + background: rgba(15, 23, 42, 0.04); + opacity: 1; + } + } + + &.edit { + color: var(--brand-primary); + + &:hover { + border-color: var(--brand-primary); + background: rgba(180, 83, 9, 0.04); + } + } + + &.delete { + color: #ef4444; + + &:hover { + border-color: #ef4444; + background: #fef2f2; + } + } + } + + .footer-spacer { + flex: 1; + } + } +} + +.loading-placeholder { + text-align: center; + padding: 60px; + color: var(--text-muted); + font-weight: 500; +} + +.empty-state { + text-align: center; + padding: 80px 24px; + + .empty-icon { + margin-bottom: 20px; + opacity: 0.2; + } + + p { + font-size: 1.1rem; + color: var(--text-muted); + margin-bottom: 24px; + } + + .text-btn { + background: none; + color: var(--brand-primary); + font-weight: 700; + } +} \ No newline at end of file diff --git a/frontend/src/app/components/project-list/project-list.ts b/frontend/src/app/components/project-list/project-list.ts new file mode 100644 index 0000000..71fb78b --- /dev/null +++ b/frontend/src/app/components/project-list/project-list.ts @@ -0,0 +1,58 @@ +import { Component, inject, signal } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ActivatedRoute, Router, RouterModule } from '@angular/router'; +import { ProjectService } from '../../services/project'; +import { LucideAngularModule, Plus, Search, MapPin, Calendar, Users, Edit2, Trash2, HardHat, GitBranch } from 'lucide-angular'; + +@Component({ + selector: 'app-project-list', + standalone: true, + imports: [CommonModule, RouterModule, LucideAngularModule], + templateUrl: './project-list.html', + styleUrl: './project-list.scss' +}) +export class ProjectListComponent { + readonly Plus = Plus; + readonly Search = Search; + readonly MapPin = MapPin; + readonly Calendar = Calendar; + readonly Users = Users; + readonly Edit2 = Edit2; + readonly Trash2 = Trash2; + readonly HardHat = HardHat; + readonly GitBranch = GitBranch; + + private projectService = inject(ProjectService); + private router = inject(Router); + projects = signal([]); + loading = signal(true); + + constructor() { + this.loadProjects(); + } + + loadProjects() { + this.loading.set(true); + this.projectService.getProjects().subscribe({ + next: (data) => { + this.projects.set(data); + this.loading.set(false); + }, + error: () => this.loading.set(false) + }); + } + + deleteProject(id: number) { + if (confirm('¿Está seguro de eliminar este proyecto?')) { + this.projectService.deleteProject(id).subscribe(() => { + this.loadProjects(); + }); + } + } + + createSubproject(parent: any) { + this.router.navigate(['/projects/new'], { + queryParams: { parentId: parent.id } + }); + } +} diff --git a/frontend/src/app/components/sidebar/sidebar.html b/frontend/src/app/components/sidebar/sidebar.html new file mode 100644 index 0000000..92970f8 --- /dev/null +++ b/frontend/src/app/components/sidebar/sidebar.html @@ -0,0 +1,47 @@ + \ No newline at end of file diff --git a/frontend/src/app/components/sidebar/sidebar.scss b/frontend/src/app/components/sidebar/sidebar.scss new file mode 100644 index 0000000..81d367f --- /dev/null +++ b/frontend/src/app/components/sidebar/sidebar.scss @@ -0,0 +1,138 @@ +.sidebar { + height: 100%; + background: var(--bg-surface); + border-right: 1px solid var(--border-color); + display: flex; + flex-direction: column; + padding: 0; + width: var(--nav-width); + transition: width 0.3s ease; + overflow: hidden; + + &.collapsed { + width: var(--nav-width-collapsed); + + .sidebar-header .logo span, + .menu-item span, + .logout-btn span { + display: none; + } + + .sidebar-header { + padding: 0; + justify-content: center; + } + + .menu-section { + padding: 24px 8px; + align-items: center; + } + + .menu-item { + padding: 12px; + justify-content: center; + } + + .sidebar-footer { + padding: 16px 8px; + display: flex; + justify-content: center; + } + + .logout-btn { + padding: 12px; + justify-content: center; + } + } +} + +.sidebar-header { + height: var(--header-height); + display: flex; + align-items: center; + padding: 0 24px; + border-bottom: 1px solid var(--border-color); + transition: all 0.3s ease; + + .logo { + display: flex; + align-items: center; + gap: 12px; + color: var(--brand-secondary); + font-weight: 800; + font-size: 1rem; + letter-spacing: -0.02em; + white-space: nowrap; + + .logo-icon { + color: var(--brand-primary); + flex-shrink: 0; + } + } +} + +.menu-section { + flex: 1; + padding: 24px 12px; + display: flex; + flex-direction: column; + gap: 4px; + transition: all 0.3s ease; +} + +.menu-item { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 16px; + border-radius: 10px; + color: var(--text-muted); + font-weight: 500; + font-size: 0.95rem; + white-space: nowrap; + transition: all 0.3s ease; + + &:hover { + background: var(--bg-main); + color: var(--brand-secondary); + } + + &.active { + background: rgba(180, 83, 9, 0.08); + color: var(--brand-primary); + font-weight: 600; + + lucide-icon { + color: var(--brand-primary); + } + } + + lucide-icon { + flex-shrink: 0; + transition: color 0.2s; + } +} + +.sidebar-footer { + padding: 16px; + border-top: 1px solid var(--border-color); + transition: all 0.3s ease; +} + +.logout-btn { + width: 100%; + display: flex; + align-items: center; + gap: 12px; + padding: 12px 16px; + border-radius: 10px; + background: transparent; + color: #ef4444; // Red 500 + font-size: 0.95rem; + white-space: nowrap; + transition: all 0.3s ease; + + &:hover { + background: #fef2f2; // Lighter red bg + } +} \ No newline at end of file diff --git a/frontend/src/app/components/sidebar/sidebar.spec.ts b/frontend/src/app/components/sidebar/sidebar.spec.ts new file mode 100644 index 0000000..46ba96c --- /dev/null +++ b/frontend/src/app/components/sidebar/sidebar.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Sidebar } from './sidebar'; + +describe('Sidebar', () => { + let component: Sidebar; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Sidebar] + }) + .compileComponents(); + + fixture = TestBed.createComponent(Sidebar); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/sidebar/sidebar.ts b/frontend/src/app/components/sidebar/sidebar.ts new file mode 100644 index 0000000..1bb55fa --- /dev/null +++ b/frontend/src/app/components/sidebar/sidebar.ts @@ -0,0 +1,35 @@ +import { Component, inject, Input } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { AuthService } from '../../services/auth'; +import { LucideAngularModule, Shield, LayoutDashboard, HardHat, ClipboardList, FileText, LogOut, AlertTriangle, Users } from 'lucide-angular'; + +@Component({ + selector: 'app-sidebar', + standalone: true, + imports: [ + CommonModule, + RouterModule, + LucideAngularModule + ], + templateUrl: './sidebar.html', + styleUrl: './sidebar.scss' +}) +export class SidebarComponent { + @Input() isCollapsed = false; + + readonly Shield = Shield; + readonly LayoutDashboard = LayoutDashboard; + readonly HardHat = HardHat; + readonly ClipboardList = ClipboardList; + readonly FileText = FileText; + readonly LogOut = LogOut; + readonly AlertTriangle = AlertTriangle; + readonly Users = Users; + + private authService = inject(AuthService); + + onLogout() { + this.authService.logout(); + } +} diff --git a/frontend/src/app/guards/auth.ts b/frontend/src/app/guards/auth.ts new file mode 100644 index 0000000..9572343 --- /dev/null +++ b/frontend/src/app/guards/auth.ts @@ -0,0 +1,15 @@ +import { inject } from '@angular/core'; +import { Router, CanActivateFn } from '@angular/router'; +import { AuthService } from '../services/auth'; + +export const authGuard: CanActivateFn = (route, state) => { + const authService = inject(AuthService); + const router = inject(Router); + + if (authService.isAuthenticated()) { + return true; + } else { + router.navigate(['/login']); + return false; + } +}; diff --git a/frontend/src/app/interceptors/auth-interceptor.spec.ts b/frontend/src/app/interceptors/auth-interceptor.spec.ts new file mode 100644 index 0000000..de59f96 --- /dev/null +++ b/frontend/src/app/interceptors/auth-interceptor.spec.ts @@ -0,0 +1,17 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpInterceptorFn } from '@angular/common/http'; + +import { authInterceptor } from './auth-interceptor'; + +describe('authInterceptor', () => { + const interceptor: HttpInterceptorFn = (req, next) => + TestBed.runInInjectionContext(() => authInterceptor(req, next)); + + beforeEach(() => { + TestBed.configureTestingModule({}); + }); + + it('should be created', () => { + expect(interceptor).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/interceptors/auth-interceptor.ts b/frontend/src/app/interceptors/auth-interceptor.ts new file mode 100644 index 0000000..6a4db4f --- /dev/null +++ b/frontend/src/app/interceptors/auth-interceptor.ts @@ -0,0 +1,16 @@ +import { HttpInterceptorFn } from '@angular/common/http'; + +export const authInterceptor: HttpInterceptorFn = (req, next) => { + const token = localStorage.getItem('access_token'); + + if (token) { + const cloned = req.clone({ + setHeaders: { + Authorization: `Bearer ${token}` + } + }); + return next(cloned); + } + + return next(req); +}; diff --git a/frontend/src/app/services/activity.ts b/frontend/src/app/services/activity.ts new file mode 100644 index 0000000..46614ee --- /dev/null +++ b/frontend/src/app/services/activity.ts @@ -0,0 +1,52 @@ +import { Injectable, inject } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { environment } from '../../environments/environment'; + +@Injectable({ + providedIn: 'root' +}) +export class ActivityService { + private http = inject(HttpClient); + private apiUrl = environment.apiUrl + '/activities'; + + getActivities(projectId?: number, specialtyId?: number) { + const params: any = {}; + if (projectId) params.project_id = projectId; + if (specialtyId) params.specialty_id = specialtyId; + + return this.http.get(this.apiUrl, { params }); + } + + getActivity(id: number) { + return this.http.get(`${this.apiUrl}/${id}`); + } + + createActivity(activity: any) { + return this.http.post(this.apiUrl, activity); + } + + uploadEvidence(activityId: number, file: File, description?: string, capturedAt?: string) { + const formData = new FormData(); + formData.append('file', file); + if (description) formData.append('description', description); + if (capturedAt) formData.append('captured_at', capturedAt); + + return this.http.post(`${this.apiUrl}/${activityId}/upload`, formData); + } + + updateActivity(id: number, activity: any) { + return this.http.put(`${this.apiUrl}/${id}`, activity); + } + + retryTranscription(evidenceId: number) { + return this.http.post(`${this.apiUrl}/evidence/${evidenceId}/retry-transcription`, {}); + } + + updateEvidence(evidenceId: number, data: any) { + return this.http.put(`${this.apiUrl}/evidence/${evidenceId}`, data); + } + + deleteEvidence(evidenceId: number) { + return this.http.delete(`${this.apiUrl}/evidence/${evidenceId}`); + } +} diff --git a/frontend/src/app/services/auth.ts b/frontend/src/app/services/auth.ts new file mode 100644 index 0000000..b80f6f0 --- /dev/null +++ b/frontend/src/app/services/auth.ts @@ -0,0 +1,54 @@ +import { Injectable, signal, inject } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Router } from '@angular/router'; +import { tap, catchError, of } from 'rxjs'; +import { environment } from '../../environments/environment'; + +@Injectable({ + providedIn: 'root' +}) +export class AuthService { + private http = inject(HttpClient); + private router = inject(Router); + private apiUrl = environment.apiUrl; + + token = signal(localStorage.getItem('access_token')); + currentUser = signal(null); + + login(credentials: { username: string; password: string }) { + const body = new URLSearchParams(); + body.set('username', credentials.username); + body.set('password', credentials.password); + + return this.http.post(`${this.apiUrl}/token`, body.toString(), { + headers: { 'Content-Type': 'application/x-www-form-urlencoded' } + }).pipe( + tap(res => { + localStorage.setItem('access_token', res.access_token); + this.token.set(res.access_token); + this.fetchCurrentUser().subscribe(); + }) + ); + } + + fetchCurrentUser() { + return this.http.get(`${this.apiUrl}/users/me`).pipe( + tap(user => this.currentUser.set(user)), + catchError(() => { + this.logout(); + return of(null); + }) + ); + } + + logout() { + localStorage.removeItem('access_token'); + this.token.set(null); + this.currentUser.set(null); + this.router.navigate(['/login']); + } + + isAuthenticated(): boolean { + return !!this.token(); + } +} diff --git a/frontend/src/app/services/contractor.ts b/frontend/src/app/services/contractor.ts new file mode 100644 index 0000000..cdf11c4 --- /dev/null +++ b/frontend/src/app/services/contractor.ts @@ -0,0 +1,36 @@ +import { Injectable, inject } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { environment } from '../../environments/environment'; + +@Injectable({ + providedIn: 'root' +}) +export class ContractorService { + private http = inject(HttpClient); + private apiUrl = environment.apiUrl + '/contractors'; + + getContractors(parentId?: number | null, isActive?: boolean, onlyParents: boolean = false) { + const params: any = {}; + if (parentId !== undefined && parentId !== null) params.parent_id = parentId; + if (isActive !== undefined && isActive !== null) params.is_active = isActive; + if (onlyParents) params.only_parents = true; + + return this.http.get(this.apiUrl, { params }); + } + + getContractor(id: number) { + return this.http.get(`${this.apiUrl}/${id}`); + } + + createContractor(contractor: any) { + return this.http.post(this.apiUrl, contractor); + } + + updateContractor(id: number, contractor: any) { + return this.http.patch(`${this.apiUrl}/${id}`, contractor); + } + + deleteContractor(id: number) { + return this.http.delete(`${this.apiUrl}/${id}`); + } +} diff --git a/frontend/src/app/services/non-conformity.ts b/frontend/src/app/services/non-conformity.ts new file mode 100644 index 0000000..bedcd80 --- /dev/null +++ b/frontend/src/app/services/non-conformity.ts @@ -0,0 +1,44 @@ +import { Injectable, inject } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { environment } from '../../environments/environment'; + +@Injectable({ + providedIn: 'root' +}) +export class NonConformityService { + private http = inject(HttpClient); + private apiUrl = environment.apiUrl + '/non-conformities'; + + getNCs(activityId?: number, status?: string) { + const params: any = {}; + if (activityId) params.activity_id = activityId; + if (status) params.status = status; + + return this.http.get(this.apiUrl, { params }); + } + + getNC(id: number) { + return this.http.get(`${this.apiUrl}/${id}`); + } + + createNC(nc: any) { + return this.http.post(this.apiUrl, nc); + } + + updateNC(id: number, nc: any) { + return this.http.patch(`${this.apiUrl}/${id}`, nc); + } + + deleteNC(id: number) { + return this.http.delete(`${this.apiUrl}/${id}`); + } + + uploadEvidence(ncId: number, file: File, description?: string, capturedAt?: string) { + const formData = new FormData(); + formData.append('file', file); + if (description) formData.append('description', description); + if (capturedAt) formData.append('captured_at', capturedAt); + + return this.http.post(`${this.apiUrl}/${ncId}/upload`, formData); + } +} diff --git a/frontend/src/app/services/project.ts b/frontend/src/app/services/project.ts new file mode 100644 index 0000000..7ee188d --- /dev/null +++ b/frontend/src/app/services/project.ts @@ -0,0 +1,31 @@ +import { Injectable, inject } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { environment } from '../../environments/environment'; + +@Injectable({ + providedIn: 'root' +}) +export class ProjectService { + private http = inject(HttpClient); + private apiUrl = environment.apiUrl + '/projects'; + + getProjects() { + return this.http.get(this.apiUrl); + } + + getProject(id: number) { + return this.http.get(`${this.apiUrl}/${id}`); + } + + createProject(project: any) { + return this.http.post(this.apiUrl, project); + } + + updateProject(id: number, project: any) { + return this.http.put(`${this.apiUrl}/${id}`, project); + } + + deleteProject(id: number) { + return this.http.delete(`${this.apiUrl}/${id}`); + } +} diff --git a/frontend/src/app/services/specialty.ts b/frontend/src/app/services/specialty.ts new file mode 100644 index 0000000..39684fd --- /dev/null +++ b/frontend/src/app/services/specialty.ts @@ -0,0 +1,15 @@ +import { Injectable, inject } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { environment } from '../../environments/environment'; + +@Injectable({ + providedIn: 'root' +}) +export class SpecialtyService { + private http = inject(HttpClient); + private apiUrl = environment.apiUrl + '/specialties'; + + getSpecialties() { + return this.http.get(this.apiUrl); + } +} diff --git a/frontend/src/app/services/transcription.ts b/frontend/src/app/services/transcription.ts new file mode 100644 index 0000000..09e8dd0 --- /dev/null +++ b/frontend/src/app/services/transcription.ts @@ -0,0 +1,17 @@ +import { Injectable, inject } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { environment } from '../../environments/environment'; + +@Injectable({ + providedIn: 'root' +}) +export class TranscriptionService { + private http = inject(HttpClient); + private apiUrl = environment.apiUrl + '/transcription'; + + transcribe(audioBlob: Blob) { + const formData = new FormData(); + formData.append('file', audioBlob, 'recording.wav'); + return this.http.post<{ text: string }>(this.apiUrl, formData); + } +} diff --git a/frontend/src/environments/environment.development.ts b/frontend/src/environments/environment.development.ts new file mode 100644 index 0000000..e1ee2b8 --- /dev/null +++ b/frontend/src/environments/environment.development.ts @@ -0,0 +1,3 @@ +export const environment = { + apiUrl: 'http://192.168.1.76:8000' +}; diff --git a/frontend/src/environments/environment.ts b/frontend/src/environments/environment.ts new file mode 100644 index 0000000..e1ee2b8 --- /dev/null +++ b/frontend/src/environments/environment.ts @@ -0,0 +1,3 @@ +export const environment = { + apiUrl: 'http://192.168.1.76:8000' +}; diff --git a/frontend/src/index.html b/frontend/src/index.html new file mode 100644 index 0000000..1cfde2b --- /dev/null +++ b/frontend/src/index.html @@ -0,0 +1,16 @@ + + + + + + SupervisionApp + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/main.ts b/frontend/src/main.ts new file mode 100644 index 0000000..5df75f9 --- /dev/null +++ b/frontend/src/main.ts @@ -0,0 +1,6 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { App } from './app/app'; + +bootstrapApplication(App, appConfig) + .catch((err) => console.error(err)); diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss new file mode 100644 index 0000000..dd976ed --- /dev/null +++ b/frontend/src/styles.scss @@ -0,0 +1,200 @@ +:root { + // Brand Colors + --brand-primary: #b45309; // Gold/Amber 700 + --brand-secondary: #0f172a; // Midnight Slate 900 + --brand-accent: #3b82f6; // Blue 500 + + // Neutral Colors (Light Theme) + --bg-main: #f8fafc; + --bg-surface: #ffffff; + --bg-sidebar: #ffffff; + + --text-main: #1e293b; + --text-muted: #64748b; + --text-on-brand: #ffffff; + + --border-color: #e2e8f0; + --border-hover: #cbd5e1; + + --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1); + --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1); + + --radius-md: 12px; + --radius-lg: 16px; + + // Layout Constants + --nav-width: 260px; + --nav-width-collapsed: 80px; + --header-height: 70px; +} + +// Media Queries Breakpoints +$mobile: 768px; +$tablet: 1024px; + +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Inter', system-ui, -apple-system, sans-serif; +} + +body { + background-color: var(--bg-main); + color: var(--text-main); + -webkit-font-smoothing: antialiased; + min-height: 100vh; +} + +h1, +h2, +h3, +h4 { + color: var(--brand-secondary); + font-weight: 700; + letter-spacing: -0.025em; +} + +a { + text-decoration: none; + color: inherit; + transition: all 0.2s; +} + +button { + cursor: pointer; + border: none; + font-weight: 600; + transition: all 0.2s ease; + + &:disabled { + opacity: 0.6; + cursor: not-allowed; + } +} + +.gold-button { + background: var(--brand-primary); + color: white; + padding: 10px 24px; + border-radius: var(--radius-md); + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + box-shadow: 0 4px 12px rgba(180, 83, 9, 0.2); + + &:hover:not(:disabled) { + transform: translateY(-1px); + box-shadow: 0 6px 16px rgba(180, 83, 9, 0.3); + background: #92400e; + } +} + +.premium-card { + background: var(--bg-surface); + border: 1px solid var(--border-color); + border-radius: var(--radius-lg); + padding: 24px; + box-shadow: var(--shadow-sm); + transition: box-shadow 0.2s, border-color 0.2s; + + &:hover { + box-shadow: var(--shadow-md); + border-color: var(--border-hover); + } +} + +// Utility Classes +.w-full { + width: 100%; +} + +.mt-2 { + margin-top: 8px; +} + +.mb-3 { + margin-bottom: 12px; +} + +.mr-2 { + margin-right: 8px; +} + +.mr-1 { + margin-right: 4px; +} + +.opacity-50 { + opacity: 0.5; +} + +.text-uppercase { + text-transform: uppercase; +} + +// Responsive Grids +.grid-cols-1 { + display: grid; + grid-template-columns: 1fr; + gap: 24px; +} + +@media (min-width: 768px) { + .grid-cols-2 { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 24px; + } + + .grid-cols-3 { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 24px; + } + + +} + +@media (max-width: 640px) { + + h1, + h2, + h3, + h4 { + color: var(--brand-secondary); + font-weight: 600; + letter-spacing: -0.025em; + font-size: 1.2rem; + } +} + +.code { + font-size: 0.7rem; + font-weight: 800; + color: var(--brand-primary); + text-transform: uppercase; + letter-spacing: 0.05em; + background: rgba(180, 83, 9, 0.08); + padding: 2px 8px; + border-radius: 6px; + display: inline-block; + margin-bottom: 8px; + height: 17px; +} + +.stat { + display: flex; + align-items: center; + gap: 8px; + font-size: 0.8rem; + color: var(--text-muted); + font-weight: 600; + + lucide-icon { + color: var(--brand-secondary); + opacity: 0.7; + } +} \ No newline at end of file diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json new file mode 100644 index 0000000..264f459 --- /dev/null +++ b/frontend/tsconfig.app.json @@ -0,0 +1,15 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "src/**/*.spec.ts" + ] +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..2ab7442 --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,33 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "compileOnSave": false, + "compilerOptions": { + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "isolatedModules": true, + "experimentalDecorators": true, + "importHelpers": true, + "target": "ES2022", + "module": "preserve" + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + }, + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/frontend/tsconfig.spec.json b/frontend/tsconfig.spec.json new file mode 100644 index 0000000..d383706 --- /dev/null +++ b/frontend/tsconfig.spec.json @@ -0,0 +1,15 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "vitest/globals" + ] + }, + "include": [ + "src/**/*.d.ts", + "src/**/*.spec.ts" + ] +} diff --git a/logo.svg b/logo.svg new file mode 100644 index 0000000..6f2ca48 --- /dev/null +++ b/logo.svg @@ -0,0 +1,305 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +