diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cc2d0d7d4..d8b09189b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.4 + rev: v0.6.5 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] diff --git a/alembic/env.py b/alembic/env.py index 56bfefd32..dfe9fafe2 100644 --- a/alembic/env.py +++ b/alembic/env.py @@ -1,192 +1,23 @@ """Alembic migration environment.""" -import asyncio -import logging -from urllib.parse import quote, urlparse - -import structlog from alembic import context -from safir.database import create_database_engine -from safir.logging import LogLevel, add_log_severity -from sqlalchemy.engine import Connection +from safir.database import run_migrations_offline, run_migrations_online +from safir.logging import configure_alembic_logging from gafaelfawr.dependencies.config import config_dependency from gafaelfawr.schema import Base -# This is the Alembic Config object, which provides access to the values -# within the .ini file in use. -config = context.config - # Load the Gafaelfawr configuration, which as a side effect also configures # logging using structlog. -gafaelfawr_config = config_dependency.config() - -# Define the SQLAlchemy schema, which enables autogenerate support. -target_metadata = Base.metadata - - -def build_database_url( - url: str, password: str | None, *, is_async: bool -) -> str: - """Build the authenticated URL for the database. - - Parameters - ---------- - url - Database connection URL, not including the password. - password - Database connection password. - is_async - Whether the resulting URL should be async or not. - - Returns - ------- - url - The URL including the password. - - Raises - ------ - ValueError - A password was provided but the connection URL has no username. - - Notes - ----- - This is duplicated from safir.database and should be replaced with an - exported Safir function once Safir provides one. - """ - if is_async or password: - parsed_url = urlparse(url) - if is_async and parsed_url.scheme == "postgresql": - parsed_url = parsed_url._replace(scheme="postgresql+asyncpg") - if password: - if not parsed_url.username: - raise ValueError(f"No username in database URL {url}") - password = quote(password, safe="") - - # The username portion of the parsed URL does not appear to decode - # URL escaping of the username, so we should not quote it again or - # we will get double-quoting. - netloc = f"{parsed_url.username}:{password}@{parsed_url.hostname}" - if parsed_url.port: - netloc = f"{netloc}:{parsed_url.port}" - parsed_url = parsed_url._replace(netloc=netloc) - url = parsed_url.geturl() - return url - - -def configure_alembic_logging( - log_level: LogLevel | str = LogLevel.INFO, -) -> None: - """Set up logging for Alembic. - - This configures Alembic to use structlog for output formatting so that its - logs are also in JSON. This helps Google's Cloud Logging system understand - the logs. - - Parameters - ---------- - log_level - The Python log level. May be given as a `LogLevel` enum (preferred) - or a case-insensitive string. - """ - if not isinstance(log_level, LogLevel): - log_level = LogLevel[log_level.upper()] - - processors = [ - structlog.stdlib.ProcessorFormatter.remove_processors_meta, - structlog.processors.format_exc_info, - structlog.processors.JSONRenderer(), - ] - logging.config.dictConfig( - { - "version": 1, - "disable_existing_loggers": False, - "formatters": { - "json": { - "()": structlog.stdlib.ProcessorFormatter, - "processors": processors, - "foreign_pre_chain": [add_log_severity], - }, - }, - "handlers": { - "alembic": { - "level": log_level.value, - "class": "logging.StreamHandler", - "formatter": "json", - "stream": "ext://sys.stdout", - }, - }, - "loggers": { - "alembic": { - "handlers": ["alembic"], - "level": log_level.value, - "propagate": False, - }, - }, - } - ) - - -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 = build_database_url( - str(gafaelfawr_config.database_url), - gafaelfawr_config.database_password.get_secret_value(), - is_async=False, - ) - context.configure( - url=url, - target_metadata=target_metadata, - literal_binds=True, - dialect_opts={"paramstyle": "named"}, - ) - - with context.begin_transaction(): - context.run_migrations() - - -def do_run_migrations(connection: Connection) -> None: - """Run database migrations with a connection.""" - context.configure(connection=connection, target_metadata=target_metadata) - with context.begin_transaction(): - context.run_migrations() - - -async def run_async_migrations() -> None: - """Run migrations in online mode with an async engine. - - In this scenario we need to create an Engine and associate a connection - with the context. - """ - engine = create_database_engine( - gafaelfawr_config.database_url, - gafaelfawr_config.database_password.get_secret_value(), - ) - - async with engine.connect() as connection: - await connection.run_sync(do_run_migrations) - - await engine.dispose() - - -def run_migrations_online() -> None: - """Run database migrations. - - This must be called outside of an event loop. - """ - asyncio.run(run_async_migrations()) - +config = config_dependency.config() +# Run the migrations. configure_alembic_logging() if context.is_offline_mode(): - run_migrations_offline() + run_migrations_offline(Base.metadata, config.database_url) else: - run_migrations_online() + run_migrations_online( + Base.metadata, + config.database_url, + config.database_password, + ) diff --git a/docs/user-guide/gafaelfawringress.rst b/docs/user-guide/gafaelfawringress.rst index da00a22f9..03a1995b9 100644 --- a/docs/user-guide/gafaelfawringress.rst +++ b/docs/user-guide/gafaelfawringress.rst @@ -199,7 +199,7 @@ The same token will also still be passed in the ``X-Auth-Request-Token`` header. If this configuration option is set, the incoming ``Authorization`` header will be entirely replaced by one containing only the delegated token, unlike Gafaelfawr's normal behavior of preserving any incoming ``Authorization`` header that doesn't include a Gafaelfawr token. Caching -======= +======== By default, Gafaelfawr is consulted for every HTTP request handled by the NGINX ingress. diff --git a/docs/user-guide/helm.rst b/docs/user-guide/helm.rst index c8291cc91..bff9dec76 100644 --- a/docs/user-guide/helm.rst +++ b/docs/user-guide/helm.rst @@ -639,7 +639,7 @@ See :ref:`client-ips` for more details. .. _config-metrics: Metrics -======= +======== Gafaelfawr can export metrics to an OpenTelemetry_ collector. Currently, it only supports the insecure gRPC mechanism for sending metrics, and therefore should use a collector within the same Kubernetes cluster. diff --git a/pyproject.toml b/pyproject.toml index 9ba1024b1..559444ea3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ dependencies = [ "pyjwt", "pyyaml", "redis>=4.2.0", - "safir[db,kubernetes]>=6.2.0", + "safir[db,kubernetes]>=6.4.0", "sqlalchemy>=2.0.0", "structlog", ] diff --git a/requirements/dev.txt b/requirements/dev.txt index 1d1f2f710..1dc844479 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -630,9 +630,9 @@ hyperframe==6.0.1 \ # via # h2 # selenium-wire -idna==3.8 \ - --hash=sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac \ - --hash=sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603 +idna==3.10 \ + --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \ + --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 # via # -c requirements/main.txt # anyio @@ -889,9 +889,9 @@ pexpect==4.9.0 ; sys_platform != 'emscripten' and sys_platform != 'win32' \ --hash=sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523 \ --hash=sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f # via ipython -platformdirs==4.3.2 \ - --hash=sha256:9e5e27a08aa095dd127b9f2e764d74254f482fef22b0970773bfba79d091ab8c \ - --hash=sha256:eb1c8582560b34ed4ba105009a4badf7f6f85768b30126f351328507b2beb617 +platformdirs==4.3.3 \ + --hash=sha256:50a5450e2e84f44539718293cbb1da0a0885c9d14adf21b77bae4e66fc99d9b5 \ + --hash=sha256:d4e0b7d8ec176b341fb03cb11ca12d0276faa8c485f9cd218f613840463fc2c0 # via jupyter-core pluggy==1.5.0 \ --hash=sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1 \ @@ -1452,9 +1452,9 @@ selenium-wire==5.1.0 \ --hash=sha256:b1cd4eae44d9959381abe3bb186472520d063c658e279f98555def3d4e6dd29b \ --hash=sha256:fbf930d9992f8b6d24bb16b3e6221bab596a41f6ae7548270b7d5a92f3402622 # via -r requirements/dev.in -setuptools==74.1.2 \ - --hash=sha256:5f4c08aa4d3ebcb57a50c33b1b07e94315d7fc7230f7115e47fc99776c8ce308 \ - --hash=sha256:95b40ed940a1c67eb70fc099094bd6e99c6ee7c23aa2306f4d2697ba7916f9c6 +setuptools==75.1.0 \ + --hash=sha256:35ab7fd3bcd95e6b7fd704e4a1539513edad446c097797f2985e0e4b960772f2 \ + --hash=sha256:d59a21b17a275fb872a9c3dae73963160ae079f1049ed956880cd7c09b120538 # via # documenteer # sphinxcontrib-bibtex @@ -1518,9 +1518,9 @@ sphinx-autodoc-typehints==2.4.1 \ --hash=sha256:af37abb816ebd2cf56c7a8174fd2f34d0f2f84fbf58265f89429ae107212fe6f \ --hash=sha256:cfe410920cecf08ade046bb387b0007edb83e992de59686c62d194c762f1e45c # via documenteer -sphinx-automodapi==0.17.0 \ - --hash=sha256:4d029cb79eef29413e94ab01bb0177ebd2d5ba86e9789b73575afe9c06ae1501 \ - --hash=sha256:7ccdadad57add4aa9149d9f2bb5cf28c8f8b590280b4735b1156ea8355c423a1 +sphinx-automodapi==0.18.0 \ + --hash=sha256:022860385590768f52d4f6e19abb83b2574772d2721fb4050ecdb6e593a1a440 \ + --hash=sha256:7bf9d9a2cb67a5389c51071cfd86674ca3892ca5d5943f95de4553d6f35dddae # via documenteer sphinx-click==6.0.0 \ --hash=sha256:1e0a3c83bcb7c55497751b19d07ebe56b5d7b85eb76dd399cf9061b497adc317 \ @@ -1599,56 +1599,8 @@ sphinxext-rediraffe==0.2.7 \ --hash=sha256:651dcbfae5ffda9ffd534dfb8025f36120e5efb6ea1a33f5420023862b9f725d \ --hash=sha256:9e430a52d4403847f4ffb3a8dd6dfc34a9fe43525305131f52ed899743a5fd8c # via documenteer -sqlalchemy==2.0.34 \ - --hash=sha256:10d8f36990dd929690666679b0f42235c159a7051534adb135728ee52828dd22 \ - --hash=sha256:13be2cc683b76977a700948411a94c67ad8faf542fa7da2a4b167f2244781cf3 \ - --hash=sha256:165bbe0b376541092bf49542bd9827b048357f4623486096fc9aaa6d4e7c59a2 \ - --hash=sha256:173f5f122d2e1bff8fbd9f7811b7942bead1f5e9f371cdf9e670b327e6703ebd \ - --hash=sha256:196958cde924a00488e3e83ff917be3b73cd4ed8352bbc0f2989333176d1c54d \ - --hash=sha256:203d46bddeaa7982f9c3cc693e5bc93db476ab5de9d4b4640d5c99ff219bee8c \ - --hash=sha256:220574e78ad986aea8e81ac68821e47ea9202b7e44f251b7ed8c66d9ae3f4278 \ - --hash=sha256:243f92596f4fd4c8bd30ab8e8dd5965afe226363d75cab2468f2c707f64cd83b \ - --hash=sha256:24af3dc43568f3780b7e1e57c49b41d98b2d940c1fd2e62d65d3928b6f95f021 \ - --hash=sha256:25691f4adfb9d5e796fd48bf1432272f95f4bbe5f89c475a788f31232ea6afba \ - --hash=sha256:2e6965346fc1491a566e019a4a1d3dfc081ce7ac1a736536367ca305da6472a8 \ - --hash=sha256:3166dfff2d16fe9be3241ee60ece6fcb01cf8e74dd7c5e0b64f8e19fab44911b \ - --hash=sha256:413c85cd0177c23e32dee6898c67a5f49296640041d98fddb2c40888fe4daa2e \ - --hash=sha256:430093fce0efc7941d911d34f75a70084f12f6ca5c15d19595c18753edb7c33b \ - --hash=sha256:43f28005141165edd11fbbf1541c920bd29e167b8bbc1fb410d4fe2269c1667a \ - --hash=sha256:526ce723265643dbc4c7efb54f56648cc30e7abe20f387d763364b3ce7506c82 \ - --hash=sha256:53e68b091492c8ed2bd0141e00ad3089bcc6bf0e6ec4142ad6505b4afe64163e \ - --hash=sha256:5bc08e75ed11693ecb648b7a0a4ed80da6d10845e44be0c98c03f2f880b68ff4 \ - --hash=sha256:6831a78bbd3c40f909b3e5233f87341f12d0b34a58f14115c9e94b4cdaf726d3 \ - --hash=sha256:6a1e03db964e9d32f112bae36f0cc1dcd1988d096cfd75d6a588a3c3def9ab2b \ - --hash=sha256:6daeb8382d0df526372abd9cb795c992e18eed25ef2c43afe518c73f8cccb721 \ - --hash=sha256:6e7cde3a2221aa89247944cafb1b26616380e30c63e37ed19ff0bba5e968688d \ - --hash=sha256:707c8f44931a4facd4149b52b75b80544a8d824162602b8cd2fe788207307f9a \ - --hash=sha256:7286c353ee6475613d8beff83167374006c6b3e3f0e6491bfe8ca610eb1dec0f \ - --hash=sha256:79cb400c360c7c210097b147c16a9e4c14688a6402445ac848f296ade6283bbc \ - --hash=sha256:7cee4c6917857fd6121ed84f56d1dc78eb1d0e87f845ab5a568aba73e78adf83 \ - --hash=sha256:80bd73ea335203b125cf1d8e50fef06be709619eb6ab9e7b891ea34b5baa2287 \ - --hash=sha256:895184dfef8708e15f7516bd930bda7e50ead069280d2ce09ba11781b630a434 \ - --hash=sha256:8fddde2368e777ea2a4891a3fb4341e910a056be0bb15303bf1b92f073b80c02 \ - --hash=sha256:95d0b2cf8791ab5fb9e3aa3d9a79a0d5d51f55b6357eecf532a120ba3b5524db \ - --hash=sha256:9661268415f450c95f72f0ac1217cc6f10256f860eed85c2ae32e75b60278ad8 \ - --hash=sha256:97b850f73f8abbffb66ccbab6e55a195a0eb655e5dc74624d15cff4bfb35bd74 \ - --hash=sha256:9ea54f7300553af0a2a7235e9b85f4204e1fc21848f917a3213b0e0818de9a24 \ - --hash=sha256:9ebc11c54c6ecdd07bb4efbfa1554538982f5432dfb8456958b6d46b9f834bb7 \ - --hash=sha256:a17d8fac6df9835d8e2b4c5523666e7051d0897a93756518a1fe101c7f47f2f0 \ - --hash=sha256:ae92bebca3b1e6bd203494e5ef919a60fb6dfe4d9a47ed2453211d3bd451b9f5 \ - --hash=sha256:b68094b165a9e930aedef90725a8fcfafe9ef95370cbb54abc0464062dbf808f \ - --hash=sha256:b75b00083e7fe6621ce13cfce9d4469c4774e55e8e9d38c305b37f13cf1e874c \ - --hash=sha256:bcd18441a49499bf5528deaa9dee1f5c01ca491fc2791b13604e8f972877f812 \ - --hash=sha256:bd90c221ed4e60ac9d476db967f436cfcecbd4ef744537c0f2d5291439848768 \ - --hash=sha256:c29d03e0adf3cc1a8c3ec62d176824972ae29b67a66cbb18daff3062acc6faa8 \ - --hash=sha256:c3330415cd387d2b88600e8e26b510d0370db9b7eaf984354a43e19c40df2e2b \ - --hash=sha256:c7db3db284a0edaebe87f8f6642c2b2c27ed85c3e70064b84d1c9e4ec06d5d84 \ - --hash=sha256:ce119fc4ce0d64124d37f66a6f2a584fddc3c5001755f8a49f1ca0a177ef9796 \ - --hash=sha256:dbcdf987f3aceef9763b6d7b1fd3e4ee210ddd26cac421d78b3c206d07b2700b \ - --hash=sha256:e54ef33ea80d464c3dcfe881eb00ad5921b60f8115ea1a30d781653edc2fd6a2 \ - --hash=sha256:e60ed6ef0a35c6b76b7640fe452d0e47acc832ccbb8475de549a5cc5f90c2c06 \ - --hash=sha256:fb1b30f31a36c7f3fee848391ff77eebdd3af5750bf95fbf9b8b5323edfdb4ec \ - --hash=sha256:fbb034f565ecbe6c530dff948239377ba859420d146d5f62f0271407ffb8c580 +sqlalchemy==2.0.35 \ + --hash=sha256:e11d7ea4d24f0a262bccf9a7cd6284c976c5369dac21db237cff59586045ab9f # via # -c requirements/main.txt # -r requirements/dev.in @@ -1795,9 +1747,9 @@ wsproto==1.2.0 \ # via # selenium-wire # trio-websocket -zipp==3.20.1 \ - --hash=sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064 \ - --hash=sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b +zipp==3.20.2 \ + --hash=sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350 \ + --hash=sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29 # via # -c requirements/main.txt # importlib-metadata diff --git a/requirements/main.txt b/requirements/main.txt index ea654a8b0..6a8023cd2 100644 --- a/requirements/main.txt +++ b/requirements/main.txt @@ -106,7 +106,9 @@ aiosignal==1.3.1 \ alembic==1.13.2 \ --hash=sha256:1ff0ae32975f4fd96028c39ed9bb3c867fe3af956bd7bb37343b54c9fe7445ef \ --hash=sha256:6b8733129a6224a9a711e17c99b08462dbf7cc9670ba8f2e2ae9af860ceb1953 - # via gafaelfawr (pyproject.toml) + # via + # gafaelfawr (pyproject.toml) + # safir annotated-types==0.7.0 \ --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \ --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89 @@ -414,9 +416,9 @@ deprecated==1.2.14 \ # opentelemetry-api # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-semantic-conventions -fastapi==0.114.1 \ - --hash=sha256:1d7bbbeabbaae0acb0c22f0ab0b040f642d3093ca3645f8c876b6f91391861d8 \ - --hash=sha256:5d4746f6e4b7dff0b4f6b6c6d5445645285f662fe75886e99af7ee2d6b58bb3e +fastapi==0.114.2 \ + --hash=sha256:0adb148b62edb09e8c6eeefa3ea934e8f276dabc038c5a82989ea6346050c3da \ + --hash=sha256:44474a22913057b1acb973ab90f4b671ba5200482e7622816d79105dcece1ac5 # via # gafaelfawr (pyproject.toml) # safir @@ -710,9 +712,9 @@ httpx==0.27.2 \ # via # gafaelfawr (pyproject.toml) # safir -idna==3.8 \ - --hash=sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac \ - --hash=sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603 +idna==3.10 \ + --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \ + --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 # via # anyio # httpx @@ -1175,13 +1177,13 @@ rsa==4.9 \ --hash=sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7 \ --hash=sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21 # via google-auth -safir==6.3.0 \ - --hash=sha256:2fcd64bf37dd42eacedd6378341b2487cd06dbaf1f28403301b8d80f60a4fb56 \ - --hash=sha256:6ad7dad520d87d853628849ef95a348c55dbd0180ad3f15c1cf2f7f8fe32f915 +safir==6.4.0 \ + --hash=sha256:ba7af071eab0d198e6e15a2117028566f3f4237e02e2278e8bfc2633a7c68228 \ + --hash=sha256:f38c3f1d7d76d304984b572288826510e5c7a0e1f965b2eabdd7f3bace07c48a # via gafaelfawr (pyproject.toml) -safir-logging==6.3.0 \ - --hash=sha256:491dfe85de89a3f2daa29c491a22a0551f0961444490418d91ec50c040ae16eb \ - --hash=sha256:e14754ab0bba6cfa248c3fc4cb5ca28410d97ff3965e831eab6581ed37485e79 +safir-logging==6.4.0 \ + --hash=sha256:4031a430d738b8fe5bfd29125dce6cbf4e4949879307ba4146648afa3d24cd0a \ + --hash=sha256:e2dbf0b5d9dabecd70c27bff9bf01629bf0724b05b0f0087a1fe4f45c702215f # via safir six==1.16.0 \ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ @@ -1195,56 +1197,8 @@ sniffio==1.3.1 \ # via # anyio # httpx -sqlalchemy==2.0.34 \ - --hash=sha256:10d8f36990dd929690666679b0f42235c159a7051534adb135728ee52828dd22 \ - --hash=sha256:13be2cc683b76977a700948411a94c67ad8faf542fa7da2a4b167f2244781cf3 \ - --hash=sha256:165bbe0b376541092bf49542bd9827b048357f4623486096fc9aaa6d4e7c59a2 \ - --hash=sha256:173f5f122d2e1bff8fbd9f7811b7942bead1f5e9f371cdf9e670b327e6703ebd \ - --hash=sha256:196958cde924a00488e3e83ff917be3b73cd4ed8352bbc0f2989333176d1c54d \ - --hash=sha256:203d46bddeaa7982f9c3cc693e5bc93db476ab5de9d4b4640d5c99ff219bee8c \ - --hash=sha256:220574e78ad986aea8e81ac68821e47ea9202b7e44f251b7ed8c66d9ae3f4278 \ - --hash=sha256:243f92596f4fd4c8bd30ab8e8dd5965afe226363d75cab2468f2c707f64cd83b \ - --hash=sha256:24af3dc43568f3780b7e1e57c49b41d98b2d940c1fd2e62d65d3928b6f95f021 \ - --hash=sha256:25691f4adfb9d5e796fd48bf1432272f95f4bbe5f89c475a788f31232ea6afba \ - --hash=sha256:2e6965346fc1491a566e019a4a1d3dfc081ce7ac1a736536367ca305da6472a8 \ - --hash=sha256:3166dfff2d16fe9be3241ee60ece6fcb01cf8e74dd7c5e0b64f8e19fab44911b \ - --hash=sha256:413c85cd0177c23e32dee6898c67a5f49296640041d98fddb2c40888fe4daa2e \ - --hash=sha256:430093fce0efc7941d911d34f75a70084f12f6ca5c15d19595c18753edb7c33b \ - --hash=sha256:43f28005141165edd11fbbf1541c920bd29e167b8bbc1fb410d4fe2269c1667a \ - --hash=sha256:526ce723265643dbc4c7efb54f56648cc30e7abe20f387d763364b3ce7506c82 \ - --hash=sha256:53e68b091492c8ed2bd0141e00ad3089bcc6bf0e6ec4142ad6505b4afe64163e \ - --hash=sha256:5bc08e75ed11693ecb648b7a0a4ed80da6d10845e44be0c98c03f2f880b68ff4 \ - --hash=sha256:6831a78bbd3c40f909b3e5233f87341f12d0b34a58f14115c9e94b4cdaf726d3 \ - --hash=sha256:6a1e03db964e9d32f112bae36f0cc1dcd1988d096cfd75d6a588a3c3def9ab2b \ - --hash=sha256:6daeb8382d0df526372abd9cb795c992e18eed25ef2c43afe518c73f8cccb721 \ - --hash=sha256:6e7cde3a2221aa89247944cafb1b26616380e30c63e37ed19ff0bba5e968688d \ - --hash=sha256:707c8f44931a4facd4149b52b75b80544a8d824162602b8cd2fe788207307f9a \ - --hash=sha256:7286c353ee6475613d8beff83167374006c6b3e3f0e6491bfe8ca610eb1dec0f \ - --hash=sha256:79cb400c360c7c210097b147c16a9e4c14688a6402445ac848f296ade6283bbc \ - --hash=sha256:7cee4c6917857fd6121ed84f56d1dc78eb1d0e87f845ab5a568aba73e78adf83 \ - --hash=sha256:80bd73ea335203b125cf1d8e50fef06be709619eb6ab9e7b891ea34b5baa2287 \ - --hash=sha256:895184dfef8708e15f7516bd930bda7e50ead069280d2ce09ba11781b630a434 \ - --hash=sha256:8fddde2368e777ea2a4891a3fb4341e910a056be0bb15303bf1b92f073b80c02 \ - --hash=sha256:95d0b2cf8791ab5fb9e3aa3d9a79a0d5d51f55b6357eecf532a120ba3b5524db \ - --hash=sha256:9661268415f450c95f72f0ac1217cc6f10256f860eed85c2ae32e75b60278ad8 \ - --hash=sha256:97b850f73f8abbffb66ccbab6e55a195a0eb655e5dc74624d15cff4bfb35bd74 \ - --hash=sha256:9ea54f7300553af0a2a7235e9b85f4204e1fc21848f917a3213b0e0818de9a24 \ - --hash=sha256:9ebc11c54c6ecdd07bb4efbfa1554538982f5432dfb8456958b6d46b9f834bb7 \ - --hash=sha256:a17d8fac6df9835d8e2b4c5523666e7051d0897a93756518a1fe101c7f47f2f0 \ - --hash=sha256:ae92bebca3b1e6bd203494e5ef919a60fb6dfe4d9a47ed2453211d3bd451b9f5 \ - --hash=sha256:b68094b165a9e930aedef90725a8fcfafe9ef95370cbb54abc0464062dbf808f \ - --hash=sha256:b75b00083e7fe6621ce13cfce9d4469c4774e55e8e9d38c305b37f13cf1e874c \ - --hash=sha256:bcd18441a49499bf5528deaa9dee1f5c01ca491fc2791b13604e8f972877f812 \ - --hash=sha256:bd90c221ed4e60ac9d476db967f436cfcecbd4ef744537c0f2d5291439848768 \ - --hash=sha256:c29d03e0adf3cc1a8c3ec62d176824972ae29b67a66cbb18daff3062acc6faa8 \ - --hash=sha256:c3330415cd387d2b88600e8e26b510d0370db9b7eaf984354a43e19c40df2e2b \ - --hash=sha256:c7db3db284a0edaebe87f8f6642c2b2c27ed85c3e70064b84d1c9e4ec06d5d84 \ - --hash=sha256:ce119fc4ce0d64124d37f66a6f2a584fddc3c5001755f8a49f1ca0a177ef9796 \ - --hash=sha256:dbcdf987f3aceef9763b6d7b1fd3e4ee210ddd26cac421d78b3c206d07b2700b \ - --hash=sha256:e54ef33ea80d464c3dcfe881eb00ad5921b60f8115ea1a30d781653edc2fd6a2 \ - --hash=sha256:e60ed6ef0a35c6b76b7640fe452d0e47acc832ccbb8475de549a5cc5f90c2c06 \ - --hash=sha256:fb1b30f31a36c7f3fee848391ff77eebdd3af5750bf95fbf9b8b5323edfdb4ec \ - --hash=sha256:fbb034f565ecbe6c530dff948239377ba859420d146d5f62f0271407ffb8c580 +sqlalchemy==2.0.35 \ + --hash=sha256:e11d7ea4d24f0a262bccf9a7cd6284c976c5369dac21db237cff59586045ab9f # via # gafaelfawr (pyproject.toml) # alembic @@ -1660,7 +1614,7 @@ yarl==1.11.1 \ --hash=sha256:f46f81501160c28d0c0b7333b4f7be8983dbbc161983b6fb814024d1b4952f79 \ --hash=sha256:f61db3b7e870914dbd9434b560075e0366771eecbe6d2b5561f5bc7485f39efd # via aiohttp -zipp==3.20.1 \ - --hash=sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064 \ - --hash=sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b +zipp==3.20.2 \ + --hash=sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350 \ + --hash=sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29 # via importlib-metadata diff --git a/requirements/tox.txt b/requirements/tox.txt index 649788c90..51693c87a 100644 --- a/requirements/tox.txt +++ b/requirements/tox.txt @@ -133,9 +133,9 @@ filelock==3.16.0 \ # via # tox # virtualenv -idna==3.8 \ - --hash=sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac \ - --hash=sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603 +idna==3.10 \ + --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \ + --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 # via # -c requirements/dev.txt # -c requirements/main.txt @@ -148,9 +148,9 @@ packaging==24.1 \ # pyproject-api # tox # tox-uv -platformdirs==4.3.2 \ - --hash=sha256:9e5e27a08aa095dd127b9f2e764d74254f482fef22b0970773bfba79d091ab8c \ - --hash=sha256:eb1c8582560b34ed4ba105009a4badf7f6f85768b30126f351328507b2beb617 +platformdirs==4.3.3 \ + --hash=sha256:50a5450e2e84f44539718293cbb1da0a0885c9d14adf21b77bae4e66fc99d9b5 \ + --hash=sha256:d4e0b7d8ec176b341fb03cb11ca12d0276faa8c485f9cd218f613840463fc2c0 # via # -c requirements/dev.txt # tox @@ -213,25 +213,25 @@ urllib3==2.2.3 \ # -c requirements/main.txt # docker # requests -uv==0.4.9 \ - --hash=sha256:0340d2c7bf9afe0098e3301c1885de10e317232cfa346f0ac16374cee284a4cb \ - --hash=sha256:060af185481ef46ab97008cad330f3cd7a7aa1ce3d219b67d27c5a2a551ac2ea \ - --hash=sha256:1a8acc7abb2174bd3c8f5fc98345f2bb602f31b7558e37f3d23bef99ddd58dec \ - --hash=sha256:34bce9f4892130b01a7605d27bbeb71395e9b031d793123c250b79187ee307ca \ - --hash=sha256:45bf0cead2436b1977f71669e945db19990ca70a7765111fb951545815467bb6 \ - --hash=sha256:52101bc8652b4284b78fac52ed7878f3bae414bc4076c377735962666b309dde \ - --hash=sha256:5422680436f4cebef945bb2e562e01c02a4fa0a95f85d1b8010f2ee868a0b8c1 \ - --hash=sha256:55cf2522262ef663114bda5d80375ddc7f7af0d054df89426372a0d494380875 \ - --hash=sha256:566d4d7a475aacd21dbb4aba053cd4f4f52d65acdef2c83c59bcdff08756701e \ - --hash=sha256:5b66a52cb60a2882a882bc5f13afa6daf3172a54fe9fb998529d19418d5aed18 \ - --hash=sha256:630a6fe215829f734278e618c1633c2bb88ee03dc6a92ae9890fabd98ee810a9 \ - --hash=sha256:69529b6bf5de6ec8fbe8e022f5bcbaef778e76136fc37fae6ec7a8b18b3f9024 \ - --hash=sha256:71e87038fcc9f61b2d6f66c4a92354c6d0abe4baae21bb90241693f161ddeaa1 \ - --hash=sha256:8869637ea6231f66fe643be22f9334874db3496844b3d8bfd8efd4227ded3d44 \ - --hash=sha256:9c9b70f016f28cc05633b564d8690cfdb7ebac4d2210d9158819947841e00347 \ - --hash=sha256:b54a9022e9e1fdbf3ae15ef340a0d1d1847dd739df5023896aa8d97d88af1efe \ - --hash=sha256:bf834f7f360a192372d879eda86f6a1dd94195faf68154dcf7c90247098d2bb2 \ - --hash=sha256:f50cbdfbc8399e1211c580e47f42650a184541ee398af95ad29bf9a2e977baba +uv==0.4.10 \ + --hash=sha256:0784f75093a75390d8d480cc8a444516e78f08849db9a13c21791a5f651df4a1 \ + --hash=sha256:0f8b9ba4ecfbea343a00e46d509669606e55fe233d800752c4c25650473df358 \ + --hash=sha256:1b6b6c6b8cc0c4e54ab25e3b46e49d1e583e26c194572eb42bfeebf71b39cca2 \ + --hash=sha256:1ff5130b6f3af79c4e47f63db03215aed15e78cb4f1f51682af6f9949c2bcf00 \ + --hash=sha256:2ff29a2f55a697e78d787a41ab41d4b26421d200728289b88b6241d3b486c436 \ + --hash=sha256:30d1f8348a2b18e21a35c97ce42528781f242d0303881fc92fbacdcb653c8bca \ + --hash=sha256:3be73788db9ceacb94a521cf67ca5cc08bac512aef71145b904ab62a3acabdae \ + --hash=sha256:444e1cdb36d7ef103e52185f918800527c255dc369c9f90eb1f198dfa3f4d5bc \ + --hash=sha256:6ba1cc3070e5c63ce0a1421fbed28bd1b3ff520671d7badda11a501504c78394 \ + --hash=sha256:8fa510dfbbde4f8ad5cd2769568c7b0c3e867b74deaf4beabcca79e74e7550cc \ + --hash=sha256:97a1187e11a9df70d55bc577721ad4a19441cda56e4d69fb2f38d88c7650d2a0 \ + --hash=sha256:99954a94dd6c4bff8a9a963c05bc3988214ea39e7511a52fda35112e1a478447 \ + --hash=sha256:a9dc1f8fca5c4a2f73054d9f56c7397e9fc6ba43baefc503d6f0128d72ea662f \ + --hash=sha256:b89dfd213359a23797155ff8175e5202ed6b84aadeb20df92132127608d46acf \ + --hash=sha256:bc87d6c581cfed0979e0f5ee93383d46006c6d4a5e4eb9f43ef13bce61b50cc2 \ + --hash=sha256:bc99e6b45303f0881a8dc199f0b7ea8261dd1779e576e8477a7721ceeeaafcc7 \ + --hash=sha256:e99e3f761875962942e0743b868bd666021d5e14c3df494e820ef8f45fb88578 \ + --hash=sha256:ff9046a8c5e836e892ac7741e672ee016e92e55c659fa8195595df65a1f3accf # via tox-uv virtualenv==20.26.4 \ --hash=sha256:48f2695d9809277003f30776d155615ffc11328e6a0a8c1f0ec80188d7874a55 \ diff --git a/src/gafaelfawr/cli.py b/src/gafaelfawr/cli.py index d0f935846..b47352f0c 100644 --- a/src/gafaelfawr/cli.py +++ b/src/gafaelfawr/cli.py @@ -8,22 +8,23 @@ import sys from pathlib import Path -import alembic import click import structlog import uvicorn -from alembic.config import Config from cryptography.fernet import Fernet from safir.asyncio import run_with_asyncio from safir.click import display_help -from safir.database import create_database_engine +from safir.database import ( + create_database_engine, + is_database_current, + stamp_database, +) from safir.slack.blockkit import SlackMessage from sqlalchemy import text from .database import ( generate_schema_sql, initialize_gafaelfawr_database, - is_database_current, is_database_initialized, ) from .dependencies.config import config_dependency @@ -87,7 +88,7 @@ async def audit(*, fix: bool, config_path: Path | None) -> None: engine = create_database_engine( config.database_url, config.database_password ) - if not await is_database_current(config, logger, engine): + if not await is_database_current(engine, logger): raise click.ClickException("Database schema is not current") logger.debug("Starting audit") async with Factory.standalone(config, engine) as factory: @@ -228,7 +229,7 @@ def init(*, config_path: Path | None, alembic_config_path: Path) -> None: logger = structlog.get_logger("gafaelfawr") logger.debug("Initializing database") asyncio.run(initialize_gafaelfawr_database(config, logger)) - alembic.command.stamp(Config(str(alembic_config_path)), "head") + stamp_database(alembic_config_path) logger.debug("Finished initializing data stores") @@ -250,7 +251,7 @@ async def maintenance(*, config_path: Path | None) -> None: engine = create_database_engine( config.database_url, config.database_password ) - if not await is_database_current(config, logger, engine): + if not await is_database_current(engine, logger): raise click.ClickException("Database schema is not current") logger.debug("Starting background maintenance") async with Factory.standalone(config, engine, check_db=True) as factory: @@ -339,8 +340,9 @@ def update_schema( if not asyncio.run(is_database_initialized(config, logger)): logger.debug("Initializing database") asyncio.run(initialize_gafaelfawr_database(config, logger)) - alembic.command.stamp(Config(str(alembic_config_path)), "head") + stamp_database(alembic_config_path) logger.debug("Finished initializing data stores") + return subprocess.run(["alembic", "upgrade", "head"], check=True, env=env) @@ -364,5 +366,5 @@ async def validate_schema(*, config_path: Path | None) -> None: ) if not await is_database_initialized(config, logger, engine): raise click.ClickException("Database has not been initialized") - if not await is_database_current(config, logger, engine): + if not await is_database_current(engine, logger): raise click.ClickException("Database schema is not current") diff --git a/src/gafaelfawr/database.py b/src/gafaelfawr/database.py index 86b08702e..76cf27af2 100644 --- a/src/gafaelfawr/database.py +++ b/src/gafaelfawr/database.py @@ -5,11 +5,8 @@ import asyncio from typing import Any -from alembic.config import Config as AlembicConfig -from alembic.runtime.migration import MigrationContext -from alembic.script import ScriptDirectory from safir.database import create_database_engine, initialize_database -from sqlalchemy import Connection, create_mock_engine, select +from sqlalchemy import create_mock_engine, select from sqlalchemy.exc import OperationalError, ProgrammingError from sqlalchemy.ext.asyncio import AsyncEngine from structlog.stdlib import BoundLogger @@ -20,7 +17,6 @@ __all__ = [ "initialize_gafaelfawr_database", - "is_database_current", "is_database_initialized", ] @@ -81,53 +77,6 @@ async def initialize_gafaelfawr_database( await engine.dispose() -async def is_database_current( - config: Config, logger: BoundLogger, engine: AsyncEngine | None = None -) -> bool: - """Check whether the database schema is at the current version. - - This must be called outside of any event loop, since Alembic doesn't work - well with async event loops. It expects :file:`alembic/versions` to - contain the migration scripts. - - Parameters - ---------- - config - Gafaelfawr configuration. - logger - Logger to use for status reporting. - engine - If given, database engine to use, which avoids the need to create - another one. - - Returns - ------- - bool - `True` if Alembic reports the database schema is current, false - otherwise. - """ - if not engine: - engine = create_database_engine( - config.database_url, config.database_password - ) - - def get_current_heads(connection: Connection) -> set[str]: - context = MigrationContext.configure(connection) - return set(context.get_current_heads()) - - async with engine.begin() as connection: - current = await connection.run_sync(get_current_heads) - await engine.dispose() - alembic_config = AlembicConfig("alembic.ini") - alembic_scripts = ScriptDirectory.from_config(alembic_config) - expected = set(alembic_scripts.get_heads()) - if current != expected: - logger.error(f"Schema mismatch: {current} != {expected}") - return False - else: - return True - - async def is_database_initialized( config: Config, logger: BoundLogger, engine: AsyncEngine | None = None ) -> bool: diff --git a/src/gafaelfawr/main.py b/src/gafaelfawr/main.py index 4109fa422..042c3e790 100644 --- a/src/gafaelfawr/main.py +++ b/src/gafaelfawr/main.py @@ -14,6 +14,7 @@ from fastapi.openapi.utils import get_openapi from fastapi.staticfiles import StaticFiles from opentelemetry.sdk.metrics.export import MetricReader +from safir.database import create_database_engine, is_database_current from safir.dependencies.db_session import db_session_dependency from safir.dependencies.http_client import http_client_dependency from safir.fastapi import ClientRequestError, client_request_error_handler @@ -23,7 +24,6 @@ from safir.slack.webhook import SlackRouteErrorHandler from .constants import COOKIE_NAME -from .database import is_database_current from .dependencies.config import config_dependency from .dependencies.context import context_dependency from .exceptions import DatabaseSchemaError @@ -78,11 +78,15 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]: config = config_dependency.config() if validate_schema: logger = structlog.get_logger("gafaelfawr") - if not await is_database_current(config, logger): + engine = create_database_engine( + config.database_url, config.database_password + ) + if not await is_database_current(engine, logger): raise DatabaseSchemaError("Database schema out of date") + await engine.dispose() await context_dependency.initialize(config, metric_reader) await db_session_dependency.initialize( - str(config.database_url), config.database_password + config.database_url, config.database_password ) if extra_startup: await extra_startup diff --git a/src/gafaelfawr/operator/startup.py b/src/gafaelfawr/operator/startup.py index 47a160325..7a1d9d1c9 100644 --- a/src/gafaelfawr/operator/startup.py +++ b/src/gafaelfawr/operator/startup.py @@ -7,11 +7,10 @@ import kopf import structlog from kubernetes_asyncio.client import ApiClient -from safir.database import create_database_engine +from safir.database import create_database_engine, is_database_current from safir.kubernetes import initialize_kubernetes from ..constants import KUBERNETES_WATCH_TIMEOUT -from ..database import is_database_current from ..dependencies.config import config_dependency from ..exceptions import DatabaseSchemaError from ..factory import Factory @@ -58,7 +57,7 @@ async def startup( config.database_url, config.database_password ) logger = structlog.get_logger("gafaelfawr") - if not await is_database_current(config, logger, engine): + if not await is_database_current(engine, logger): raise DatabaseSchemaError("Database schema is not current") factory = await Factory.create(config, engine, check_db=True) api_client = ApiClient() diff --git a/tests/conftest.py b/tests/conftest.py index 35843095d..c2204afdd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,18 +10,18 @@ import pytest_asyncio import respx import structlog -from alembic.config import Config as AlembicConfig -from alembic.runtime.migration import MigrationContext -from alembic.script import ScriptDirectory from asgi_lifespan import LifespanManager from cryptography.fernet import Fernet from fastapi import FastAPI from httpx import ASGITransport, AsyncClient from opentelemetry.sdk.metrics.export import InMemoryMetricReader, MetricReader -from safir.database import create_database_engine, initialize_database +from safir.database import ( + create_database_engine, + initialize_database, + stamp_database_async, +) from safir.testing.slack import MockSlackWebhook, mock_slack_webhook from seleniumwire import webdriver -from sqlalchemy import Connection from sqlalchemy.ext.asyncio import AsyncEngine from gafaelfawr.config import Config @@ -36,7 +36,6 @@ from .pages.tokens import TokensPage from .support.config import config_path, configure from .support.constants import TEST_HOSTNAME -from .support.database import clear_alembic_version from .support.firestore import MockFirestore, patch_firestore from .support.ldap import MockLDAP, patch_ldap from .support.selenium import SeleniumConfig, run_app, selenium_driver @@ -132,27 +131,7 @@ async def empty_database(engine: AsyncEngine, config: Config) -> None: async with factory.session.begin(): await admin_service.add_initial_admins(config.initial_admins) await factory._context.redis.flushdb() - - # Get Alembic configuration information. - alembic_config = AlembicConfig("alembic.ini") - alembic_scripts = ScriptDirectory.from_config(alembic_config) - current_head = alembic_scripts.get_current_head() - assert current_head - - def set_version(connection: Connection) -> None: - context = MigrationContext.configure(connection) - context.stamp(alembic_scripts, current_head) - - # Stamp the database with the current Alembic version. Initializing the - # database will not update the Aflembic version, so we have to do this - # explicitly to ensure that there isn't any out-of-date schema information - # left over from a previous test. - # - # We have to do this somewhat elaborately because the alembic.command - # interface cannot be run inside an asyncio loop. - await clear_alembic_version(engine) - async with engine.begin() as connection: - await connection.run_sync(set_version) + await stamp_database_async(engine) @pytest_asyncio.fixture diff --git a/tests/support/database.py b/tests/support/database.py index 7a3486b23..2e7d6aaba 100644 --- a/tests/support/database.py +++ b/tests/support/database.py @@ -2,11 +2,10 @@ from __future__ import annotations -import contextlib from pathlib import Path +from safir.database import unstamp_database from sqlalchemy import text -from sqlalchemy.exc import ProgrammingError from sqlalchemy.ext.asyncio import AsyncEngine from gafaelfawr.config import Config @@ -14,28 +13,11 @@ from gafaelfawr.schema import Base __all__ = [ - "clear_alembic_version", "create_old_database", "drop_database", ] -async def clear_alembic_version(engine: AsyncEngine) -> None: - """Clear the Alembic version from the database. - - This is done when clearing the whole database or before stamping the - database to ensure that the database is in the expected state. - - Parameters - ---------- - engine - Engine to use for SQL calls. - """ - async with engine.begin() as conn: - with contextlib.suppress(ProgrammingError): - await conn.execute(text("DROP TABLE alembic_version")) - - async def create_old_database( config: Config, engine: AsyncEngine, version: str ) -> None: @@ -77,4 +59,4 @@ async def drop_database(engine: AsyncEngine) -> None: """ async with engine.begin() as conn: await conn.run_sync(Base.metadata.drop_all) - await clear_alembic_version(engine) + await unstamp_database(engine) diff --git a/ui/package-lock.json b/ui/package-lock.json index fcc98cde7..63e899018 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -2059,9 +2059,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -2090,38 +2090,38 @@ } }, "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@floating-ui/core": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.7.tgz", - "integrity": "sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==", + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", + "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==", "dependencies": { - "@floating-ui/utils": "^0.2.7" + "@floating-ui/utils": "^0.2.8" } }, "node_modules/@floating-ui/dom": { - "version": "1.6.10", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.10.tgz", - "integrity": "sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A==", + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.11.tgz", + "integrity": "sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==", "dependencies": { "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.7" + "@floating-ui/utils": "^0.2.8" } }, "node_modules/@floating-ui/react": { - "version": "0.26.23", - "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.23.tgz", - "integrity": "sha512-9u3i62fV0CFF3nIegiWiRDwOs7OW/KhSUJDNx2MkQM3LbE5zQOY01sL3nelcVBXvX7Ovvo3A49I8ql+20Wg/Hw==", + "version": "0.26.24", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.24.tgz", + "integrity": "sha512-2ly0pCkZIGEQUq5H8bBK0XJmc1xIK/RM3tvVzY3GBER7IOD1UgmC2Y2tjj4AuS+TC+vTE1KJv2053290jua0Sw==", "dependencies": { - "@floating-ui/react-dom": "^2.1.1", - "@floating-ui/utils": "^0.2.7", + "@floating-ui/react-dom": "^2.1.2", + "@floating-ui/utils": "^0.2.8", "tabbable": "^6.0.0" }, "peerDependencies": { @@ -2130,9 +2130,9 @@ } }, "node_modules/@floating-ui/react-dom": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.1.tgz", - "integrity": "sha512-4h84MJt3CHrtG18mGsXuLCHMrug49d7DFkU0RMIyshRveBeyV2hmV/pDaF2Uxtu8kgq5r46llp5E5FQiR0K2Yg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", "dependencies": { "@floating-ui/dom": "^1.0.0" }, @@ -2142,9 +2142,9 @@ } }, "node_modules/@floating-ui/utils": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.7.tgz", - "integrity": "sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA==" + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", + "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==" }, "node_modules/@gatsbyjs/parcel-namer-relative-to-cwd": { "version": "2.13.1", @@ -2662,13 +2662,13 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, @@ -4266,9 +4266,9 @@ } }, "node_modules/@types/node": { - "version": "22.5.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", - "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", + "version": "22.5.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz", + "integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==", "dependencies": { "undici-types": "~6.19.2" } @@ -4288,9 +4288,9 @@ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" }, "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" }, "node_modules/@types/reach__router": { "version": "1.3.15", @@ -4301,9 +4301,9 @@ } }, "node_modules/@types/react": { - "version": "18.3.5", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz", - "integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==", + "version": "18.3.6", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.6.tgz", + "integrity": "sha512-CnGaRYNu2iZlkGXGrOYtdg5mLK8neySj0woZ4e2wF/eli2E6Sazmq5X+Nrj6OBrrFVQfJWTUFeqAzoRhWQXYvg==", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -4347,16 +4347,16 @@ "integrity": "sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.5.0.tgz", - "integrity": "sha512-lHS5hvz33iUFQKuPFGheAB84LwcJ60G8vKnEhnfcK1l8kGVLro2SFYW6K0/tj8FUhRJ0VHyg1oAfg50QGbPPHw==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.6.0.tgz", + "integrity": "sha512-UOaz/wFowmoh2G6Mr9gw60B1mm0MzUtm6Ic8G2yM1Le6gyj5Loi/N+O5mocugRGY+8OeeKmkMmbxNqUCq3B4Sg==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.5.0", - "@typescript-eslint/type-utils": "8.5.0", - "@typescript-eslint/utils": "8.5.0", - "@typescript-eslint/visitor-keys": "8.5.0", + "@typescript-eslint/scope-manager": "8.6.0", + "@typescript-eslint/type-utils": "8.6.0", + "@typescript-eslint/utils": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -4380,15 +4380,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.5.0.tgz", - "integrity": "sha512-gF77eNv0Xz2UJg/NbpWJ0kqAm35UMsvZf1GHj8D9MRFTj/V3tAciIWXfmPLsAAF/vUlpWPvUDyH1jjsr0cMVWw==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.6.0.tgz", + "integrity": "sha512-eQcbCuA2Vmw45iGfcyG4y6rS7BhWfz9MQuk409WD47qMM+bKCGQWXxvoOs1DUp+T7UBMTtRTVT+kXr7Sh4O9Ow==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.5.0", - "@typescript-eslint/types": "8.5.0", - "@typescript-eslint/typescript-estree": "8.5.0", - "@typescript-eslint/visitor-keys": "8.5.0", + "@typescript-eslint/scope-manager": "8.6.0", + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/typescript-estree": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0", "debug": "^4.3.4" }, "engines": { @@ -4408,13 +4408,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.5.0.tgz", - "integrity": "sha512-06JOQ9Qgj33yvBEx6tpC8ecP9o860rsR22hWMEd12WcTRrfaFgHr2RB/CA/B+7BMhHkXT4chg2MyboGdFGawYg==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.6.0.tgz", + "integrity": "sha512-ZuoutoS5y9UOxKvpc/GkvF4cuEmpokda4wRg64JEia27wX+PysIE9q+lzDtlHHgblwUWwo5/Qn+/WyTUvDwBHw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.5.0", - "@typescript-eslint/visitor-keys": "8.5.0" + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4425,13 +4425,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.5.0.tgz", - "integrity": "sha512-N1K8Ix+lUM+cIDhL2uekVn/ZD7TZW+9/rwz8DclQpcQ9rk4sIL5CAlBC0CugWKREmDjBzI/kQqU4wkg46jWLYA==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.6.0.tgz", + "integrity": "sha512-dtePl4gsuenXVwC7dVNlb4mGDcKjDT/Ropsk4za/ouMBPplCLyznIaR+W65mvCvsyS97dymoBRrioEXI7k0XIg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.5.0", - "@typescript-eslint/utils": "8.5.0", + "@typescript-eslint/typescript-estree": "8.6.0", + "@typescript-eslint/utils": "8.6.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -4449,9 +4449,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.5.0.tgz", - "integrity": "sha512-qjkormnQS5wF9pjSi6q60bKUHH44j2APxfh9TQRXK8wbYVeDYYdYJGIROL87LGZZ2gz3Rbmjc736qyL8deVtdw==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.6.0.tgz", + "integrity": "sha512-rojqFZGd4MQxw33SrOy09qIDS8WEldM8JWtKQLAjf/X5mGSeEFh5ixQlxssMNyPslVIk9yzWqXCsV2eFhYrYUw==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4462,13 +4462,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.5.0.tgz", - "integrity": "sha512-vEG2Sf9P8BPQ+d0pxdfndw3xIXaoSjliG0/Ejk7UggByZPKXmJmw3GW5jV2gHNQNawBUyfahoSiCFVov0Ruf7Q==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.6.0.tgz", + "integrity": "sha512-MOVAzsKJIPIlLK239l5s06YXjNqpKTVhBVDnqUumQJja5+Y94V3+4VUFRA0G60y2jNnTVwRCkhyGQpavfsbq/g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.5.0", - "@typescript-eslint/visitor-keys": "8.5.0", + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -4526,15 +4526,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.5.0.tgz", - "integrity": "sha512-6yyGYVL0e+VzGYp60wvkBHiqDWOpT63pdMV2CVG4LVDd5uR6q1qQN/7LafBZtAtNIn/mqXjsSeS5ggv/P0iECw==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.6.0.tgz", + "integrity": "sha512-eNp9cWnYf36NaOVjkEUznf6fEgVy1TWpE0o52e4wtojjBx7D1UV2WAWGzR+8Y5lVFtpMLPwNbC67T83DWSph4A==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.5.0", - "@typescript-eslint/types": "8.5.0", - "@typescript-eslint/typescript-estree": "8.5.0" + "@typescript-eslint/scope-manager": "8.6.0", + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/typescript-estree": "8.6.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4548,12 +4548,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.5.0.tgz", - "integrity": "sha512-yTPqMnbAZJNy2Xq2XU8AdtOW9tJIr+UQb64aXB9f3B1498Zx9JorVgFJcZpEc9UBuCCrdzKID2RGAMkYcDtZOw==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.6.0.tgz", + "integrity": "sha512-wapVFfZg9H0qOYh4grNVQiMklJGluQrOUiOhYRrQWhx7BY/+I1IYb8BczWNbbUpO+pqy0rDciv3lQH5E1bCLrg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.5.0", + "@typescript-eslint/types": "8.6.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -5288,12 +5288,12 @@ "integrity": "sha512-Mh1j/rw4xM9T3YICkw22aBQ78FhsHdsmlb9NEk4uVAFBOg+Ez9ZgXXHugoBPCZui3XLomk/7/JBBH4daJqTkQQ==" }, "node_modules/babel-loader": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", - "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.4.1.tgz", + "integrity": "sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA==", "dependencies": { "find-cache-dir": "^3.3.1", - "loader-utils": "^2.0.0", + "loader-utils": "^2.0.4", "make-dir": "^3.1.0", "schema-utils": "^2.6.5" }, @@ -5498,9 +5498,9 @@ "optional": true }, "node_modules/bare-fs": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.4.tgz", - "integrity": "sha512-7YyxitZEq0ey5loOF5gdo1fZQFF7290GziT+VbAJ+JbYTJYaPZwuEz2r/Nq23sm4fjyTgUf2uJI2gkT3xAuSYA==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", + "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", "optional": true, "dependencies": { "bare-events": "^2.0.0", @@ -5509,9 +5509,9 @@ } }, "node_modules/bare-os": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.3.tgz", - "integrity": "sha512-FjkNiU3AwTQNQkcxFOmDcCfoN1LjjtU+ofGJh5DymZZLTqdw2i/CzV7G0h3snvh6G8jrWtdmNSgZPH4L2VOAsQ==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", + "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", "optional": true }, "node_modules/bare-path": { @@ -7522,9 +7522,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.5.21", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.21.tgz", - "integrity": "sha512-+rBAerCpQvFSPyAO677i5gJuWGO2WFsoujENdcMzsrpP7Ebcc3pmpERgU8CV4fFF10a5haP4ivnFQ/AmLICBVg==" + "version": "1.5.23", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.23.tgz", + "integrity": "sha512-mBhODedOXg4v5QWwl21DjM5amzjmI1zw9EPrPK/5Wx7C8jt33bpZNrC7OhHUG3pxRtbLpr3W2dXT+Ph1SsfRZA==" }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -7640,9 +7640,9 @@ } }, "node_modules/envinfo": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz", - "integrity": "sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", + "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", "bin": { "envinfo": "dist/cli.js" }, @@ -7923,16 +7923,16 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -13625,9 +13625,9 @@ } }, "node_modules/postcss": { - "version": "8.4.45", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.45.tgz", - "integrity": "sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "funding": [ { "type": "opencollective", @@ -13644,8 +13644,8 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -16822,9 +16822,9 @@ } }, "node_modules/ua-parser-js": { - "version": "1.0.38", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.38.tgz", - "integrity": "sha512-Aq5ppTOfvrCMgAPneW1HfWj66Xi7XL+/mIy996R1/CLS/rcyJQm6QZdsKrUeivDFQ+Oc9Wyuwor8Ze8peEoUoQ==", + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.39.tgz", + "integrity": "sha512-k24RCVWlEcjkdOxYmVJgeD/0a1TiSpqLg+ZalVGV9lsnr4yqu0w7tX/x2xX6G4zpkgQnRf89lxuZ1wsbjXM8lw==", "funding": [ { "type": "opencollective", @@ -16839,6 +16839,9 @@ "url": "https://github.com/sponsors/faisalman" } ], + "bin": { + "ua-parser-js": "script/cli.js" + }, "engines": { "node": "*" }